Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trim UIImage border

Here's an example of an image I would like to trim. I want to get rid of the borders around the image (in this case the top and bottom black bars).

UIImage to trim

I found a library on Github: CKImageAdditions, however it doesn't seem to work. When I pass in a UIColor (with RGB colours) it just returns the same image.

I can find a lot of examples and category classes that would trim a UIImage with any transparent pixels as the border, but in this case I need to trim the black colour. I have sampled the colour in my images and they the colour value is indeed 255, but it doesn't seem to match what the above library is looking for.

Does anyone have a library they have used or any insight? I've searched and searched and CKImageAdditions has been the only thing I can find that advertises to trim with a colour (although, unfortunately doesn't work in my case).

like image 208
runmad Avatar asked Nov 18 '13 20:11

runmad


2 Answers

I ended up customizing a method from a function in CKImageAdditions that had this functionality supposedly but I couldn't get it to work. It just wouldn't trim the colour, so I instead check the pixel's RGB values to be \0 (black). The CKImageAdditions just couldn't find the black pixels, unfortunately.

Since the images I wanted to trim sometimes didn't have super black bars (sometimes they'd have a stray pixel with a lighter dark colour or something) I added GPUImage functionality to the method, which basically just creates a black and white version of the image with a strong filter on it so any dark colours become black and any light colours become white, making the black bar borders much more prominent and ensuring better results when I look for them in the method. And of course I crop the original image at the end based on the results from the black/white image.

Here's my code:

typedef struct Pixel { uint8_t r, g, b, a; } Pixel;

+(UIImage*)trimBlack:(UIImage*)originalImage {
    GPUImagePicture *stillImageSource = [[GPUImagePicture alloc] initWithImage:originalImage];
    GPUImageLuminanceThresholdFilter *stillImageFilter = [[GPUImageLuminanceThresholdFilter alloc] init];
    stillImageFilter.threshold = 0.1;
    [stillImageSource addTarget:stillImageFilter];
    [stillImageSource processImage];
    UIImage *imageToProcess = [stillImageFilter imageFromCurrentlyProcessedOutput];

    RMImageTrimmingSides sides = RMImageTrimmingSidesAll;
    CGImageRef image = imageToProcess.CGImage;
    void * bitmapData = NULL;
    CGContextRef context = CKBitmapContextAndDataCreateWithImage(image, &bitmapData);

    Pixel *data = bitmapData;

    size_t width = CGBitmapContextGetWidth(context);
    size_t height = CGBitmapContextGetHeight(context);

    size_t top = 0;
    size_t bottom = height;
    size_t left = 0;
    size_t right = width;

    // Scan the left
    if (sides & RMImageTrimmingSidesLeft) {
        for (size_t x = 0; x < width; x++) {
            for (size_t y = 0; y < height; y++) {
                Pixel pixel = data[y * width + x];
                if (pixel.r != '\0' && pixel.g != '\0' && pixel.b != '\0') {
                    left = x;
                    goto SCAN_TOP;
                }
            }
        }
    }

    // Scan the top
SCAN_TOP:
    if (sides & RMImageTrimmingSidesTop) {
        for (size_t y = 0; y < height; y++) {
            for (size_t x = 0; x < width; x++) {
                Pixel pixel = data[y * width + x];
                if (pixel.r != '\0' && pixel.g != '\0' && pixel.b != '\0') {
                    top = y;
                    goto SCAN_RIGHT;
                }
            }
        }
    }

    // Scan the right
SCAN_RIGHT:
    if (sides & RMImageTrimmingSidesRight) {
        for (size_t x = width-1; x >= left; x--) {
            for (size_t y = 0; y < height; y++) {
                Pixel pixel = data[y * width + x];
                if (pixel.r != '\0' && pixel.g != '\0' && pixel.b != '\0') {
                    right = x;
                    goto SCAN_BOTTOM;
                }
            }
        }
    }

    // Scan the bottom
SCAN_BOTTOM:
    if (sides & RMImageTrimmingSidesBottom) {
        for (size_t y = height-1; y >= top; y--) {
            for (size_t x = 0; x < width; x++) {
                Pixel pixel = data[y * width + x];
                if (pixel.r != '\0' && pixel.g != '\0' && pixel.b != '\0') {
                    bottom = y;
                    goto FINISH;
                }
            }
        }
    }

FINISH:
    CGContextRelease(context);
    free(bitmapData);

    CGRect rect = CGRectMake(left, top, right - left, bottom - top);
    return [originalImage imageCroppedToRect:rect];
}

Thanks to all the hard work from the developers of the libraries used in the code above and of course all credit goes to them!

like image 58
runmad Avatar answered Nov 20 '22 11:11

runmad


I tweaked your solution now that GPUImage's imageFromCurrentlyProcessedOutput method has been removed and replaced with another method that doesn't work at all.

Also tweaked how the image was cropped and removed some stuff that was just broken. Seems to work.

typedef struct Pixel { uint8_t r, g, b, a; } Pixel;

-(UIImage*)trimBlack:(UIImage*)originalImage {
    GPUImagePicture *stillImageSource = [[GPUImagePicture alloc] initWithImage:originalImage];
    GPUImageLuminanceThresholdFilter *stillImageFilter = [[GPUImageLuminanceThresholdFilter alloc] init];
    stillImageFilter.threshold = 0.1;
    [stillImageSource addTarget:stillImageFilter];
    [stillImageSource processImage];
    [stillImageSource useNextFrameForImageCapture];
    
    //UIImage *imageToProcess = [stillImageFilter imageFromCurrentFramebufferWithOrientation:UIImageOrientationUp];
    //UIImage *imageToProcess = [UIImage imageWithCGImage:[stillImageFilter newCGImageFromCurrentlyProcessedOutput]];
    UIImage *imageToProcess = originalImage;
    
    //RMImageTrimmingSides sides = RMImageTrimmingSidesAll;
    CGImageRef image = imageToProcess.CGImage;
    void * bitmapData = NULL;
    CGContextRef context = CKBitmapContextAndDataCreateWithImage(image, &bitmapData);
    
    Pixel *data = bitmapData;
    
    size_t width = CGBitmapContextGetWidth(context);
    size_t height = CGBitmapContextGetHeight(context);
    
    size_t top = 0;
    size_t bottom = height;
    size_t left = 0;
    size_t right = width;
    
    // Scan the left
    //if (sides & RMImageTrimmingSidesLeft) {
        for (size_t x = 0; x < width; x++) {
            for (size_t y = 0; y < height; y++) {
                Pixel pixel = data[y * width + x];
                if (pixel.r != '\0' && pixel.g != '\0' && pixel.b != '\0') {
                    left = x;
                    goto SCAN_TOP;
                }
            }
        }
    //}
    
    // Scan the top
SCAN_TOP:
    //if (sides & RMImageTrimmingSidesTop) {
        for (size_t y = 0; y < height; y++) {
            for (size_t x = 0; x < width; x++) {
                Pixel pixel = data[y * width + x];
                if (pixel.r != '\0' && pixel.g != '\0' && pixel.b != '\0') {
                    top = y;
                    goto SCAN_RIGHT;
                }
            }
        }
    //}
    
    // Scan the right
SCAN_RIGHT:
    //if (sides & RMImageTrimmingSidesRight) {
        for (size_t x = width-1; x >= left; x--) {
            for (size_t y = 0; y < height; y++) {
                Pixel pixel = data[y * width + x];
                if (pixel.r != '\0' && pixel.g != '\0' && pixel.b != '\0') {
                    right = x;
                    goto SCAN_BOTTOM;
                }
            }
        }
    //}
    
    // Scan the bottom
SCAN_BOTTOM:
    //if (sides & RMImageTrimmingSidesBottom) {
        for (size_t y = height-1; y >= top; y--) {
            for (size_t x = 0; x < width; x++) {
                Pixel pixel = data[y * width + x];
                if (pixel.r != '\0' && pixel.g != '\0' && pixel.b != '\0') {
                    bottom = y;
                    goto FINISH;
                }
            }
        }
    //}
    
FINISH:
    CGContextRelease(context);
    free(bitmapData);
    
    CGRect rect = CGRectMake(left, top, right - left, bottom - top);
    CGImageRef imageRef = CGImageCreateWithImageInRect([originalImage CGImage], rect);
    UIImage *croppedImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    return croppedImage;
}
like image 1
Dave Cole Avatar answered Nov 20 '22 11:11

Dave Cole