Flo Flo - 22 days ago 12
Android Question

How to remove a border with an unknown width from an image

I'm trying to build a program which can remove an single-colored border form an image.

The border is always white but the width of the border on the left and right side might differ from the width of the border at the top and bottom. So the image I want to extract is centered within the source image.

So from the following image I want to extract the green rectangle.

enter image description here

At the moment I don't know how to start solving this problem.

UPDATE

So finally calsign's code snippet and some improvements on it, solves my problem. I realized that the border around the inner image may not be completely single colored but can vary slightly. This leads to the behavior that for some images were left with a small border.

I solved this problem by improving the comparison of the color of two pixels by comparing the color distance of the two colors with a threshold. When the distance is below the threshold then the colors are handled as equally.

public Bitmap cropBorderFromBitmap(Bitmap bmp) {
//Convenience variables
int width = bmp.getWidth();
int height = bmp.getHeight();

int[] pixels = new int[height * width];

//Load the pixel data into the pixels array
bmp.getPixels(pixels, 0, width, 0, 0, width, height);

int length = pixels.length;

int borderColor = pixels[0];

//Locate the start of the border
int borderStart = 0;
for(int i = 0; i < length; i ++) {

// 1. Compare the color of two pixels whether they differ
// 2. Check whether the difference is significant
if(pixels[i] != borderColor && !sameColor(borderColor, pixels[i])) {
Log.i(TAG,"Current Color: " + pixels[i]);
borderStart = i;
break;
}
}

//Locate the end of the border
int borderEnd = 0;
for(int i = length - 1; i >= 0; i --) {
if(pixels[i] != borderColor && !sameColor(borderColor, pixels[i])) {
Log.i(TAG,"Current Color: " + pixels[i]);
borderEnd = length - i;
break;
}
}

//Calculate the margins
int leftMargin = borderStart % width;
int rightMargin = borderEnd % width;
int topMargin = borderStart / width;
int bottomMargin = borderEnd / width;

//Create the new, cropped version of the Bitmap
bmp = Bitmap.createBitmap(bmp, leftMargin, topMargin, width - leftMargin - rightMargin, height - topMargin - bottomMargin);
return bmp;
}

private boolean sameColor(int color1, int color2){
// Split colors into RGB values
long r1 = (color1)&0xFF;
long g1 = (color1 >>8)&0xFF;
long b1 = (color1 >>16)&0xFF;

long r2 = (color2)&0xFF;
long g2 = (color2 >>8)&0xFF;
long b2 = (color2 >>16)&0xFF;

long dist = (r2 - r1) * (r2 - r1) + (g2 - g1) * (g2 - g1) + (b2 - b1) *(b2 - b1);

// Check vs. threshold
return dist < 200;
}

Answer

Perhaps not the best use of the APIs to find a solution, but the one that came to mind: directly modify the image's pixels.

You can get a Bitmap's pixels with getPixels() and then create a new, cropped Bitmap with createBitmap(). Then, it's just a matter of finding the dimensions of the border.

You can find the color of the border by accessing the pixel located at position 0, and then compare that value (an int) to the value of each proceeding pixel until your reach the border (the pixel that isn't that color). With a little bit of math, it can be done.

Here is some simple code that demonstrates the point:

private void cropBorderFromBitmap(Bitmap bmp) {
    int[] pixels;
    //Load the pixel data into the pixels array
    bmp.getPixels(pixels, 0, width, 0, 0, width, height);

    //Convenience variables
    int width = bmp.getWidth();
    int height = bmp.getHeight();
    int length = pixels.length;

    int borderColor = pixels[0];

    //Locate the start of the border
    int borderStart;
    for(int i = 0; i < length; i ++) {
        if(pixels[i] != borderColor) {
            borderStart = i;
            break;
        }
    }

    //Locate the end of the border
    int borderEnd;
    for(int i = length - 1; i >= 0; i --) {
        if(pixels[i] != borderColor) {
            borderEnd = length - i;
            break;
        }
    }

    //Calculate the margins
    int leftMargin = borderStart % width;
    int rightMargin = borderEnd % width;
    int topMargin = borderStart / width;
    int bottomMargin = borderEnd / width;

    //Create the new, cropped version of the Bitmap
    bmp = createBitmap(bmp, leftMargin, topMargin, width - leftMargin - rightMargin, height - topMargin - bottomMargin);
}

This is untested and lacks error checking (e.g., what if the width is 0?), but it should serve as a proof-of-concept.

EDIT: I just realized that I failed to complete the getPixels() method. The wonders of testing your code... it's fixed now.

Comments