【问题标题】:NSImage losing quality upon writeToFileNSImage 在 writeToFile 时质量下降
【发布时间】:2011-10-18 08:26:58
【问题描述】:

基本上,我正在尝试创建一个用于批量图像处理的程序,该程序将调整每个图像的大小并在边缘周围添加边框(边框也将由图像组成)。尽管我还没有实现那个实现,这超出了我的问题的范围,但我问它是因为即使我在这里得到了很好的答案,我仍然可能采取了错误的方法来实现这一点,以及任何有助于认识到这一点的方法将不胜感激。无论如何,这是我的问题:

问题: 我可以使用下面的现有代码并对其进行修改以创建比当前输出的代码更高质量的图像保存到文件吗?我真的花了 10 多个小时试图找出我做错了什么; “secondaryImage”将高质量的调整大小的图像绘制到自定义视图中,但我为保存文件所做的一切都导致图像质量大大降低(没有那么多像素化,只是明显更模糊)。最后,我在 Apple 的“Reducer”示例(ImageReducer.m 的末尾)中找到了一些代码,它锁定了焦点并从当前视图中获取 NSBitmapImageRep。这使得图像质量显着提高,但是,Photoshop 执行相同操作的输出更清晰一些。看起来绘制到视图的图像与保存到文件的质量相同,因此两者都低于 Photoshop 将同一图像调整为 50% 的质量,就像这张一样。是否有可能获得比这更高质量的调整大小的图像?

除此之外,如何修改现有代码以控制保存到文件的图像质量?我可以更改压缩和像素密度吗?对于修改我的代码或向我指出好的示例或教程(最好是后者)的任何帮助,我将不胜感激。非常感谢!

- (void)drawRect:(NSRect)rect {

// Getting source image
NSImage *image = [[NSImage alloc] initWithContentsOfFile: @"/Users/TheUser/Desktop/4.jpg"];

// Setting NSRect, which is how resizing is done in this example. Is there a better way?
NSRect halfSizeRect = NSMakeRect(0, 0, image.size.width * 0.5, image.size.height * 0.5);

// Sort of used as an offscreen image or palate to do drawing onto; the future I will use to group several images into one.
NSImage *secondaryImage = [[NSImage alloc] initWithSize: halfSizeRect.size];
[secondaryImage lockFocus];

[[NSGraphicsContext currentContext] setImageInterpolation: NSImageInterpolationHigh];

[image drawInRect: halfSizeRect fromRect: NSZeroRect operation: NSCompositeSourceOver fraction: 1.0];

[secondaryImage unlockFocus];
[secondaryImage drawInRect: halfSizeRect fromRect: NSZeroRect   operation: NSCompositeSourceOver fraction: 1.0];

// Trying to add image quality options; does this usage even affect the final image?
NSBitmapImageRep *bip = nil;
bip = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide: secondaryImage.size.width pixelsHigh: secondaryImage.size.width bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:0 bitsPerPixel:0];

[secondaryImage addRepresentation: bip];

// Four lines below are from aforementioned "ImageReducer.m"
NSSize size = [secondaryImage size];
[secondaryImage lockFocus];
NSBitmapImageRep *bitmapImageRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect(0, 0, size.width, size.height)];
[secondaryImage unlockFocus];

NSDictionary *prop = [NSDictionary dictionaryWithObject: [NSNumber numberWithFloat: 1.0] forKey: NSImageCompressionFactor];
NSData *outputData = [bitmapImageRep representationUsingType:NSJPEGFileType properties: prop];
[outputData writeToFile:@"/Users/TheUser/Desktop/4_halfsize.jpg" atomically:NO];

// release from memory
[image release];    
[secondaryImage release];
[bitmapImageRep release];
[bip release];
}

【问题讨论】:

  • 我应该提一下我之前使用的导致图像质量差得多的代码。我没有锁定焦点并从 initWithFocusedView 初始化 NSBitmapImageRep,而是在做我在网上无数其他地方看到的事情,聚焦后从被绘制的 secondaryImage 释放,使用以下内容将 secondaryImage 写入文件:NSData *imageData = [[secondaryImage TIFFRepresentation] representationUsingType: NSJPEGFileType properties: nil];@987654323 @

标签: objective-c cocoa nsimage


【解决方案1】:

我不确定您为什么在屏幕之间往返。这可能会影响结果,因此不需要。

您可以使用 CGImage 和 CGBitmapContext 完成所有这些工作,如果需要,可以使用生成的图像在屏幕上绘制。我使用了这些 API 并取得了不错的效果(但我不知道它们与您当前的方法相比如何)。

另一个注意事项:以更高质量为中间渲染,然后为您编写的版本调整大小并减少到 8bpc。现在这不会产生重大影响,但是(在大多数情况下)引入过滤后会产生显着影响。

【讨论】:

  • 嗨贾斯汀,谢谢你的回答!我在实现 NSImage 时偶然发现了另一个问题,并在解决这个问题时解决了第一个(也是潜在的问题)。出于好奇,您会说这样的 CGImage 实现会更容易还是更好?我还没有尝试学习,应该研究一下。您遇到过任何好的教程或资源吗?谢谢!
  • @Mike 这不一定更容易。对于一个重要的应用程序,我会使用 CGImage 作为默认值,因为你有更多的控制权。更好的?是 - AppKit API/类型不能在多线程上下文中安全使用(除非明确记录)。 CoreGraphics API 没有这种限制(尽管如果共享 CGTypes/数据,您应该引入自己的锁定)。如果苹果的示例代码不够用,试试amazon.com/Programming-Quartz-Graphics-Kaufmann-Computer/dp/…(续)
  • (cont) 或 amazon.com/Quartz-2D-Graphics-Mac-Developers/dp/0321336631/… 至于轻松...这取决于您在做什么,以及性能的重要性。您最终可能希望使用位图作为您的默认图像信号,然后在需要 CG 渲染时使用 CGBitmapContexts——这实际上取决于渲染的复杂性、您需要的性能以及您最常使用的 API。
【解决方案2】:

最后,其中之一是“啊哈!”时刻!我尝试在高质量的 .tif 文件上使用相同的代码,结果图像(尺寸)小 8 倍,而不是我告诉它的 50%。当我尝试显示它会重新缩放图像时,它仍然比原始图像小 4 倍,而它应该以相同的高度和宽度显示。 我发现我从导入的图像中获取 NSSize 的方式是错误的。 以前,它是:

NSRect halfSizeRect = NSMakeRect(0, 0, image.size.width * 0.5, image.size.height * 0.5);

它应该在哪里:

NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData: [image TIFFRepresentation]];
NSRect halfSizeRect = NSMakeRect(0, 0, [imageRep pixelsWide]/2, [imageRep pixelsHigh]/2);

显然它与 DPI 和爵士乐有关,所以我需要从 BitmapImageRep 而不是从 image.size 获取正确的大小。 通过此更改,我能够以与 Photoshop 几乎没有区别的质量进行保存。

【讨论】:

  • 实际上,我在 NSRect 中使用浮点值是一个更大的问题。如果我将它们投射到 (int) 他们会没事的。此外,似乎 NSBitmapImageRep 中的 pixelWide 比 NSImage 的 .size.width 更可靠地获得正确的大小。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-17
  • 2010-12-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多