7

i load a transparent .png into an UIImage.

How to i calculte the real bounding-box. E.g. if the the real image is smaller than the .png-dimensions.

Thanks for helping

Hase
  • 99
  • 3
  • 5
    Let me rephrase: "How do I find the smallest rectangle in an partially transparent image that contains all pixels with an alpha value above a certain threshold?". Is that what you meant? – Nikolai Ruhe Mar 23 '10 at 16:24
  • 2
    Got an answer from http://stackoverflow.com/questions/6521987/crop-uiimage-to-alpha and user404709's answer works for me. – Wayne Liu Mar 19 '12 at 04:45

2 Answers2

8

Assuming "bounding box of an image" is simply a rectangle in the image, specified in pixel coordinates.

You want the rectangle of image which contains all pixels with an alpha greater than threshold (it is equivalent to say that all pixels that are not in this rectangle have an alpha lower than threshold). After that you can transform this rectangle in screen coordinate (or whatever you want).

The basic algorithm is to start with a rectangle containing the whole image, then shrink the rectangle horizontally, then vertically (or vertically then horizontally).

I don't know Objective-C, so I put the code in pure C (some functions are just to make the code more clearer):

typedef struct Rectangle
{
    unsigned int x1, y1, x2, y2;
} Rectangle;

typedef struct Image
{
    unsigned int height,width;
    unsigned int* data;
} Image;

unsigned char getPixelAlpha(Image* img, unsigned int x, unsigned int y)
{
    unsigned int pixel = 0; // default = fully transparent

    if(x >= img->width || y >= img->height)
        return pixel; // Consider everything not in the image fully transparent

    pixel = img->data[x + y * img->width];
    return (unsigned char)((pixel & 0xFF000000) >> 24);
}

void shrinkHorizontally(Image* img, unsigned char threshold, Rectangle* rect)
{
    int x, y;

    // Shrink from left
    for(x = 0; x < (int)img->width; x++)
    {
        // Find the maximum alpha of the vertical line at x
        unsigned char lineAlphaMax = 0;
        for(y = 0; y < (int)img->height; y++)
        {
            unsigned char alpha = getPixelAlpha(img,x,y);
            if(alpha > lineAlphaMax)
                lineAlphaMax = alpha;
        }

        // If at least on pixel of the line if more opaque than 'threshold'
        // then we found the left limit of the rectangle
        if(lineAlphaMax >= threshold)
        {
            rect->x1 = x;
            break;
        }
    }


    // Shrink from right
    for(x = img->width - 1; x >= 0; x--)
    {
        // Find the maximum alpha of the vertical line at x
        unsigned char lineAlphaMax = 0;
        for(y = 0; y < (int)img->height; y++)
        {
            unsigned char alpha = getPixelAlpha(img,x,y);
            if(alpha > lineAlphaMax)
                lineAlphaMax = alpha;
        }

        // If at least on pixel of the line if more opaque than 'threshold'
        // then we found the right limit of the rectangle
        if(lineAlphaMax >= threshold)
        {
            rect->x2 = x;
            break;
        }
    }
}

// Almost the same than shrinkHorizontally.
void shrinkVertically(Image* img, unsigned char threshold, Rectangle* rect)
{
    int x, y;

    // Shrink from up
    for(y = 0; y < (int)img->height; y++)
    {
        // Find the maximum alpha of the horizontal line at y
        unsigned char lineAlphaMax = 0;
        for(x = 0; x < (int)img->width; x++)
        {
            unsigned char alpha = getPixelAlpha(img,x,y);
            if(alpha > lineAlphaMax)
                lineAlphaMax = alpha;
        }

        // If at least on pixel of the line if more opaque than 'threshold'
        // then we found the up limit of the rectangle
        if(lineAlphaMax >= threshold)
        {
            rect->y1 = x;
            break;
        }
    }


    // Shrink from bottom
    for(y = img->height- 1; y >= 0; y--)
    {
        // Find the maximum alpha of the horizontal line at y
        unsigned char lineAlphaMax = 0;
        for(x = 0; x < (int)img->width; x++)
        {
            unsigned char alpha = getPixelAlpha(img,x,y);
            if(alpha > lineAlphaMax)
                lineAlphaMax = alpha;
        }

        // If at least on pixel of the line if more opaque than 'threshold'
        // then we found the bottom limit of the rectangle
        if(lineAlphaMax >= threshold)
        {
            rect->y2 = x;
            break;
        }
    }
}

// Find the 'real' bounding box
Rectangle findRealBoundingBox(Image* img, unsigned char threshold)
{
    Rectangle r = { 0, 0, img->width, img->height };
    shrinkHorizontally(img,threshold,&r);
    shrinkVertically(img,threshold,&r);
    return r;
}

Now that you have the coordinates of the bounding box in pixel in your image, you should be able to transform it in device coordinates.

Synxis
  • 9,236
  • 2
  • 42
  • 64
  • There's a possible (though uncommon) problem. `x` is `unsigned` in `shrinkHorizontally`, yet you're looping with `for(x = img->width - 1; x >= 0; x--)` to shrink from the right. Similar for `y` in `shrinkVertically`. There won't be undefined behavior since `getPixelAlpha` checks the bounds, but if threshold is too high (compared to the image), it will loop indefinitely. Also, you could speed up the search a little by not reconsidering already processed pixels (though the big-O performance won't change). – jerry Mar 26 '13 at 21:02
  • You mean, is the size of the image is (0,0) or (w,0) or (0,h) ? – Synxis Mar 27 '13 at 08:46
  • Well, that's another problem if `Image`s are allowed to have a dimension be `0`. What I meant, however, is if there wasn't a pixel whose alpha channel was greater than or equal to `threshold`. Say `threshold = 0xFF` but all pixels have `getPixelAlpha(img,x,y) <= 0xFE` (as I said, uncommon). Then `lineAlphaMax >= threshold` will never be true, so you'll never `break` out of the outer loop. Instead, the loop will run as expected until the loop iterator `x--` runs when `x` is `0` (for `shrinkHorizontally`). This will cause `x` to wrap to `UINT_MAX`, which is `>= 0`. The loop will run forever. – jerry Mar 27 '13 at 14:30
  • Sure, no problem. I just noticed you failed to shift `(pixel & 0xFF000000);` before casting it in `getPixelAlpha`. That will always return `0`. Also, I might be getting a little language lawyer-y here (don't worry, it's not about the type widths), but make sure `img->height` and `img->width` are `> 0` and `<= INT_MAX`. – jerry Mar 28 '13 at 15:34
  • Fixed. The solution is not meant to be exhaustive ;) – Synxis Mar 28 '13 at 17:21
0
CGRect myImageViewRect = [myImageView frame];
CGSize myImageSize = [[myImageView image]size];

if(myImageSize.width < myImageViewRect.size.width){
   NSLog(@"it's width smaller!");
}
if(myImageSize.height < myImageViewRect.size.height){
   NSLog(@"it's height smaller!");
}

If you want the image to resize to the size of the image view you can call

[myImageView sizeToFit];
christo16
  • 4,843
  • 5
  • 42
  • 53