Hi,
I've made a class that converts an image into grayscale. But it works way too slow. Is there a way to make it work faster?
Here's my class:
@implementation PixelProcessing
SYNTHESIZE_SINGLETON_FOR_CLASS(PixelProcessing);
#define bytesPerPixel 4
#define bitsPerComponent 8
-(UIImage*)scaleAndRotateImage: (UIImage*)img withMaxResolution: (int)kMaxResolution
{
    CGImageRef imgRef = img.CGImage;
    CGFloat width = CGImageGetWidth(imgRef);
    CGFloat height = CGImageGetHeight(imgRef);
    CGAffineTransform transform = CGAffineTransformIdentity;
    CGRect bounds = CGRectMake(0, 0, width, height);
    if ( (kMaxResolution != 0) && (width > kMaxResolution || height > kMaxResolution) ) {
     CGFloat ratio = width/height;
     if (ratio > 1) {
      bounds.size.width = kMaxResolution;
      bounds.size.height = bounds.size.width / ratio;
     }
     else {
      bounds.size.height = kMaxResolution;
      bounds.size.width = bounds.size.height * ratio;
     }
    }
    CGFloat scaleRatio;
    if (kMaxResolution != 0){
     scaleRatio = bounds.size.width / width;
    } else
    {
     scaleRatio = 1.0f;
    }
    CGSize imageSize = CGSizeMake(CGImageGetWidth(imgRef), CGImageGetHeight(imgRef));
    CGFloat boundHeight;
    UIImageOrientation orient = img.imageOrientation;
    switch(orient) {
     case UIImageOrientationUp: //EXIF = 1
      transform = CGAffineTransformIdentity;
      break;
     case UIImageOrientationUpMirrored: //EXIF = 2
      transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
      transform = CGAffineTransformScale(transform, -1.0, 1.0);
      break;
     case UIImageOrientationDown: //EXIF = 3
      transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
      transform = CGAffineTransformRotate(transform, M_PI);
      break;
     case UIImageOrientationDownMirrored: //EXIF = 4
      transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
      transform = CGAffineTransformScale(transform, 1.0, -1.0);
      break;
     case UIImageOrientationLeftMirrored: //EXIF = 5
      boundHeight = bounds.size.height;
      bounds.size.height = bounds.size.width;
      bounds.size.width = boundHeight;
      transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
      transform = CGAffineTransformScale(transform, -1.0, 1.0);
      transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
      break;
     case UIImageOrientationLeft: //EXIF = 6
      boundHeight = bounds.size.height;
      bounds.size.height = bounds.size.width;
      bounds.size.width = boundHeight;
      transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
      transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
      break;
     case UIImageOrientationRightMirrored: //EXIF = 7
      boundHeight = bounds.size.height;
      bounds.size.height = bounds.size.width;
      bounds.size.width = boundHeight;
      transform = CGAffineTransformMakeScale(-1.0, 1.0);
      transform = CGAffineTransformRotate(transform, M_PI / 2.0);
      break;
     case UIImageOrientationRight: //EXIF = 8
      boundHeight = bounds.size.height;
      bounds.size.height = bounds.size.width;
      bounds.size.width = boundHeight;
      transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
      transform = CGAffineTransformRotate(transform, M_PI / 2.0);
      break;
     default:
      [NSException raise:NSInternalInconsistencyException format: @"Invalid image orientation"];
    }
    UIGraphicsBeginImageContext(bounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    if (orient == UIImageOrientationRight || orient == UIImageOrientationLeft) {
     CGContextScaleCTM(context, -scaleRatio, scaleRatio);
     CGContextTranslateCTM(context, -height, 0);
    }
    else {
     CGContextScaleCTM(context, scaleRatio, -scaleRatio);
     CGContextTranslateCTM(context, 0, -height);
    }
    CGContextConcatCTM(context, transform);
    CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef);
    UIImage *tempImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return tempImage;
}
#pragma mark Getting Ans Writing Pixels
-(float*) getColorForPixel: (NSUInteger)xCoordinate andForY: (NSUInteger)yCoordinate
{
    int byteIndex = (bytesPerRow * yCoordinate) + xCoordinate * bytesPerPixel;
    float *colorToReturn = malloc(3);
    colorToReturn[0] = bitmap[byteIndex] / 255.f; //Red
    colorToReturn[1] = bitmap[byteIndex + 1] / 255.f; //Green
    colorToReturn[2] = bitmap[byteIndex + 2] / 255.f; //Blue
    return colorToReturn;
}
-(void) writeColor: (float*)colorToWrite forPixelAtX: (NSUInteger)xCoordinate andY: (NSUInteger)yCoordinate
{
    int byteIndex = (bytesPerRow * yCoordinate) + xCoordinate * bytesPerPixel;
    bitmap[byteIndex] = (unsigned char) ( colorToWrite[0] * 255);
    bitmap[byteIndex + 1] = (unsigned char) ( colorToWrite[1] * 255);
    bitmap[byteIndex + 2] = (unsigned char) ( colorToWrite[2] * 255);
}
#pragma mark Bitmap
-(float) getAverageBrightnessForImage: (UIImage*)img
{
    UIImage *tempImage = [self scaleAndRotateImage: img withMaxResolution: 100];
    unsigned char *rawData = [self getBytesForImage: tempImage];
    double aBrightness = 0;
    for(int y = 0; y < tempImage.size.height; y++) {
        for(int x = 0; x < tempImage.size.width; x++) {
      int byteIndex = ( (tempImage.size.width * y) + x) * bytesPerPixel;
      aBrightness += (rawData[byteIndex] + rawData[byteIndex + 1] + rawData[byteIndex + 2]);
        }
    }
    free(rawData);
    aBrightness /= 3.0f;
    aBrightness /= 255.0f;
    aBrightness /= tempImage.size.width * tempImage.size.height;
    return aBrightness;
}
-(unsigned char*) getBytesForImage: (UIImage*)pImage
{
    CGImageRef image = [pImage CGImage];
    NSUInteger width = CGImageGetWidth(image);
    NSUInteger height = CGImageGetHeight(image);
    bytesPerRow = bytesPerPixel * width;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char *rawData = malloc(height * width * bytesPerPixel);
    CGContextRef context = CGBitmapContextCreate(rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast);
    CGColorSpaceRelease(colorSpace);
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
    CGContextRelease(context);
    return rawData;
}
-(void) loadWithImage: (UIImage*)img
{   
    averageBrightness = [self getAverageBrightnessForImage: img];
    currentImage = [self scaleAndRotateImage: img withMaxResolution: 0];
    imgWidth = currentImage.size.width;
    imgHeight = currentImage.size.height;
    bitmap = [self getBytesForImage: currentImage];
    bytesPerRow = bytesPerPixel * imgWidth;
}
-(void) processImage
{   
    // now convert to grayscale
    for(int y = 0; y < imgHeight; y++) {
        for(int x = 0; x < imgWidth; x++) {
      float *currentColor = [self getColorForPixel: x andForY: y];
      //Grayscale
      float averageColor = (currentColor[0] + currentColor[1] + currentColor[2]) / 3.0f;
      averageColor += 0.5f - averageBrightness;
      if (averageColor > 1.0f) averageColor = 1.0f;
      currentColor[0] = averageColor;
      currentColor[1] = averageColor;
      currentColor[2] = averageColor;
      [self writeColor: currentColor forPixelAtX: x andY: y];
      free(currentColor);
        }
    }
}
-(UIImage*) getProcessedImage
{
    // create a UIImage
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(bitmap, imgWidth, imgHeight, bitsPerComponent, bytesPerRow, colorSpace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast);
    CGImageRef image = CGBitmapContextCreateImage(context);
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);
    UIImage *resultUIImage = [UIImage imageWithCGImage: image];
    CGImageRelease(image);
    return resultUIImage;
}
-(void) releaseCurrentImage
{
    free(bitmap);
}
@end
And I convert an image into grayscale in the following way:
    [ [PixelProcessing sharedPixelProcessing] loadWithImage: imageToDisplay.image];
    [ [PixelProcessing sharedPixelProcessing] processImage];
    imageToDisplay.image = [ [PixelProcessing sharedPixelProcessing] getProcessedImage];
    [ [PixelProcessing sharedPixelProcessing] releaseCurrentImage];
Why is it working so slow? Is there a way to get float values for RGB color components of pixel? How can I optimize it?
Thanks.