NSImage 的工作 CIAreaHistogram / CIHistogramDisplayFilter 示例

Working CIAreaHistogram / CIHistogramDisplayFilter example for NSImage

不知何故,我不知道如何使用 CIAreaHistogramCIHistogramDisplayFilter 过滤器从 NSImage 输入中获取实际的、有意义的直方图图像。

我在 SO 上阅读了 Apple 的 "Core Image Filter Reference" 和相关帖子,但无论我尝试什么,我都没有得到有意义的输出。

到目前为止,这是我的代码:

- (void) testHist3:(NSImage *)image {

    CIContext* context = [[NSGraphicsContext currentContext] CIContext];

    NSBitmapImageRep *rep = [image bitmapImageRepresentation];
    CIImage *ciImage = [[CIImage alloc] initWithBitmapImageRep:rep];


    ciImage = [CIFilter filterWithName:@"CIAreaHistogram" keysAndValues:kCIInputImageKey, ciImage, @"inputExtent", ciImage.extent, @"inputScale", [NSNumber numberWithFloat:1.0], @"inputCount", [NSNumber numberWithFloat:256.0], nil].outputImage;
    ciImage = [CIFilter filterWithName:@"CIHistogramDisplayFilter" keysAndValues:kCIInputImageKey, ciImage, @"inputHeight", [NSNumber numberWithFloat:100.0], @"inputHighLimit", [NSNumber numberWithFloat:1.0], @"inputLowLimit", [NSNumber numberWithFloat:0.0], nil].outputImage;


    CGImageRef cgImage2 = [context createCGImage:ciImage fromRect:ciImage.extent];
    NSImage *img2 = [[NSImage alloc] initWithCGImage:cgImage2 size:ciImage.extent.size];

    NSLog(@"Histogram image: %@", img2);
    self.histImage = img2;


}

我得到的是一个 64x100 图像,具有零表示(=不可见)。如果我使用

创建 CI 上下文
CIContext *context = [[CIContext alloc] init];

那么生成的图像是灰色的,但至少它确实有一个表示:

Histogram image: <NSImage 0x6100002612c0 Size={64, 100} Reps=(
    "<NSCGImageSnapshotRep:0x6100002620c0 cgImage=<CGImage 0x6100001a1880>>" )>

输入图像是 1024x768 JPEG 图像。

我对 Core Image 或 Core Graphics 没有什么经验,所以错误可能出在转换回 NSImage 时...有什么想法吗?


编辑 2016-10-26:有了 rickster 非常全面的回答,我取得了很大的进步。

确实是 inputExtent 参数弄乱了我的结果。在那里提供 CIVector 解决了问题。我发现您也不能将其保留为默认值;我不知道默认值是多少,但它不是输入图像的全尺寸。 (我通过 运行 一张图像和它的镜像版本通过过滤器发现了这一点;我得到了不同的直方图。)

编辑 2016-10-28:

所以,我现在有一个可以工作的、可显示的直方图;我的下一步将是弄清楚 "intermediate" 直方图(来自过滤器的 256x1 像素图像)如何包含实际的直方图信息,即使除了最后一个像素之外的所有像素始终为 (0, 0, 0, 0 ).

我假设您代码中的 [image bitmapImageRepresentation] 是一个本地类别方法,大致相当于 (NSBitmapImageRep *)image.representations[0]?否则,请首先确保您获得正确的输入。

接下来,看起来您正在将 ciImage.extent 的原始输出传递到您的过滤器参数中 — 假定所述参数需要一个 CIVector 对象而不是 CGRect 结构,您可能在 运行 时将过滤器的输入搞砸了。通过使用基于字典的过滤方法 filterWithName:withInputParametersimageByApplyingFilter:withInputParameters:,您可以获得对此类问题更有用的诊断——这样,如果您尝试通过 nil 作为过滤键或通过一些不是正确对象的东西,你会得到一个编译时错误。后者为您提供了一种直接从输入图像到输出图像或链式过滤器的简单方法,无需创建中间 CIFilter 对象并需要在每个对象上设置输入图像。

相关提示:您传递的大部分参数都是这些过滤器的默认值,因此您可以只传递需要的值:

CIImage *hist = [inputImage imageByApplyingFilter:@"CIAreaHistogram"
                              withInputParameters:@{ @"inputCount": @256 }];
CIImage *outputImage = [hist imageByApplyingFilter:@"CIHistogramDisplayFilter"
                               withInputParameters:nil];

最后,根据输入图像的外观,您可能仍会从 CIHistogramDisplayFilter 中获得 几乎-全灰图像,因为所有直方图 bin可能有非常小的酒吧。我得到 Lenna 的以下信息:

增加 kCIInputScaleKey 的值可以对此有所帮助。


此外,您不需要通过 CGImageCIImageNSImage — 创建一个 NSCIImageRep 而 AppKit 将自动管理一个 CIContext 在为 display/output.

渲染图像时在幕后
// input from NSImage
NSBitmapImageRep *inRep = [nsImage bitmapImageRepresentation];
CIImage *inputImage = [[CIImage alloc] initWithBitmapImageRep:inRep];

CIImage *outputImage = // filter, rinse, repeat

// output to NSImage
NSCIImageRep *outRep = [NSCIImageRep imageRepWithCIImage: outputImage];
NSImage *outNSImage = [[NSImage alloc] init];
[outNSImage addRepresentation: outRep];