CIFilter / CIKernel中的最大图像尺寸是多少?

4
有没有人知道自定义CIFilters的图像大小限制是什么?当图像高达2百万像素时,我创建的滤镜表现如预期,但当图像更大时则产生非常奇怪的结果。我在我的可可应用程序和石英合成器中都进行了测试。我开发的滤镜是一种几何类型的扭曲滤镜,需要一个ROI和一个跨越整个输入图像的DOD(我认为)。我已经为重新映射全景图像创建了这个滤镜,所以我希望它能在非常大的(50-100兆像素)图像上工作。
作为一个简单的测试,考虑以下CIFilter(可以在Quartz Composer中使用),它只是将图像平移,使图像的左下角被平移到中心(我知道这可以用仿射变换来完成,但我需要在更复杂的滤镜中执行这样的操作)。当图像为2000x1000时,此滤镜按预期工作,但当输入图像为4000x2000像素时,则产生奇怪的结果。问题在于,要么平移未将角落精确地移动到中心,要么图像输出完全消失。我注意到在大型图像上使用更复杂的滤镜时会出现其他奇怪的问题,但我认为这个简单的滤镜说明了我的问题,并且可以在Quartz Composer中复制。
kernel vec4 equidistantProjection(sampler src, __color color)
{
     vec2 coordinate = samplerCoord(src);
     vec2 result;
     vec4 outputImage;

     result.x = (coordinate.x - samplerSize(src).x / 2.0);
     result.y = (coordinate.y - samplerSize(src).y / 2.0);

     outputImage = unpremultiply(sample(src,result));

     return premultiply(outputImage);
}

使用工作坐标而不是采样器坐标时出现相同的奇怪行为,但在这种情况下,对于大小为2000x1000的图像会出错,但对于大小为1000x500的图像则可以正常工作。

kernel vec4 equidistantProjection(sampler src, __color color, vec2 destinationDimensions)
{
     vec2 coordinate = destCoord();
     vec2 result;
     vec4 outputImage;

     result.x = (coordinate.x - destinationDimensions.x / 2.0);
     result.y = (coordinate.y - destinationDimensions.y / 2.0);

     outputImage = unpremultiply(sample(src,result));
     outputImage = unpremultiply(sample(src,samplerTransform(src, result)));

     return premultiply(outputImage);
}

为了参考,我已经在我的滤镜的- (CIImage *)outputImage方法的Objective-C部分中添加了以下内容,以设置DOD为整个输入图像。

- (CIImage *)outputImage
{
    CISampler *src = [CISampler samplerWithImage: inputImage];



     NSArray * outputExtent = [NSArray arrayWithObjects:
            [NSNumber numberWithInt:0],
            [NSNumber numberWithInt:0],
            [NSNumber numberWithFloat:[inputImage extent].size.width],
            [NSNumber numberWithFloat:[inputImage extent].size.height],nil];


return [self apply: filterKernel, src, inputColor, zoom, viewBounds, inputOrigin,
     kCIApplyOptionDefinition, [src definition], kCIApplyOptionExtent, outputExtent, nil];

}

此外,我添加了以下方法来设置ROI,我在我的- (id)init方法中调用它:[filterKernel setROISelector:@selector(regionOf:destRect:userInfo:)];

注:ROI指的是感兴趣区域(Region of Interest),在图像处理中常用。
- (CGRect) regionOf:(int)samplerIndex destRect:(CGRect)r userInfo:obj
{

     return r;
}

任何有关此问题的帮助或建议将不胜感激。我相信CIFilters可以处理更大的图像,因为我已经使用CIBumpDistortion处理了超过50兆像素的图像,所以我一定做错了什么。有什么想法吗?
1个回答

5

在使用CoreImage时,我发现它会将大图像分割成几个部分。例如,在您的情况下,4k x 2k的图像可以被分割成4个2k x 1k的图像并单独渲染。不幸的是,这种优化技巧会影响samplerCoord,并且一些与坐标有关的滤镜在大图像上无法正常工作。

我的解决方案是使用destCoord而不是samplerCoord。当然,您应该记住图像可能以非零原点和destCoord渲染。我编写了自己的过滤器,因此能够将整个范围作为vec4参数传递。

示例:尝试使用CIFilter生成图像,类似于以下内容:

float gray = (samplerCoord.x / samplerSize.width) * (samplerCoord.y / samplerSize.height);

这个输出应该在(0,0)处给出黑色,在(1,1)处给出白色,对吗?然而,对于大图片,你会看到几个四边形而不是单一的渐变。这是由于优化的渲染来自CoreImage引擎,我还没有找到一种方法来传递它,但你可以按照以下方式重新编写内核代码:
float gray = ((destCoord.x - rect.x) / rect.size) * ((destCoord.y - rect.y) / rect.height)

需要传递的是采样器的实际范围,用于此目的我使用了[inputImage extent],但这取决于过滤器并且在你的情况下可能会有所不同。

希望这个解释很清楚。顺便说一下,看起来系统内核即使处理大型图像也能正常工作,因此你只需在自定义内核中使用这些技巧。


是否有方法可以事先知道平铺图像的原始位置?如果您传入4维范围(原点x,原点y,宽度,高度),是否可以通过编程方式确定CoreImage将如何将图像切割成部分以知道每个块的原点位置?或者我漏掉了什么。我过去一直使用destCoord和samplerTransform,并且明确地将输入图像的整个范围作为ROI进行传递,取得了不错的结果。只要输入图像足够小,这种方法就运行良好。 - Kevin Gross
@KevinGross 你找到解决方案了吗?谢谢! - Artem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接