如何根据从扫描仪接收到的数据创建图像

Posted

技术标签:

【中文标题】如何根据从扫描仪接收到的数据创建图像【英文标题】:How to create an image from data received from scanner 【发布时间】:2014-05-16 13:26:22 【问题描述】:

我从扫描仪接收到的数据 (NSData) 在使用 NSLog 登录时看起来像这样:

2014-04-07 10:42:07.309 Pantelegraph[488:303] <
4c9d8f4d 9f9849a2 9a4fa79f 46a8984d a9a346aa 9f53aea0 50ad884d aa9354b4
9251b291 54b18f54 b28e47b3 9254b396 49af8d4f b3834fb1 904fb488 59b7874f
b38a4cb2 8551b186 4ab78556 b4884cb1 894eb180 59b17d4e b37b57b2 754fb386
56b98453 b38449b1 794fb37b 49ae8852 b08253ac 8652af80 58b18552 b08850b2
8856b086 51b08a51 ad9656ad 8d4fb091 50ab934c ab9753af 9a4aae9a 54af9c53
ae9c49b0 a451afa5 4faba450 aea54eae aa4db0a3 57b0a24e aea34caf a74eafa2
49b09b51 afa448ae a152b0a1 51aca147 aea44db3 9851b0a3 4ab49d50 b09d49b3
924fb198 49af964e b29553b1 8e50b294 52b6874b b18d50b2 944eb48d 49b28a56
b3844aae 934fb485 53af884d b08356b0 814eac84 52b37754 b08650ae 7d57b081
57ae8752 ac8356a9 9150ae8a 52af7f52 ac8e51ac 8554ad8e 44ae9851 ad9c55af
9d54b095 56ae9a52 afa052ae a150aca4 51aba74c aaa34eac a453aea7 4bafa254
af9c4fac a150b29a 54aea652 af9d4db0 9e4eb195 4eaf9454 af974caf 884eb086
54a98750 ad8d51ad 8249ac85 51ab7750 ad7951ae 8453ab80 4cac8553 …
>

现在我认为这些是十六进制颜色,逐个像素。

连同这个NSData 对象,我还收到了图像的宽度和高度等信息。

我的问题是如何从这些数据中创建图像(NSImageCGImageRef 等...)?我使用了CGImageCreate,但我从中得到的只是很多噪音。

编辑

按照其中一个 cmets 的要求,她是我用 6 种颜色扫描的一张纸 (A8) 的完整数据。

Pastebin of the data and metadata.

在底部还有我从扫描仪收到的其他信息。

我以 8 位位深度和 50ppi 分辨率扫描 rgb 图像。下面是我用另一个应用程序扫描的纸张的图像:

使用此代码创建CGImageRef时:

CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, (__bridge const void *)([data dataBuffer]), [data dataSize], NULL);
    CGImageRef image = CGImageCreate([data fullImageWidth], [data fullImageHeight], [data bitsPerComponent], [data bitsPerPixel], [data bytesPerRow], CGColorSpaceCreateDeviceRGB(), kCGBitmapByteOrderDefault|kCGImageAlphaNone, provider, NULL, false, kCGRenderingIntentDefault);

我得到了很多看起来像这样的噪音(对于上面显示的图像):

编辑

正如其中一个答案所建议的,我得到的数据大小与这张图片中元数据所说的一样:

【问题讨论】:

扫描全黑对象或全白对象时会发生什么。你关于它是像素数据的理论仍然成立吗? @Warren 是的,当我在扫描仪中没有放任何东西时,大多数都是 ffffffff。 全黑给ffffffff? 您能否在粘贴箱中提供(小)扫描的全部数据?连同所有元数据作为宽度和高度? 最好只有几个大的单色矩形 【参考方案1】:

我做了一个320*320px的测试bmp,添加了红绿蓝区域。

在文本编辑器中打开它并在开始时删除元数据。

当我使用这段代码时

NSString *imagePath = [[NSBundle mainBundle] pathForResource: @"test3" ofType: @"bmp"];

NSData *data = [NSData dataWithContentsOfFile:imagePath];
CGFloat width = 320.0;
CGFloat height = 320.0;

NSUInteger bitPerByte = 8;
NSUInteger bytePerColor = 3;
NSUInteger len = [data length];

Byte *byteData = (Byte*)malloc(len);
memcpy(byteData, [data bytes], len);


CGDataProviderRef provider = CGDataProviderCreateWithData(NULL,
                                                          byteData,
                                                          width*height*bytePerColor,
                                                          NULL);

CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();

CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;

CGImageRef imageRef = CGImageCreate(width,
                                    height,
                                    bitPerByte,
                                    bytePerColor*bitPerByte,
                                    bytePerColor*width,colorSpaceRef,
                                    bitmapInfo,
                                    provider,NULL,NO,0);

红色和蓝色在哪里切换

数据看起来像这样

<… 00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000 
ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff 
0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00
00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000
ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff
0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00
00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000
ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff
0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000 ff0000ff 0000ff00 00ff0000 ffffffff ffffffff
ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff …>

BTW:8个字节的摸索与图像中颜色分量的个数无关。 NSData 不知道它包含哪些数据。这只是它的描述方法显示数据的方式。

一个解决方案是创建一个 BGR 颜色空间,但我没有这个选项,因为我决定创建一个 ios 应用程序,因为我对 Mac Cocoa 不是很熟悉。

相反,我手动交换了红色和蓝色值

// on ios i cannot create a BGR colorspace, there for I swap red and blue values
for (NSUInteger i =2; i < len; i+=3) 
    Byte l = byteData[i-2];
    Byte r = byteData[i];
    byteData[i] = l;
    byteData[i-2] = r;

给我以下代码

NSString *imagePath = [[NSBundle mainBundle] pathForResource: @"test3" ofType: @"bmp"];

NSData *data = [NSData dataWithContentsOfFile:imagePath];
CGFloat width = 320.0;
CGFloat height = 320.0;

NSUInteger bitPerByte = 8;
NSUInteger bytePerColor = 3;
NSUInteger len = [data length];

Byte *byteData = (Byte*)malloc(len);
memcpy(byteData, [data bytes], len);

// on ios i cannot create a BGR colorspace, there for I swap red and blue values
for (NSUInteger i =2; i < len; i+=3) 
    Byte l = byteData[i-2];
    Byte r = byteData[i];
    byteData[i] = l;
    byteData[i-2] = r;


CGDataProviderRef provider = CGDataProviderCreateWithData(NULL,
                                                          byteData,
                                                          width*height*bytePerColor,
                                                          NULL);

CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();

CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;

CGImageRef imageRef = CGImageCreate(width,
                                    height,
                                    bitPerByte,
                                    bytePerColor*bitPerByte,
                                    bytePerColor*width,colorSpaceRef,
                                    bitmapInfo,
                                    provider,NULL,NO,0);

这个结果:


所以由于您还没有提供样本数据集,您可能需要调整某些内容,主要是bitPerBytebytePerColor。您还必须检查扫描仪是否添加了一些元数据。


我不知道为什么,但是原始数据数组的字节数正好是元数据的两倍,89352 而不是 44676。

使用此代码

NSData *data = [NSData dataWithContentsOfFile:imagePath];
CGFloat width = 146.0;
CGFloat height = 102.0;

NSUInteger bitPerByte = 8;
NSUInteger bytePerColor = 2*3;
NSUInteger len = [data length];

Byte *byteData = (Byte*)malloc(len);
memcpy(byteData, [data bytes], len);


CGDataProviderRef provider = CGDataProviderCreateWithData(NULL,
                                                          byteData,
                                                          width*height*bytePerColor,
                                                          NULL);

CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();

CGBitmapInfo bitmapInfo = kCGBitmapByteOrder16Big;

CGImageRef imageRef = CGImageCreate(width,
                                    height,
                                    bitPerByte * 2,
                                    bytePerColor*bitPerByte ,
                                    bytePerColor*width ,
                                    colorSpaceRef,
                                    bitmapInfo,
                                    provider,NULL,NO,0);

我可以显示

【讨论】:

你也可以添加一张图片。噪音。 我会试试你的答案。我还发布了噪声图像和我使用的 CGImageCreate 代码。 (它不是很花哨但我以前从未使用过它) 哦,我们找错地方了。您实际上已经给出了答案,这与我提供数据的方式有关。我创建了一个这样的数据提供者: (__bridge const void *)([data dataBuffer]) 因为 xCode 建议这样做。我必须像您的答案一样创建 byteData 。感谢您的帮助! 感谢赏金。而对于挑战,这些都是我通常不需要做的事情。 是的,这确实让我头疼。顺便说一句,我现在使用这段代码来创建 CGDataProviderRef: CGDataProviderRef provider = CGDataProviderCreateWithCFData(CFDataCreate(NULL, [[data dataBuffer] bytes], [data dataSize]));让我减少记忆方面的麻烦,并且可以避免我未来的头痛。您可能想更新您的答案。【参考方案2】:

在上述答案的帮助下,我设法使它工作,这是我使用的代码:

- (void)scannerDevice:(ICScannerDevice *)scanner didScanToBandData:(ICScannerBandData *)data 
    CGColorSpaceRef color = CGColorSpaceCreateDeviceGray();
    if ([data pixelDataType] == ICScannerPixelDataTypeRGB) color = CGColorSpaceCreateDeviceRGB();
    CGDataProviderRef provider = CGDataProviderCreateWithCFData(CFDataCreate(NULL, [[data dataBuffer] bytes], [data dataSize]));
    CGImageRef image = CGImageCreate([data fullImageWidth], [data fullImageHeight], [data bitsPerComponent], [data bitsPerPixel], [data bytesPerRow], color, kCGBitmapByteOrderDefault|kCGImageAlphaNone, provider, NULL, false, kCGRenderingIntentDefault);

    NSImage *finImage = [[NSImage alloc] initWithCGImage:image size:NSMakeSize([data fullImageWidth], [data fullImageHeight])];
    CGImageRelease(image);
    CGDataProviderRelease(provider);
    CGColorSpaceRelease(color);

问题是我将NSData 对象直接放入数据提供程序中。这为数据提供者提供了您内存中的其他数据,而不是实际的原始图像数据,因此这就是图片中的噪声。

【讨论】:

以上是关于如何根据从扫描仪接收到的数据创建图像的主要内容,如果未能解决你的问题,请参考以下文章

如何从扫描仪获取图像?

我如何从打印机“扫描仪”获取图像 [关闭]

扫描硬币,然后从图像中确定日期

如何编写从物理扫描仪设备扫描图像的 java 代码? [关闭]

如何从文档进纸器异步扫描和传输图像

twain 扫描仪未从扫描仪获取图像