创建/使用 JPEG-2000 格式的缩略图时出现问题

发布于 2024-11-17 00:57:38 字数 5886 浏览 2 评论 0原文

我正在构建一个 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)
This is the image that the thumbnail will be created from...

Thumbnail from the above using JPEG-2000 as thumbnailSrc (in the red square)
This has the thumbnail from the above in the red square...

Thumbnail from the top using JPEG (in the red square)
enter image description here

Thumbnail from the top using GIF (in the red square)
enter image description here

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

手心的海 2024-11-24 00:57:38

您是否尝试过直接使用 CGImageDestination 而不是 NSBitmapImageRep 来创建缩略图数据?

Have you tried using CGImageDestination directly instead of NSBitmapImageRep to create the thumbnail data?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文