I want to calculate histogram of an NSImage, so I turned to CIFilter
naturally. There's a filter named CIAreaHistogram
does what I want. Here's my code:
NSBitmapImageRep *rep = [image bitmapImageRepresentation];
CIImage* hImage = nil;
@autoreleasepool {
CIImage *input = [[CIImage alloc] initWithBitmapImageRep:rep];
CIFilter *histogramFilter = [CIFilter filterWithName:@"CIAreaHistogram"];
[histogramFilter setDefaults];
[histogramFilter setValue:input forKey:kCIInputImageKey];
[histogramFilter setValue:[CIVector vectorWithCGRect:[input extent]] forKeyPath:@"inputExtent"];
[histogramFilter setValue:[NSNumber numberWithInt:256] forKeyPath:@"inputCount"];
[histogramFilter setValue:[NSNumber numberWithFloat:1.0] forKey:@"inputScale"];
hImage = [histogramFilter valueForKey:kCIOutputImageKey];
input = nil;
NSImage *result = nil;
@autoreleasepool
{
NSCIImageRep *rep = [NSCIImageRep imageRepWithCIImage:hImage];
result = [[NSImage alloc] initWithSize:rep.size];
[result addRepresentation:rep];
}
NSBitmapImageRep *imgRep = [result bitmapImageRepresentation];
for (int y = 0; y < imgRep.size.height; y ++) {
for (int x = 0; x < imgRep.size.width; x ++) {
NSUInteger pixel[4];
[imgRep getPixel:pixel atX:x y:y];
NSLog(@"(%d, %d): %lu - %lu - %lu - %lu", x, y, pixel[0], pixel[1], pixel[2], pixel[3]);
}
}
}
The bitmapImageRepresentation
is just a help method that converts NSImage
to NSBitmapImageRep
. I've tested and used a lot with it, seems it's cool.
And the output is listed below:
(0, 0): 0 - 0 - 0 - 0
(1, 0): 0 - 0 - 0 - 0
(2, 0): 0 - 0 - 0 - 0
(3, 0): 0 - 0 - 0 - 0
(4, 0): 0 - 0 - 0 - 0
(5, 0): 0 - 0 - 0 - 0
(6, 0): 0 - 0 - 0 - 0
(7, 0): 0 - 0 - 0 - 0
(8, 0): 0 - 0 - 0 - 0
(9, 0): 0 - 0 - 0 - 0
(10, 0): 0 - 0 - 0 - 0
(11, 0): 0 - 0 - 0 - 0
(12, 0): 0 - 0 - 0 - 0
(13, 0): 0 - 0 - 0 - 0
(14, 0): 0 - 0 - 0 - 0
(15, 0): 0 - 0 - 0 - 0
(16, 0): 0 - 0 - 0 - 0
(17, 0): 0 - 0 - 0 - 0
(18, 0): 0 - 0 - 0 - 0
(19, 0): 0 - 0 - 0 - 0
(20, 0): 0 - 0 - 0 - 0
(21, 0): 0 - 0 - 0 - 0
(22, 0): 0 - 0 - 0 - 0
(23, 0): 0 - 0 - 0 - 0
(24, 0): 0 - 0 - 0 - 0
(25, 0): 0 - 0 - 0 - 0
(26, 0): 0 - 0 - 0 - 0
(27, 0): 0 - 0 - 0 - 0
(28, 0): 0 - 0 - 0 - 0
(29, 0): 0 - 0 - 0 - 0
(30, 0): 0 - 0 - 0 - 0
(31, 0): 0 - 0 - 0 - 0
(32, 0): 0 - 0 - 0 - 0
(33, 0): 0 - 0 - 0 - 0
(34, 0): 0 - 0 - 0 - 0
(35, 0): 0 - 0 - 0 - 0
(36, 0): 0 - 0 - 0 - 0
(37, 0): 0 - 0 - 0 - 0
(38, 0): 0 - 0 - 0 - 0
(39, 0): 0 - 0 - 0 - 0
(40, 0): 0 - 0 - 0 - 0
(41, 0): 0 - 0 - 0 - 0
(42, 0): 0 - 0 - 0 - 0
(43, 0): 0 - 0 - 0 - 0
(44, 0): 0 - 0 - 0 - 0
(45, 0): 0 - 0 - 0 - 0
(46, 0): 0 - 0 - 0 - 0
(47, 0): 0 - 0 - 0 - 0
(48, 0): 0 - 0 - 0 - 0
(49, 0): 0 - 0 - 0 - 0
(50, 0): 0 - 0 - 0 - 0
(51, 0): 0 - 0 - 0 - 0
(52, 0): 0 - 0 - 0 - 0
(53, 0): 0 - 0 - 0 - 0
(54, 0): 0 - 0 - 0 - 0
(55, 0): 0 - 0 - 0 - 0
(56, 0): 0 - 0 - 0 - 0
(57, 0): 0 - 0 - 0 - 0
(58, 0): 0 - 0 - 0 - 0
(59, 0): 0 - 0 - 0 - 0
(60, 0): 0 - 0 - 0 - 0
(61, 0): 0 - 0 - 0 - 0
(62, 0): 0 - 0 - 0 - 0
(63, 0): 0 - 0 - 0 - 0
(64, 0): 0 - 0 - 0 - 0
(65, 0): 0 - 0 - 0 - 0
(66, 0): 0 - 0 - 0 - 0
(67, 0): 0 - 0 - 0 - 0
(68, 0): 0 - 0 - 0 - 0
(69, 0): 0 - 0 - 0 - 0
(70, 0): 0 - 0 - 0 - 0
(71, 0): 0 - 0 - 0 - 0
(72, 0): 0 - 0 - 0 - 0
(73, 0): 0 - 0 - 0 - 0
(74, 0): 0 - 0 - 0 - 0
(75, 0): 0 - 0 - 0 - 0
(76, 0): 0 - 0 - 0 - 0
(77, 0): 0 - 0 - 0 - 0
(78, 0): 0 - 0 - 0 - 0
(79, 0): 0 - 0 - 0 - 0
(80, 0): 0 - 0 - 0 - 0
(81, 0): 0 - 0 - 0 - 0
(82, 0): 0 - 0 - 0 - 0
(83, 0): 0 - 0 - 0 - 0
(84, 0): 0 - 0 - 0 - 0
(85, 0): 0 - 0 - 0 - 0
(86, 0): 0 - 0 - 0 - 0
(87, 0): 0 - 0 - 0 - 0
(88, 0): 0 - 0 - 0 - 0
(89, 0): 0 - 0 - 0 - 0
(90, 0): 0 - 0 - 0 - 0
(91, 0): 0 - 0 - 0 - 0
(92, 0): 0 - 0 - 0 - 0
(93, 0): 0 - 0 - 0 - 0
(94, 0): 0 - 0 - 0 - 0
(95, 0): 0 - 0 - 0 - 0
(96, 0): 0 - 0 - 0 - 0
(97, 0): 0 - 0 - 0 - 0
(98, 0): 0 - 0 - 0 - 0
(99, 0): 0 - 0 - 0 - 0
(100, 0): 0 - 0 - 0 - 0
(101, 0): 0 - 0 - 0 - 0
(102, 0): 0 - 0 - 0 - 0
(103, 0): 0 - 0 - 0 - 0
(104, 0): 0 - 0 - 0 - 0
(105, 0): 0 - 0 - 0 - 0
(106, 0): 0 - 0 - 0 - 0
(107, 0): 0 - 0 - 0 - 0
(108, 0): 0 - 0 - 0 - 0
(109, 0): 0 - 0 - 0 - 0
(110, 0): 0 - 0 - 0 - 0
(111, 0): 0 - 0 - 0 - 0
(112, 0): 0 - 0 - 0 - 0
(113, 0): 0 - 0 - 0 - 0
(114, 0): 0 - 0 - 0 - 0
(115, 0): 0 - 0 - 0 - 0
(116, 0): 0 - 0 - 0 - 0
(117, 0): 0 - 0 - 0 - 0
(118, 0): 0 - 0 - 0 - 0
(119, 0): 0 - 0 - 0 - 0
(120, 0): 0 - 0 - 0 - 0
(121, 0): 0 - 0 - 0 - 0
(122, 0): 0 - 0 - 0 - 0
(123, 0): 0 - 0 - 0 - 0
(124, 0): 0 - 0 - 0 - 0
(125, 0): 0 - 0 - 0 - 0
(126, 0): 0 - 0 - 0 - 0
(127, 0): 0 - 0 - 0 - 0
(128, 0): 0 - 0 - 0 - 0
(129, 0): 0 - 0 - 0 - 0
(130, 0): 0 - 0 - 0 - 0
(131, 0): 0 - 0 - 0 - 0
(132, 0): 0 - 0 - 0 - 0
(133, 0): 0 - 0 - 0 - 0
(134, 0): 0 - 0 - 0 - 0
(135, 0): 0 - 0 - 0 - 0
(136, 0): 0 - 0 - 0 - 0
(137, 0): 0 - 0 - 0 - 0
(138, 0): 0 - 0 - 0 - 0
(139, 0): 0 - 0 - 0 - 0
(140, 0): 0 - 0 - 0 - 0
(141, 0): 0 - 0 - 0 - 0
(142, 0): 0 - 0 - 0 - 0
(143, 0): 0 - 0 - 0 - 0
(144, 0): 0 - 0 - 0 - 0
(145, 0): 0 - 0 - 0 - 0
(146, 0): 0 - 0 - 0 - 0
(147, 0): 0 - 0 - 0 - 0
(148, 0): 0 - 0 - 0 - 0
(149, 0): 0 - 0 - 0 - 0
(150, 0): 0 - 0 - 0 - 0
(151, 0): 0 - 0 - 0 - 0
(152, 0): 0 - 0 - 0 - 0
(153, 0): 0 - 0 - 0 - 0
(154, 0): 0 - 0 - 0 - 0
(155, 0): 0 - 0 - 0 - 0
(156, 0): 0 - 0 - 0 - 0
(157, 0): 0 - 0 - 0 - 0
(158, 0): 0 - 0 - 0 - 0
(159, 0): 0 - 0 - 0 - 0
(160, 0): 0 - 0 - 0 - 0
(161, 0): 0 - 0 - 0 - 0
(162, 0): 0 - 0 - 0 - 0
(163, 0): 0 - 0 - 0 - 0
(164, 0): 0 - 0 - 0 - 0
(165, 0): 0 - 0 - 0 - 0
(166, 0): 0 - 0 - 0 - 0
(167, 0): 0 - 0 - 0 - 0
(168, 0): 0 - 0 - 0 - 0
(169, 0): 0 - 0 - 0 - 0
(170, 0): 0 - 0 - 0 - 0
(171, 0): 0 - 0 - 0 - 0
(172, 0): 0 - 0 - 0 - 0
(173, 0): 0 - 0 - 0 - 0
(174, 0): 0 - 0 - 0 - 0
(175, 0): 0 - 0 - 0 - 0
(176, 0): 0 - 0 - 0 - 0
(177, 0): 0 - 0 - 0 - 0
(178, 0): 0 - 0 - 0 - 0
(179, 0): 0 - 0 - 0 - 0
(180, 0): 0 - 0 - 0 - 0
(181, 0): 0 - 0 - 0 - 0
(182, 0): 0 - 0 - 0 - 0
(183, 0): 0 - 0 - 0 - 0
(184, 0): 0 - 0 - 0 - 0
(185, 0): 0 - 0 - 0 - 0
(186, 0): 0 - 0 - 0 - 0
(187, 0): 0 - 0 - 0 - 0
(188, 0): 0 - 0 - 0 - 0
(189, 0): 0 - 0 - 0 - 0
(190, 0): 0 - 0 - 0 - 0
(191, 0): 0 - 0 - 0 - 0
(192, 0): 0 - 0 - 0 - 0
(193, 0): 0 - 0 - 0 - 0
(194, 0): 0 - 0 - 0 - 0
(195, 0): 0 - 0 - 0 - 0
(196, 0): 0 - 0 - 0 - 0
(197, 0): 0 - 0 - 0 - 0
(198, 0): 0 - 0 - 0 - 0
(199, 0): 0 - 0 - 0 - 0
(200, 0): 0 - 0 - 0 - 0
(201, 0): 0 - 0 - 0 - 0
(202, 0): 0 - 0 - 0 - 0
(203, 0): 0 - 0 - 0 - 0
(204, 0): 0 - 0 - 0 - 0
(205, 0): 0 - 0 - 0 - 0
(206, 0): 0 - 0 - 0 - 0
(207, 0): 0 - 0 - 0 - 0
(208, 0): 0 - 0 - 0 - 0
(209, 0): 0 - 0 - 0 - 0
(210, 0): 0 - 0 - 0 - 0
(211, 0): 0 - 0 - 0 - 0
(212, 0): 0 - 0 - 0 - 0
(213, 0): 0 - 0 - 0 - 0
(214, 0): 0 - 0 - 0 - 0
(215, 0): 0 - 0 - 0 - 0
(216, 0): 0 - 0 - 0 - 0
(217, 0): 0 - 0 - 0 - 0
(218, 0): 0 - 0 - 0 - 0
(219, 0): 0 - 0 - 0 - 0
(220, 0): 0 - 0 - 0 - 0
(221, 0): 0 - 0 - 0 - 0
(222, 0): 0 - 0 - 0 - 0
(223, 0): 0 - 0 - 0 - 0
(224, 0): 0 - 0 - 0 - 0
(225, 0): 0 - 0 - 0 - 0
(226, 0): 0 - 0 - 0 - 0
(227, 0): 0 - 0 - 0 - 0
(228, 0): 0 - 0 - 0 - 0
(229, 0): 0 - 0 - 0 - 0
(230, 0): 0 - 0 - 0 - 0
(231, 0): 0 - 0 - 0 - 0
(232, 0): 0 - 0 - 0 - 0
(233, 0): 0 - 0 - 0 - 0
(234, 0): 0 - 0 - 0 - 0
(235, 0): 0 - 0 - 0 - 0
(236, 0): 0 - 0 - 0 - 0
(237, 0): 0 - 0 - 0 - 0
(238, 0): 0 - 0 - 0 - 0
(239, 0): 0 - 0 - 0 - 0
(240, 0): 0 - 0 - 0 - 0
(241, 0): 0 - 0 - 0 - 0
(242, 0): 0 - 0 - 0 - 0
(243, 0): 0 - 0 - 0 - 0
(244, 0): 0 - 0 - 0 - 0
(245, 0): 0 - 0 - 0 - 0
(246, 0): 0 - 0 - 0 - 0
(247, 0): 0 - 0 - 0 - 0
(248, 0): 0 - 0 - 0 - 0
(249, 0): 0 - 0 - 0 - 0
(250, 0): 0 - 0 - 0 - 0
(251, 0): 0 - 0 - 0 - 0
(252, 0): 0 - 0 - 0 - 0
(253, 0): 0 - 0 - 0 - 0
(254, 0): 0 - 0 - 0 - 0
(255, 0): 0 - 0 - 0 - 255
All of the buckets
are 0 except the last alpha value. And I wonder if anyone has ever used this filter, and is kind enough to explain to me... Big thanks!
I've written a post on how to do this here: http://shapeof.com/archives/2011/08/drawing_a_histogram_with_core_image.html
The short of it is: you need to read the values as floats, not ints, which means you'll have to hook up a CGBitmapContext to blit to. Or if you keep everything in CI land, you'll need another filter to read the data and print something out with it.
When using Core Image (which you should do in your case), the format argument of a context determines what gobbledegook is returned by code that enables pixel-by-pixel processing.
Having chosen the right format, here's the code I used to access output from the CIAreaHistogram:
CIFilter* histogram = [CIFilter filterWithName:@"CIAreaHistogram"];
[histogram setValue:inputImage forKey:@"inputImage"];
[histogram setValue:[CIVector vectorWithX:0.0 Y:0.0 Z:self.inputImage.extent.size.width W:self.inputImage.extent.size.height] forKey:@"inputExtent"];
[histogram setValue:@256 forKey:@"inputCount"];
[histogram setValue:@1.0 forKey:@"inputScale"];
/*id histogramData = [histogram valueForKey:@"outputData"];
if (histogramData)
NSLog(@"outputData: %@", histogramData);*/
@autoreleasepool {
CIImage* histogramImage = [histogram valueForKey:@"outputImage"];
int rowBytes = 256 * 4; // ARGB has 4 components
uint8_t byteBuffer[rowBytes]; // Buffer to render into
EAGLContext *myEAGLContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
NSDictionary *options = @{ kCIContextWorkingColorSpace : [NSNull null] };
CIContext *ctx = [CIContext contextWithEAGLContext:myEAGLContext options:options];
//CIContext* ctx = [[CIContext alloc] init];
[ctx render:histogramImage toBitmap:byteBuffer
rowBytes:rowBytes
bounds:[histogramImage extent]
format:kCIFormatRGBAf
colorSpace:nil];
for (int i = 0; i < 256; i++)
{
const uint8_t* pixel = &byteBuffer[i*4];
printf("%u, %u, %u\n", pixel[0], pixel[1], pixel[2]);
}
}
You need to pass the pixel as a reference.
[imgRep getPixel:&pixel atX:x y:y];
来源:https://stackoverflow.com/questions/24613936/ciareahistogram-gives-me-all-0-except-the-last-element