创建/使用 JPEG-2000 格式的缩略图时出现问题
我正在构建一个 Cocoa 应用程序来帮助我组织无数的数字图片。它的目的是保留一个非常精简的信息数据库,以便我可以找到我想要的图像,然后我可以转到保存实际图像(刻录到 DVD 或 CD)的存储。我可以浏览图像并设置评级、标签等。然后,我将这些数据存储在我的硬盘上,数据库的大小小于几十张图片本身。在我的数据库中,我想存储缩略图(这将使其放大约 10 倍)。我尝试过多种技术,使用 JPEG-2000 格式是最小的,但它似乎无法正常显示?
我创建缩略图的代码是:
// This code 'downsamples' my very large digital pictures
// Thumbnail size
int pixelsWide = 72;
int pixelsHigh = 72;
CGContextRef myBitmapContext = NULL;
CGColorSpaceRef colorSpace;
void *bitmapData;
int bitmapByteCount;
int bitmapBytesPerRow;
bitmapBytesPerRow = (pixelsWide * 4);
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
colorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB );
bitmapData = calloc( 1, bitmapByteCount );
if( bitmapData == NULL ) {
NSLog( @"Memory not allocated!" );
} else {
myBitmapContext = CGBitmapContextCreate( bitmapData,
pixelsWide,
pixelsHigh,
8, // bits per component
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedLast );
if( myBitmapContext == NULL ) {
free (bitmapData);
NSLog( @"myBitmapContext not created!" );
}
}
CGColorSpaceRelease( colorSpace );
// This gets any zooming or rotating that I've done to the master image
CGAffineTransform imageTransformMatrix = [self generateImageTransformMatrixForRect: NSMakeRect( 0.0, 0.0, (CGFloat) pixelsWide, (CGFloat) pixelsHigh )];
CGContextConcatCTM( myBitmapContext, imageTransformMatrix );
// imageRef is a CGImageRef to the master image to create the thumbnail of
double imgWidth = (double) CGImageGetWidth( imageRef );
double imgHeight = (double) CGImageGetHeight( imageRef );
NSRect dr = NSMakeRect( 0, 0, imgWidth, imgHeight );
CGContextDrawImage( myBitmapContext, dr, imageRef );
// This code pulls the downsampled image back in to save to the database
thumbnailImageRef = CGBitmapContextCreateImage( myBitmapContext );
bitmapData = CGBitmapContextGetData( myBitmapContext );
CGContextRelease( myBitmapContext );
if( bitmapData ) free( bitmapData );
NSDictionary *JPGopts = [NSDictionary dictionaryWithObjectsAndKeys: NSImageCompressionFactor, [NSNumber numberWithFloat: 0.0], nil];
NSBitmapImageRep *thumbnailBitmap = [[NSBitmapImageRep alloc] initWithCGImage: thumbnailImageRef];
NSData *thumbnailDataJPG = [thumbnailBitmap representationUsingType: NSJPEGFileType properties: JPGopts];
[thumbnailDataJPG writeToFile: @"/tmp/thumb.jpg" atomically: NO];
NSData *thumbnailDataPNG = [thumbnailBitmap representationUsingType: NSPNGFileType properties: nil];
[thumbnailDataPNG writeToFile: @"/tmp/thumb.png" atomically: NO];
NSData *thumbnailDataGIF = [thumbnailBitmap representationUsingType: NSGIFFileType properties: nil];
[thumbnailDataGIF writeToFile: @"/tmp/thumb.gif" atomically: NO];
NSData *thumbnailDataJPG2 = [thumbnailBitmap representationUsingType: NSJPEG2000FileType properties: nil];
[thumbnailDataJPG2 writeToFile: @"/tmp/thumb.jp2" atomically: NO];
[thumbnailBitmap release];
NSLog( @"The thumbnail sizes are %d for JPG, %d for PNG, %d for GIF, and %d for JPEG2000",
[thumbnailDataJPG length],
[thumbnailDataPNG length],
[thumbnailDataGIF length],
[thumbnailDataJPG2 length] );
// At this point I will store the selected NSData object to a BLOB in my database...
// I change the 1's to 0's and rebuild to try different formats
#if 1
CGImageSourceRef thumbSrc = CGImageSourceCreateWithData( (CFDataRef) thumbnailDataJPG2, NULL );
#elif 1
CGImageSourceRef thumbSrc = CGImageSourceCreateWithData( (CFDataRef) thumbnailDataGIF, NULL );
#elif 1
CGImageSourceRef thumbSrc = CGImageSourceCreateWithData( (CFDataRef) thumbnailDataPNG, NULL );
#else
CGImageSourceRef thumbSrc = CGImageSourceCreateWithData( (CFDataRef) thumbnailDataJPG, NULL );
#endif
thumbnailImageRef = CGImageSourceCreateImageAtIndex( thumbSrc, 0, NULL );
然后在我的 drawRect
方法中我这样做:
CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];
if( thumbnailImageRef ) {
imgWidth = (double) CGImageGetWidth( thumbnailImageRef );
imgHeight = (double) CGImageGetHeight( thumbnailImageRef );
// Make 4-pixel red border around thumbnail
CGContextSetRGBFillColor( myContext, 1, 0, 0, 1);
CGContextFillRect( myContext, CGRectMake( 96, 96, imgWidth+8, imgHeight+8 ) );
// Draw the thumbnail image
CGContextDrawImage( myContext, NSMakeRect( 100, 100, imgWidth, imgHeight ), thumbnailImageRef );
}
当我在预览中查看thumb.*文件时,它们看起来都很好(除了JPG,因为它具有任何白色背景)未填充区域)。但是,如果我使用 JPEG-2000,则在 drawRect
中显示的 thumbnailImageRef
在图像外部(在我放在其下方的红色矩形中)有额外的黑色标记。它对于 PNG 和 GIF 来说看起来很完美,除了白色背景之外,对于 JPG 来说看起来还不错。但由于 JPEG-2000 的大小是下一个最小对象的 1/4,我真的很想使用它。
我不知道那些小黑痕是从哪里来的。有人有什么建议吗?
屏幕截图示例: 要缩略图的图像(忽略红色方块)
使用 JPEG-2000 作为上述缩略图的缩略图(在红色方块)
使用 JPEG 的顶部缩略图(红色方块中)
使用 GIF 从顶部开始的缩略图(在红色方块中)
我在这些之间切换所做的唯一更改是 #if/# 中的“1”和“0” elif/#endif 部分。
由于尺寸原因,我想使用 JPEG-2000,但让它看起来像 GIF 图像一样。
谢谢!
I am building a Cocoa application to help me organize my zillions of digital pictures. It is intended to keep a very lean database of information to allow me to find the images I want, then I can go to the storage where I keep the actual images (burned to DVDs or CDs). I can browse the images and set ratings, tags, etc. I then store this data on my hard disk with a database smaller than a couple dozen of the pictures themselves. In my database I would like to store a thumbnail image (which will make it about 10x larger). I have played around with several techniques and using JPEG-2000 format would be the smallest, but it does not seem to display properly?
My code to create the thumbnails is:
// This code 'downsamples' my very large digital pictures
// Thumbnail size
int pixelsWide = 72;
int pixelsHigh = 72;
CGContextRef myBitmapContext = NULL;
CGColorSpaceRef colorSpace;
void *bitmapData;
int bitmapByteCount;
int bitmapBytesPerRow;
bitmapBytesPerRow = (pixelsWide * 4);
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
colorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB );
bitmapData = calloc( 1, bitmapByteCount );
if( bitmapData == NULL ) {
NSLog( @"Memory not allocated!" );
} else {
myBitmapContext = CGBitmapContextCreate( bitmapData,
pixelsWide,
pixelsHigh,
8, // bits per component
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedLast );
if( myBitmapContext == NULL ) {
free (bitmapData);
NSLog( @"myBitmapContext not created!" );
}
}
CGColorSpaceRelease( colorSpace );
// This gets any zooming or rotating that I've done to the master image
CGAffineTransform imageTransformMatrix = [self generateImageTransformMatrixForRect: NSMakeRect( 0.0, 0.0, (CGFloat) pixelsWide, (CGFloat) pixelsHigh )];
CGContextConcatCTM( myBitmapContext, imageTransformMatrix );
// imageRef is a CGImageRef to the master image to create the thumbnail of
double imgWidth = (double) CGImageGetWidth( imageRef );
double imgHeight = (double) CGImageGetHeight( imageRef );
NSRect dr = NSMakeRect( 0, 0, imgWidth, imgHeight );
CGContextDrawImage( myBitmapContext, dr, imageRef );
// This code pulls the downsampled image back in to save to the database
thumbnailImageRef = CGBitmapContextCreateImage( myBitmapContext );
bitmapData = CGBitmapContextGetData( myBitmapContext );
CGContextRelease( myBitmapContext );
if( bitmapData ) free( bitmapData );
NSDictionary *JPGopts = [NSDictionary dictionaryWithObjectsAndKeys: NSImageCompressionFactor, [NSNumber numberWithFloat: 0.0], nil];
NSBitmapImageRep *thumbnailBitmap = [[NSBitmapImageRep alloc] initWithCGImage: thumbnailImageRef];
NSData *thumbnailDataJPG = [thumbnailBitmap representationUsingType: NSJPEGFileType properties: JPGopts];
[thumbnailDataJPG writeToFile: @"/tmp/thumb.jpg" atomically: NO];
NSData *thumbnailDataPNG = [thumbnailBitmap representationUsingType: NSPNGFileType properties: nil];
[thumbnailDataPNG writeToFile: @"/tmp/thumb.png" atomically: NO];
NSData *thumbnailDataGIF = [thumbnailBitmap representationUsingType: NSGIFFileType properties: nil];
[thumbnailDataGIF writeToFile: @"/tmp/thumb.gif" atomically: NO];
NSData *thumbnailDataJPG2 = [thumbnailBitmap representationUsingType: NSJPEG2000FileType properties: nil];
[thumbnailDataJPG2 writeToFile: @"/tmp/thumb.jp2" atomically: NO];
[thumbnailBitmap release];
NSLog( @"The thumbnail sizes are %d for JPG, %d for PNG, %d for GIF, and %d for JPEG2000",
[thumbnailDataJPG length],
[thumbnailDataPNG length],
[thumbnailDataGIF length],
[thumbnailDataJPG2 length] );
// At this point I will store the selected NSData object to a BLOB in my database...
// I change the 1's to 0's and rebuild to try different formats
#if 1
CGImageSourceRef thumbSrc = CGImageSourceCreateWithData( (CFDataRef) thumbnailDataJPG2, NULL );
#elif 1
CGImageSourceRef thumbSrc = CGImageSourceCreateWithData( (CFDataRef) thumbnailDataGIF, NULL );
#elif 1
CGImageSourceRef thumbSrc = CGImageSourceCreateWithData( (CFDataRef) thumbnailDataPNG, NULL );
#else
CGImageSourceRef thumbSrc = CGImageSourceCreateWithData( (CFDataRef) thumbnailDataJPG, NULL );
#endif
thumbnailImageRef = CGImageSourceCreateImageAtIndex( thumbSrc, 0, NULL );
Then in my drawRect
method I do:
CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];
if( thumbnailImageRef ) {
imgWidth = (double) CGImageGetWidth( thumbnailImageRef );
imgHeight = (double) CGImageGetHeight( thumbnailImageRef );
// Make 4-pixel red border around thumbnail
CGContextSetRGBFillColor( myContext, 1, 0, 0, 1);
CGContextFillRect( myContext, CGRectMake( 96, 96, imgWidth+8, imgHeight+8 ) );
// Draw the thumbnail image
CGContextDrawImage( myContext, NSMakeRect( 100, 100, imgWidth, imgHeight ), thumbnailImageRef );
}
When I look at the thumb.* files in Preview they all look fine (except the JPG since it has a white background of any non-filled area). But the thumbnailImageRef
that is displayed in drawRect
has extra black marks outside the image (in the red rectangle I put under it) if I use JPEG-2000. It looks perfect for PNG and GIF and, except for the white background again, looks OK for JPG. But since JPEG-2000 is 1/4 the size of the next smallest object I really want to use it.
I cannot figure out where the little black marks are coming from. Does anyone have any suggestions?
Example screen shots:
Image to be thumbnailed (ignoring red square)
Thumbnail from the above using JPEG-2000 as thumbnailSrc (in the red square)
Thumbnail from the top using JPEG (in the red square)
Thumbnail from the top using GIF (in the red square)
The only changes I made to switch between these was the '1's and '0's in the #if/#elif/#endif section.
I would like to use JPEG-2000 for size reasons but have it look like the GIF image does.
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您是否尝试过直接使用 CGImageDestination 而不是 NSBitmapImageRep 来创建缩略图数据?
Have you tried using CGImageDestination directly instead of NSBitmapImageRep to create the thumbnail data?