繁体   English   中英

iPhone SDK-优化for循环

[英]iPhone SDK - Optimize a for loop

我正在开发图像处理应用程序,并且正在寻找调整代码的建议。

我需要将图像划分为块(80x80),然后为每个块计算平均颜色。

我的第一个方法包含主循环,第二个方法称为:

- (NSArray*)getRGBAsFromImage:(UIImage *)image {
int width       = image.size.width;
int height  = image.size.height;

int blocPerRow  = 80;
int blocPerCol  = 80;

int pixelPerRowBloc = width  / blocPerRow;
int pixelPerColBloc = height / blocPerCol;

int xx,yy;

// Row loop
for (int i=0; i<blocPerRow; i++) {

    xx = (i * pixelPerRowBloc) + 1;

    // Colon loop
    for (int j=0; j<blocPerCol; j++) {

        yy = (j * pixelPerColBloc) +1;

        [self getRGBAsFromImageBloc:image 
                    atX:xx 
                    andY:yy 
                    withPixelPerRow:pixelPerRowBloc 
                    AndPixelPerCol:pixelPerColBloc];
    }
}
// return my NSArray not done yet !
}

我的第二种方法浏览像素块并返回ColorStruct:

- (ColorStruct*)getRGBAsFromImageBloc:(UIImage*)image 
                            atX:(int)xx 
                            andY:(int)yy 
                            withPixelPerRow:(int)pixelPerRow 
                            AndPixelPerCol:(int)pixelPerCol {

// First get the image into your data buffer
CGImageRef imageRef = [image CGImage];

NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

unsigned char *rawData = malloc(height * width * 4);

NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;

    NSUInteger bitsPerComponent = 8;

CGContextRef context = CGBitmapContextCreate(rawData, width, height,
                bitsPerComponent, bytesPerRow, colorSpace,
                kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

CGColorSpaceRelease(colorSpace);

CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
CGContextRelease(context);

// Now your rawData contains the image data in the RGBA8888 pixel format.
int byteIndex = (bytesPerRow * yy) + xx * bytesPerPixel;

    int red = 0;
    int green = 0;
    int blue = 0;
    int alpha = 0;
    int currentAlpha;

    // bloc loop
    for (int i = 0 ; i < (pixelPerRow*pixelPerCol) ; ++i) {
        currentAlpha = rawData[byteIndex + 3];

        red   += (rawData[byteIndex]        )   * currentAlpha;
        green += (rawData[byteIndex + 1]) * currentAlpha;
        blue  += (rawData[byteIndex + 2]) * currentAlpha;
        alpha += currentAlpha;

        byteIndex += 4;

        if ( i == pixelPerRow ) {
            byteIndex += (width-pixelPerRow) * 4;
        }
    }
    red     /= alpha;
    green /= alpha;
    blue    /= alpha;

    ColorStruct *bColorStruct = newColorStruct(red, blue, green);

    free(rawData);

    return bColorStruct;
   }

颜色结构:

typedef struct {
  int red;
    int blue;
    int green;
} ColorStruct;

与构造函数:

ColorStruct *newColorStruct(int red, int blue, int green) {
ColorStruct *ret = malloc(sizeof(ColorStruct));
ret->red = red;
    ret->blue = blue;
ret->green = green;
return ret;
}

如您所见,我具有三个循环级别:行循环,冒号循环和块循环。

我已经测试了我的代码,一张320x480的图片大约需要5到6秒。

欢迎任何帮助。

谢谢,Bahaaldine

为其提供Grand Central Dispatch似乎是一个完美的问题?

我认为此代码中的主要问题是图像读取过多。 对于每个(!)块,整个图像都被加载到内存中(malloc也很昂贵)。 您应该一次预加载图像数据(将其缓存),然后在getRGBAsFromImageBloc()使用该内存。 现在对于320x480的图片,您有4 x 6 = 24个块。 因此,仅使用缓存,您就可以加快应用程序的速度。

一天结束时,拍摄图像并依次对每个像素执行三个乘法和五个加法运算总是比较慢的。

幸运的是,您可以将您正在做的事情视为将图像从一种尺寸插入另一种尺寸的一种特殊情况-即,图像的平均像素与将图像尺寸调整为1x1的图像相同(假设正在使用调整大小的图像)某种形式的线性插值,但这通常是标准的插值方式),并且有一些高度优化的选项(或者至少比您不费力气就能获得的优化的选项)做为iPhone图形的一部分库。 首先,我会尝试使用Quartz方法来调整图像大小:

    CGImageRef sourceImage = yourImage;

int numBytesPerPixel = 4;
u_char* scaledImageData = (u_char*)malloc(numBytesPerPixel);

CGColorSpaceRef colorspace = CGImageGetColorSpace(sourceImage);
CGContextRef context = CGBitmapContextCreate (scaledImageData, 1, 1, 8, numBytesPerPixel, colorspace, kCGImageAlphaNoneSkipFirst);
CGColorSpaceRelease(colorspace);
CGContextDrawImage(context, CGRectMake(0,0,1,1), sourceImage);

int a = scaledImageData[0];
int r = scaledImageData[1];
int g = scaledImageData[2];
int b = scaledImageData[3];

(这只是将原始图像缩小到1像素,并且没有显示子区域的裁剪,但是很遗憾,我现在没有时间使用该代码-如果您尝试实现它并陷入困境,请添加注释,然后我可以告诉您如何做)。

如果这不起作用,您可以始终尝试使用OpenGL ES进行此操作(在需要缩放的图像部分中创建纹理,将其渲染到1x1缓冲区中,然后从缓冲区中测试结果)。 这要复杂得多,但可能具有一些优势,因为它使您可以访问GPU,对于大型图像,这可能要快得多。

希望有道理并能帮助...

PS-一定要遵循y0prst的建议,只读取一次图像-这是一个简单的解决方案,可以为您带来大量的性能。

PPS-我尚未测试代码,因此通常需要注意。

您正在检查每个像素-看起来,无论您如何循环遍历它,都将花费大致相同的时间(假设您仅检查每个像素一次)。

我建议在块内使用随机采样-每个“第n个”像素,这将减少循环时间(和精度),或者允许调整粒度。

现在,如果存在一种用于计算一组像素的平均值的算法,则可以考虑将其作为替代方案。

您可以通过在循环中间不调用方法来加快处理速度。 只需将代码内联即可。

添加:此外,如果您有足够的内存,则可以尝试只绘制一次图像,而不是循环重复。

完成此操作后,您还可以尝试将一些乘法提升到内部循环之外,以提高一些性能(尽管编译器可能会为您优化其中的一部分)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM