Objective c 分析NSAffinetTransform和CILineOverlay过滤器生成的位图

Objective c 分析NSAffinetTransform和CILineOverlay过滤器生成的位图,objective-c,cocoa,quartz-graphics,Objective C,Cocoa,Quartz Graphics,我正在尝试使用一系列CIFILTER操作一个图像,然后检查结果图像(位图)的每个字节。长期来看,我不需要显示结果图像(位图)——我只需要在内存中“分析”它。但近期我会在屏幕上显示它,以帮助调试 我有一些“位图检查”代码,在检查我用作输入(从JPG文件加载到NSImage)的NSImage(位图表示)时,这些代码可以正常工作。当我在下面代码生成的outputBitmap上使用它时,它有时会像预期的那样工作。更具体地说,当我使用NSAffineTransform过滤器创建outputBitmap时,

我正在尝试使用一系列CIFILTER操作一个图像,然后检查结果图像(位图)的每个字节。长期来看,我不需要显示结果图像(位图)——我只需要在内存中“分析”它。但近期我会在屏幕上显示它,以帮助调试

我有一些“位图检查”代码,在检查我用作输入(从JPG文件加载到NSImage)的NSImage(位图表示)时,这些代码可以正常工作。当我在下面代码生成的outputBitmap上使用它时,它有时会像预期的那样工作。更具体地说,当我使用NSAffineTransform过滤器创建outputBitmap时,outputBitmap包含我期望的数据。但是,如果我使用CILineOverlay过滤器来创建outputBitmap,位图中的所有字节都没有任何数据。我相信这两个过滤器都按预期工作,因为当我在屏幕上显示它们的结果时(通过outputImageView),它们看起来“正确”。然而,当我检查OutputBitmap时,从CILineOverlay过滤器创建的过滤器是“空的”,而从NSAffinetTransfer创建的过滤器包含数据。此外,如果我将两个过滤器链接在一起,则最终生成的位图似乎只包含上次运行仿射变换时的数据。对我来说似乎很奇怪

我的理解(通过阅读CI编程指南)是,CIImage应该被视为一个“图像配方”,而不是一个实际的图像,因为直到图像被“绘制”后,图像才真正被创建。鉴于此,CIimage位图没有数据是有道理的,但我不明白为什么在运行nsaffinettransform后它有数据,而在运行CILineOverlay转换后它没有数据?基本上,我试图确定从CIImage(myResult)创建NSCIImageRep(下面代码中的ir)是否等同于“绘制”CIImage——换句话说,这是否应该强制填充位图?如果有人知道这个问题的答案,请让我知道——这将节省我几个小时的反复试验

最后,如果答案是“您必须绘制图形上下文”。。。然后我有另一个问题:我是否需要按照Quartz 2D编程指南:图形上下文,清单2-7和2-8:绘制位图图形上下文中描述的内容做一些事情?这就是我要走的路。。。但是似乎有很多代码只是为了强制位图数据被转储到一个数组中,我可以从中获取它。因此,如果有更简单或更好的方法,请让我知道。我只想将myResult中的数据(应该是)放入一个位图数组中,在那里我可以在字节级别访问它。由于我已经有了可以与NSBitmapImageRep一起使用的代码,除非由于某种原因(我不太清楚)这样做是个坏主意,否则我更愿意将我的结果“转换”为NSBitmapImageRep

CIImage * myResult = [transform valueForKey:@"outputImage"]; 
NSImage *outputImage;
NSCIImageRep *ir = [NSCIImageRep alloc];
ir = [NSCIImageRep imageRepWithCIImage:myResult];
outputImage = [[[NSImage alloc] initWithSize:
                              NSMakeSize(inputImage.size.width, inputImage.size.height)]
                             autorelease];
                    [outputImage addRepresentation:ir];
[outputImageView setImage: outputImage];
NSBitmapImageRep *outputBitmap = [[NSBitmapImageRep alloc] initWithCIImage: myResult];
谢谢, 亚当

编辑#1——针对Peter H.评论: 访问位图数据的示例代码

for (row = 0; row < heightInPixels; row++)
  for (column = 0; column < widthInPixels; column++) {
    if (row == 1340) {  //just check this one row, that I know what to expect
        NSLog(@"Row 1340 column %d pixel redByte of pixel is %d",column,thisPixel->redByte);
    }
}
如果我将%d更改为%h,则不会打印任何内容(空白而不是“0”)。如果我将其更改为%@I get“(null)”,而不是上面显示的“0”。另一方面当我只运行nsaffinettransform过滤器,然后执行此代码时,打印的字节包含我期望的数据(无论如何格式化NSLog输出,都会打印一些内容)

在6月14日添加更多代码

// prior code retrieves JPG image from disk and loads into NSImage
CIImage * inputCIimage = [[CIImage alloc] initWithBitmapImageRep:inputBitmap];
if (inputCIimage == nil) {
  NSLog(@"Bailing out.  Could not create CI Image");
  return;
}
NSLog (@"CI Image created.  working on transforms...");
旋转图像的过滤器。。。。这是以前的一种方法,但后来我把它改为“一致”,因为我一直在试图找出什么是错误的

// rotate imageIn by degreesToRotate, using an AffineTransform      
CIFilter *transform = [CIFilter filterWithName:@"CIAffineTransform"];
[transform setDefaults];
[transform setValue:inputCIimage forKey:@"inputImage"];
NSAffineTransform *affineTransform = [NSAffineTransform transform];
[affineTransform transformPoint: NSMakePoint(inputImage.size.width/2, inputImage.size.height / 2)]; 
//inputImage.size.width /2.0,inputImage.size.height /2.0)];
[affineTransform rotateByDegrees:3.75];
[transform setValue:affineTransform forKey:@"inputTransform"];
CIImage * myResult2 = [transform valueForKey:@"outputImage"]; 
要应用CILineOverlay筛选器的筛选器。。。(以前也是在一种方法中)

最后。。。使用结果的代码

if (myResult2 == Nil)
    NSLog(@"Transformations failed");
else {
NSLog(@"Finished transformations successfully ... now render final image");
// make an NSImage from the CIImage (to display it, during initial development)
NSImage *outputImage;
NSCIImageRep *ir = [NSCIImageRep alloc];
// show the tranformed output on screen...
ir = [NSCIImageRep imageRepWithCIImage:myResult2];
outputImage = [[[NSImage alloc] initWithSize:
              NSMakeSize(inputImage.size.width, inputImage.size.height)]
             autorelease];
[outputImage addRepresentation:ir];
[outputImageView setImage: outputImage];  //rotatedImage
在这一点上,转换后的图像在屏幕上显示得很好,不管我应用了哪种转换,也不管我遗漏了哪种转换。如果我将变换“链接”在一起,使#1的输出进入#2,它甚至可以正常工作。因此,对我来说,这似乎表明过滤器正在工作

然而。。。我真正需要使用的代码是“位图分析”代码,它正在检查Results2中(或“应该”中)的位图。并且该代码只在CIAffineTransform过滤器生成的位图上工作。当我使用它检查CILineOverlay生成的位图时,整个位图似乎只包含零

下面是用于分析的代码

// this is the next line after the [outputImageView ...] shown above
[self findLeftEdge :myResult2];
这是findLeftEdge方法的代码

- (void) findLeftEdge :(CIImage*)imageInCI {
    // find the left edge of the input image, assuming it will be the first non-white pixel
    // because we have already applied the Threshold filter

    NSBitmapImageRep *outputBitmap = [[NSBitmapImageRep alloc] initWithCIImage: imageInCI];
    if (outputBitmap == nil)
        NSLog(@"unable to create outputBitmap");
    else 
        NSLog(@"ouputBitmap image rep created -- samples per pixel = %d", [outputBitmap samplesPerPixel]);

    RGBAPixel 
        *thisPixel, 
        *bitmapPixels = (RGBAPixel *)[outputBitmap bitmapData];  

    int 
    row, 
    column,
    widthInPixels = [outputBitmap pixelsWide], 
    heightInPixels = [outputBitmap pixelsHigh];

    //RGBAPixel *leftEdge [heightInPixels];
    struct { 
        int pixelNumber;
        unsigned char pixelValue;
    } leftEdge[heightInPixels];

    // Is this necessary, or does objective-c always intialize it to zero, for me?
    for (row = 0; row < heightInPixels; row++) {
        leftEdge[row].pixelNumber = 0;
        leftEdge[row].pixelValue = 0;
    }

    for (row = 0; row < heightInPixels; row++)
        for (column = 0; column < widthInPixels; column++)  {
            thisPixel = (&bitmapPixels[((widthInPixels * row) + column)]);

            //red is as good as any channel, for this test (assume threshold filter already applied)
            //this should "save" the column number of the first non-white pixel encountered
            if (leftEdge[row].pixelValue < thisPixel->redByte) {
                leftEdge[row].pixelValue = thisPixel->redByte;  
                leftEdge[row].pixelNumber = column;
            }
            // For debugging, display contents of each pixel
            //NSLog(@"Row %d column %d pixel redByte of pixel is %@",row,column,thisPixel->redByte);
            // For debugging, display contents of each pixel on one row
            //if (row == 1340) {
            //  NSLog(@"Row 1340 column %d pixel redByte of pixel is %@",column,thisPixel->redByte);
            //}

        }

    // For debugging, display the left edge that we discovered
    for (row = 0; row < heightInPixels; row++) {
        NSLog(@"Left edge on row %d was at pixel #%d", row, leftEdge[row].pixelNumber);
    }

    [outputBitmap release];  
}
for(行=0;行redByte);
}
}
除了语法错误外,此代码不起作用,因为您从未分配给
thisPixel
。您在索引中循环,没有任何结果,因为您从来没有在这些索引中实际查找像素值并将其分配给
thisPixel
,以便对其进行检查

NSLog
语句之前添加这样的赋值


此外,如果您关心的唯一一行是1340,那么就不需要在行之间循环。使用
if
语句检查1340是否小于高度,如果小于,则仅执行列循环。(另外,不要在代码中嵌入像这样的幻数文字。给这个常量一个名称,解释数字1340的意义,也就是说,为什么它是你唯一关心的行。)

你声称位图数据包含“全零”,但你只看到每个像素一个字节。假设第一个组件是红色组件,并且假设每个组件的数据为一个字节;如果数据是alpha-first或浮点型,则其中一个或两个assu
// this is the next line after the [outputImageView ...] shown above
[self findLeftEdge :myResult2];
- (void) findLeftEdge :(CIImage*)imageInCI {
    // find the left edge of the input image, assuming it will be the first non-white pixel
    // because we have already applied the Threshold filter

    NSBitmapImageRep *outputBitmap = [[NSBitmapImageRep alloc] initWithCIImage: imageInCI];
    if (outputBitmap == nil)
        NSLog(@"unable to create outputBitmap");
    else 
        NSLog(@"ouputBitmap image rep created -- samples per pixel = %d", [outputBitmap samplesPerPixel]);

    RGBAPixel 
        *thisPixel, 
        *bitmapPixels = (RGBAPixel *)[outputBitmap bitmapData];  

    int 
    row, 
    column,
    widthInPixels = [outputBitmap pixelsWide], 
    heightInPixels = [outputBitmap pixelsHigh];

    //RGBAPixel *leftEdge [heightInPixels];
    struct { 
        int pixelNumber;
        unsigned char pixelValue;
    } leftEdge[heightInPixels];

    // Is this necessary, or does objective-c always intialize it to zero, for me?
    for (row = 0; row < heightInPixels; row++) {
        leftEdge[row].pixelNumber = 0;
        leftEdge[row].pixelValue = 0;
    }

    for (row = 0; row < heightInPixels; row++)
        for (column = 0; column < widthInPixels; column++)  {
            thisPixel = (&bitmapPixels[((widthInPixels * row) + column)]);

            //red is as good as any channel, for this test (assume threshold filter already applied)
            //this should "save" the column number of the first non-white pixel encountered
            if (leftEdge[row].pixelValue < thisPixel->redByte) {
                leftEdge[row].pixelValue = thisPixel->redByte;  
                leftEdge[row].pixelNumber = column;
            }
            // For debugging, display contents of each pixel
            //NSLog(@"Row %d column %d pixel redByte of pixel is %@",row,column,thisPixel->redByte);
            // For debugging, display contents of each pixel on one row
            //if (row == 1340) {
            //  NSLog(@"Row 1340 column %d pixel redByte of pixel is %@",column,thisPixel->redByte);
            //}

        }

    // For debugging, display the left edge that we discovered
    for (row = 0; row < heightInPixels; row++) {
        NSLog(@"Left edge on row %d was at pixel #%d", row, leftEdge[row].pixelNumber);
    }

    [outputBitmap release];  
}
- (CIImage*) applyCropToCI:(CIImage*) imageIn {
rectToCrop { 
    // crop the rectangle specified from the input image
    CIFilter *crop = [CIFilter filterWithName:@"CICrop"];
    [crop setDefaults];  
    [crop setValue:imageIn forKey:@"inputImage"];
//  [crop setValue:rectToCrop forKey:@"inputRectangle"];  //vector defaults to 0,0,300,300
    //CIImage * myResult = [transform valueForKey:@"outputImage"]; //this is the way it was "in-line", before putting this code into a method
    return [crop valueForKey:@"outputImage"];   //does this need to be retained?
}
for (row = 0; row < heightInPixels; row++)
  for (column = 0; column < widthInPixels; column++) {
    if (row == 1340) {  //just check this one row, that I know what to expect
        NSLog(@"Row 1340 column %d pixel redByte of pixel is %d",column,thisPixel->redByte);
  }
}