Black and white image of a real-time Android threshold

I have a code that turns a bitmap with gray colors into a bitmap in black and white using this code:

// scan through all pixels for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) { // get pixel color pixel = bitmap.getPixel(x, y); A = Color.alpha(pixel); R = Color.red(pixel); G = Color.green(pixel); B = Color.blue(pixel); int gray = (int) (0.2989 * R + 0.5870 * G + 0.1140 * B); // use 128 as threshold, above -> white, below -> black if (gray > 128) gray = 255; else gray = 0; // set new pixel color to output bitmap bmOut.setPixel(x, y, Color.argb(A, gray, gray, gray)); } } 

As you can see, I move all the pixel points of the original bitmap, and then compare the color components with the given threshold, in this case 128, and then if I say it above it is white, otherwise it will be a black pixel.

What I want to do now is Spinner, which can change this threshold value, and then the BW image will be different.

To do this, I will need to draw the whole image again, and this is a very expensive processor time, it will take time to move all the pixels again.

Is there a way to change the image using a different BW threshold in real time?

Someone told me to use GIF, and then, what would I do, just change the values ​​of the lookup table in GIF, does anyone know about this on Android?

+6
source share
3 answers

It has been a little time since this question was asked, but I came across this in search of something else and, as it turned out, got a solution. You can achieve this without OpenCV or any other third-party library using only the ColorMatrixColorFilter , accessible from API level 1.

Here are the matrices you can use:

 //matrix that changes picture into gray scale public static ColorMatrix createGreyMatrix() { ColorMatrix matrix = new ColorMatrix(new float[] { 0.2989f, 0.5870f, 0.1140f, 0, 0, 0.2989f, 0.5870f, 0.1140f, 0, 0, 0.2989f, 0.5870f, 0.1140f, 0, 0, 0, 0, 0, 1, 0 }); return matrix; } // matrix that changes gray scale picture into black and white at given threshold. // It works this way: // The matrix after multiplying returns negative values for colors darker than threshold // and values bigger than 255 for the ones higher. // Because the final result is always trimed to bounds (0..255) it will result in bitmap made of black and white pixels only public static ColorMatrix createThresholdMatrix(int threshold) { ColorMatrix matrix = new ColorMatrix(new float[] { 85.f, 85.f, 85.f, 0.f, -255.f * threshold, 85.f, 85.f, 85.f, 0.f, -255.f * threshold, 85.f, 85.f, 85.f, 0.f, -255.f * threshold, 0f, 0f, 0f, 1f, 0f }); return matrix; } 

And here is how to use them:

 BitmapFactory.Options options = new BitmapFactory.Options(); options.inScaled = false; //load source bitmap and prepare destination bitmap Bitmap pic = BitmapFactory.decodeResource(getResources(), R.drawable.thePicture, options); Bitmap result = Bitmap.createBitmap(pic.getWidth(), pic.getHeight(), Bitmap.Config.ARGB_8888); Canvas c = new Canvas(result); //first convert bitmap to grey scale: bitmapPaint.setColorFilter(new ColorMatrixColorFilter(createGreyMatrix())); c.drawBitmap(pic, 0, 0, bitmapPaint); //then convert the resulting bitmap to black and white using threshold matrix bitmapPaint.setColorFilter(new ColorMatrixColorFilter(createThresholdMatrix(128))); c.drawBitmap(result, 0, 0, bitmapPaint); //voilΓ ! You can now draw the result bitmap anywhere You want: bitmapPaint.setColorFilter(null); otherCanvas.drawBitmap(result, null, new Rect(x, y, x + size, y + size), bitmapPaint); 

Hope this helps someone.

+4
source

I recommend that you familiarize yourself with the OpenCV library for Android. Once you have installed it, you can use it for a quick threshold among many other commonly used image operations. The following code snippet converts your color cue ball into a gray image and a gray level threshold of 128.

 // first convert bitmap into OpenCV mat object Mat imageMat = new Mat (bitmap.getHeight(), bitmap.getWidth(), CvType.CV_8U, new Scalar(4)); Bitmap myBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true); Utils.bitmapToMat(myBitmap, imageMat); // now convert to gray Mat grayMat = new Mat ( bitmap.getHeight(), bitmap.getWidth(), CvType.CV_8U, new Scalar(1)); Imgproc.cvtColor(imageMat, grayMat, Imgproc.COLOR_RGB2GRAY, 1); // get the thresholded image Mat thresholdMat = new Mat ( bitmap.getHeight(), bitmap.getWidth(), CvType.CV_8U, new Scalar(1)); Imgproc.threshold(grayMat, thresholdMat , 128, 255, Imgproc.THRESH_BINARY); // convert back to bitmap for displaying Bitmap resultBitmap = Bitmap.createBitmap(bitmap.cols(), bitmap.rows(), Bitmap.Config.ARGB_8888); thresholdMat.convertTo(thresholdMat, CvType.CV_8UC1); Utils.matToBitmap(thresholdMat, resultBitmap); 
+3
source

GIFs have a color palette in the header that binds for each β€œvalue” the color refers to ( Wikipedia) . Catch: back that day when they thought that 256 colors would be enough, and that there were as many palettes as there were. You can use the GifDecoder.java class with a local color table of 256 entries and two values: your two outputs are 0 and 255. But at the end of the day, there is a loop that checks every byte, the value of which is linked using a palette search ( this is a string ). Not much faster than your code.

An alternative to making this faster would be to use OpenGL to create your threshold (given that you are going to draw a bitmap on the screen anyway, right?). This requires a bunch of additional code, fortunately this tutorial does exactly what you want to do, minus the threshold.

For a threshold value, the OpenGL program must be compiled (on GlRenderer::onSurfaceChanged ) and used every time (in GlRenderer::onDrawFrame ). The program receives two shaders that will spawn each pixel at the speed of light! (OK, maybe a little slower, but very fast!).

Shaders will look as follows. Let me know if I can help you connect them to example code.

 private static final String mVertexShader = "attribute vec4 aPosition;\n" + "attribute vec4 aTextureCoord;\n" + "varying vec2 vTextureCoord;\n" + "void main() {\n" + " gl_Position = aPosition;\n" + " vTextureCoord = aTextureCoord;\n" + "}\n"; private static final String mFragmentShader = "precision mediump float;\n" + "varying vec2 vTextureCoord;\n" + "uniform float threshold_in_range_0_to_1;\n" + "const vec4 coeff_y = vec4(0.256, 0.504, 0.097, 0.0625);\n" + "float y;\n" + "void main() {\n" + " y = dot(coeff_y, texture2D(sTexture, vTextureCoord));\n" + " if ( y > threshold_in_range_0_to_1)" + " gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0)\n" + " else" + " gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0)\n" + "}\n"; 
+1
source

All Articles