问题
I am using tess-two library and I wish to convert all the colors other than black in my image to white (Black will be text). Thus making it easier for the tess-two to read the text. I have tried various methods but they are taking too much time as they convert pixel by pixel. Is there a way to achieve this using canvas or anything that give results faster.
UPDATE
Another problem that came up with this algorithm is that printer doesn't print with the same BLACK and White as in android. So the algorithm converts the whole picture to white.
Pixel by pixel method that I am currently using.
binarizedImage = convertToMutable(cropped);// the bitmap is made mutable
int width = binarizedImage.getWidth();
int height = binarizedImage.getHeight();
int[] pixels = new int[width * height];
binarizedImage.getPixels(pixels, 0, width, 0, 0, width, height);
for(int i=0;i<binarizedImage.getWidth();i++) {
for(int c=0;c<binarizedImage.getHeight();c++) {
int pixel = binarizedImage.getPixel(i, c);
if(!(pixel == Color.BLACK || pixel == Color.WHITE))
{
int index = c * width + i;
pixels[index] = Color.WHITE;
binarizedImage.setPixels(pixels, 0, width, 0, 0, width, height);
}
}
}
回答1:
Per, Rishabh's comment. Use a color matrix. Since black is black and is RGB(0,0,0,255), it's immune to multiplications. So if you multiply everything by 255 in all channels everything will exceed the limit and get crimped to white, except for black which will stay black.
ColorMatrix bc = new ColorMatrix(new float[] {
255, 255, 255, 0, 0,
255, 255, 255, 0, 0,
255, 255, 255, 0, 0,
0, 0, 0, 1, 0,
});
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(bc);
paint.setColorFilter(filter);
You can use that paint to paint that bitmap in only-black-stays-black colormatrix filter glory.
Note: This is a quick and awesome trick, but, it will ONLY work for black. While it's perfect for your use and will turn that lengthy op into something that is instant, it does not actually conform to the title question of "a particular color" my algorithm works in any color you want, so long as it is black.
回答2:
Though @Tatarize answer was perfect I was having troubles reading a printed image as its not always jet black.
This algorithm which i found on stack overflow works great, it actually checks whether the particular pixel is closer to black or white and converts the pixel to the closest color. Hence providing binarization with range. (https://stackoverflow.com/a/16534187/3710223).
What I am doing now is keeping the unwanted areas in light colors while text in black. This algorithm gives binarized image in approximately 20-35 sec. Still not that fast but efficient.
private static boolean shouldBeBlack(int pixel) {
int alpha = Color.alpha(pixel);
int redValue = Color.red(pixel);
int blueValue = Color.blue(pixel);
int greenValue = Color.green(pixel);
if(alpha == 0x00) //if this pixel is transparent let me use TRASNPARENT_IS_BLACK
return TRASNPARENT_IS_BLACK;
// distance from the white extreme
double distanceFromWhite = Math.sqrt(Math.pow(0xff - redValue, 2) + Math.pow(0xff - blueValue, 2) + Math.pow(0xff - greenValue, 2));
// distance from the black extreme
double distanceFromBlack = Math.sqrt(Math.pow(0x00 - redValue, 2) + Math.pow(0x00 - blueValue, 2) + Math.pow(0x00 - greenValue, 2));
// distance between the extremes
double distance = distanceFromBlack + distanceFromWhite;
return ((distanceFromWhite/distance)>SPACE_BREAKING_POINT);
}
If the return value is true then we convert the pixel to black else we convert it to white.
I know there can be better/faster answers and more answers are welcomed :)
回答3:
Same thing but done in renderscript, times about 60-100ms. You won't even notice the delay.
Bitmap blackbitmap = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(),bitmap.getConfig());
RenderScript mRS = RenderScript.create(TouchEmbroidery.activity);
ScriptC_blackcheck script = new ScriptC_blackcheck(mRS);
Allocation allocationRaster0 = Allocation.createFromBitmap(
mRS,
bitmap,
Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT
);
Allocation allocationRaster1 = Allocation.createTyped(mRS, allocationRaster0.getType());
script.forEach_root(allocationRaster0, allocationRaster1);
allocationRaster1.copyTo(blackbitmap);
Does the allocation, uses renderscript to write out the data to blackbitmap.
#pragma version(1)
#pragma rs java_package_name(<YOUR PACKAGENAME GOES HERE>)
void root(const uchar4 *v_in, uchar4 *v_out) {
uint32_t value = (v_in->r * v_in->r);
value = value + (v_in->g * v_in->g);
value = value + (v_in->b * v_in->b);
if (value > 1200) {
v_out->r = 255;
v_out->g = 255;
v_out->b = 255;
}
else {
v_out->r = 0;
v_out->g = 0;
v_out->b = 0;
}
v_out->a = 0xFF;
}
Note the 1200 is just the threshold I used, should be all three components less than 20 (or, like 0, 0, sqrt(1200) aka (~34)). You can set the 1200 limit up or down accordingly.
And the build gradle needs Renderscript:
renderscriptTargetApi 22
Last few things of the build tools claims to have fixed a bunch of the renderscript headaches. So it might be perfectly reasonable to do this kind of stuff in mission critical places like yours. 20 seconds is too long to wait, 60 milliseconds is not.
来源:https://stackoverflow.com/questions/36474343/convert-all-colors-other-than-a-particular-color-in-a-bitmap-to-white