Binarize Image in Android

后端 未结 7 976
耶瑟儿~
耶瑟儿~ 2020-12-17 03:11

I am developing android app that uses tesseract OCR to scan a text from image,
I heard that binarizing image before performing OCR on it will give better result,
So

相关标签:
7条回答
  • 2020-12-17 03:52

    simple clean and first first convert image to grayscale(if u don't you'll get an input image error) after conversion use the adaptive threshold method to complete task code:

     Mat tmp = new Mat(bitmap.getWidth(), bitmap.getHeight(), CvType.CV_8UC1);
                    // Convert
                    Utils.bitmapToMat(bitmap, tmp);
    
                    Mat gray = new Mat(bitmap.getWidth(), bitmap.getHeight(),     CvType.CV_8UC1);
                    // Conver the color
                    Imgproc.cvtColor(tmp, gray, Imgproc.COLOR_RGB2GRAY);
                    // Convert back to bitmap
    
    
                    Mat destination = new Mat(gray.rows(),gray.cols(),gray.type());
    
                    Imgproc.adaptiveThreshold(gray, destination, 255,
                            Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY_INV, 15, 4);
    
                    Utils.matToBitmap(destination, bitmap);
                    imv_binary.setImageBitmap(bitmap);
    
    0 讨论(0)
  • 2020-12-17 03:58

    A Simple Solution

    In the following, I simply alter each pixel in an image based on the normal 3-dimension-space distance formula. I decide whether a pixel should be black or white based on how far it is from each of these colors. For example, (1,2,3) is closer to (0,0,0) than it is to (255,255,255) and therefore it is decided to be black. I'm sure there are more clever algorithms out there. This is just a simple one

    MainActivity.java

    package com.example.binarizeimage;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.nio.MappedByteBuffer;
    import java.nio.channels.FileChannel;
    import java.nio.channels.FileChannel.MapMode;
    
    import android.app.Activity;
    import android.graphics.Bitmap;
    import android.graphics.Bitmap.Config;
    import android.graphics.BitmapFactory;
    import android.graphics.Color;
    import android.os.Bundle;
    import android.os.Environment;
    import android.widget.ImageView;
    
    import com.example.binarizeimage.R.drawable;
    
    /**
     * @author Sherif elKhatib - shush
     *
     */
    public class MainActivity extends Activity {
        /**
         * Boolean that tells me how to treat a transparent pixel (Should it be black?)
         */
        private static final boolean TRASNPARENT_IS_BLACK = false;
        /**
         * This is a point that will break the space into Black or white
         * In real words, if the distance between WHITE and BLACK is D;
         * then we should be this percent far from WHITE to be in the black region.
         * Example: If this value is 0.5, the space is equally split.  
         */
        private static final double SPACE_BREAKING_POINT = 13.0/30.0;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //this is the original image
            Bitmap theOriginalImage = BitmapFactory.decodeResource(this.getResources(), drawable.ic_launcher);
            //this is the image that is binarized
            Bitmap binarizedImage = convertToMutable(theOriginalImage);
            // I will look at each pixel and use the function shouldBeBlack to decide 
            // whether to make it black or otherwise white
            for(int i=0;i<binarizedImage.getWidth();i++) {
                for(int c=0;c<binarizedImage.getHeight();c++) {
                    int pixel = binarizedImage.getPixel(i, c);
                    if(shouldBeBlack(pixel))
                        binarizedImage.setPixel(i, c, Color.BLACK);
                    else
                        binarizedImage.setPixel(i, c, Color.WHITE);
                }
            }
    
    
            ImageView iv = (ImageView) findViewById(R.id.imageView1);
            ImageView ivb = (ImageView) findViewById(R.id.ImageView01);
            //show the original image
            iv.setImageBitmap(BitmapFactory.decodeResource(this.getResources(), drawable.ic_launcher));
            //show the binarized image
            ivb.setImageBitmap(binarizedImage);
        }
        /**
         * @param pixel the pixel that we need to decide on
         * @return boolean indicating whether this pixel should be black
         */
        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 //this should not be computed and might be as well a function of distanceFromWhite and the whole distance
            double distanceFromBlack = Math.sqrt(Math.pow(0x00 - redValue, 2) + Math.pow(0x00 - blueValue, 2) + Math.pow(0x00 - greenValue, 2));
            // distance between the extremes //this is a constant that should not be computed :p
            double distance = distanceFromBlack + distanceFromWhite;
            // distance between the extremes
            return ((distanceFromWhite/distance)>SPACE_BREAKING_POINT);
        }
        /**
         * @author Derzu
         * 
         * @see http://stackoverflow.com/a/9194259/833622
         * 
         * Converts a immutable bitmap to a mutable bitmap. This operation doesn't allocates
         * more memory that there is already allocated.
         * 
         * @param imgIn - Source image. It will be released, and should not be used more
         * @return a copy of imgIn, but muttable.
         */
        public static Bitmap convertToMutable(Bitmap imgIn) {
            try {
                //this is the file going to use temporally to save the bytes. 
                // This file will not be a image, it will store the raw image data.
                File file = new File(Environment.getExternalStorageDirectory() + File.separator + "temp.tmp");
    
                //Open an RandomAccessFile
                //Make sure you have added uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                //into AndroidManifest.xml file
                RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
    
                // get the width and height of the source bitmap.
                int width = imgIn.getWidth();
                int height = imgIn.getHeight();
                Config type = imgIn.getConfig();
    
                //Copy the byte to the file
                //Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888;
                FileChannel channel = randomAccessFile.getChannel();
                MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, imgIn.getRowBytes()*height);
                imgIn.copyPixelsToBuffer(map);
                //recycle the source bitmap, this will be no longer used.
                imgIn.recycle();
                System.gc();// try to force the bytes from the imgIn to be released
    
                //Create a new bitmap to load the bitmap again. Probably the memory will be available. 
                imgIn = Bitmap.createBitmap(width, height, type);
                map.position(0);
                //load it back from temporary 
                imgIn.copyPixelsFromBuffer(map);
                //close the temporary file and channel , then delete that also
                channel.close();
                randomAccessFile.close();
    
                // delete the temp file
                file.delete();
    
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } 
    
            return imgIn;
        }
    }
    

    *activity_main.xml*

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context=".MainActivity" >
    
        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/hello_world" />
    
        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/textView2"
            android:layout_centerHorizontal="true"
            android:text="Original Image" />
    
        <ImageView
            android:id="@+id/imageView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/textView1"
            android:layout_centerHorizontal="true"
            android:src="@drawable/ic_launcher" />
    
        <TextView
            android:id="@+id/TextView02"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@+id/textView1"
            android:layout_below="@+id/imageView1"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="28dp"
            android:text="YES/NO Image" />
    
        <ImageView
            android:id="@+id/ImageView01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/TextView02"
            android:layout_centerHorizontal="true"
            android:src="@drawable/ic_launcher" />
    
    </RelativeLayout>
    
    0 讨论(0)
  • 2020-12-17 04:04

    You can use Catalano Framework, It's simple and there is more than 60 filters

    http://code.google.com/p/catalano-framework/

    FastBitmap fb = new FastBitmap(bitmap);
    
    Grayscale g = new Grayscale(fb);
    g.applyInPlace(fb);
    
    Threshold t = new Threshold(100);
    t.applyInPlace(fb);
    
    bitmap = fb.toBitmap();
    
    0 讨论(0)
  • 2020-12-17 04:07

    Wouldn't be to hard to port this from java to android:

    /**
     * Image binarization - Otsu algorithm
     *
     * Author: Bostjan Cigan (http://zerocool.is-a-geek.net)
     *
     */
    
    import java.awt.Color;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    import javax.imageio.ImageIO;
    
    public class OtsuBinarize {
    
        private static BufferedImage original, grayscale, binarized;
    
        public static void main(String[] args) throws IOException {
    
            File original_f = new File(args[0]+".jpg");
            String output_f = args[0]+"_bin";
            original = ImageIO.read(original_f);
            grayscale = toGray(original);
            binarized = binarize(grayscale);
            writeImage(output_f);         
    
        }
    
        private static void writeImage(String output) throws IOException {
            File file = new File(output+".jpg");
            ImageIO.write(binarized, "jpg", file);
        }
    
        // Return histogram of grayscale image
        public static int[] imageHistogram(BufferedImage input) {
    
            int[] histogram = new int[256];
    
            for(int i=0; i<histogram.length; i++) histogram[i] = 0;
    
            for(int i=0; i<input.getWidth(); i++) {
                for(int j=0; j<input.getHeight(); j++) {
                    int red = new Color(input.getRGB (i, j)).getRed();
                    histogram[red]++;
                }
            }
    
            return histogram;
    
        }
    
        // The luminance method
        private static BufferedImage toGray(BufferedImage original) {
    
            int alpha, red, green, blue;
            int newPixel;
    
            BufferedImage lum = new BufferedImage(original.getWidth(), original.getHeight(), original.getType());
    
            for(int i=0; i<original.getWidth(); i++) {
                for(int j=0; j<original.getHeight(); j++) {
    
                    // Get pixels by R, G, B
                    alpha = new Color(original.getRGB(i, j)).getAlpha();
                    red = new Color(original.getRGB(i, j)).getRed();
                    green = new Color(original.getRGB(i, j)).getGreen();
                    blue = new Color(original.getRGB(i, j)).getBlue();
    
                    red = (int) (0.21 * red + 0.71 * green + 0.07 * blue);
                    // Return back to original format
                    newPixel = colorToRGB(alpha, red, red, red);
    
                    // Write pixels into image
                    lum.setRGB(i, j, newPixel);
    
                }
            }
    
            return lum;
    
        }
    
        // Get binary treshold using Otsu's method
        private static int otsuTreshold(BufferedImage original) {
    
            int[] histogram = imageHistogram(original);
            int total = original.getHeight() * original.getWidth();
    
            float sum = 0;
            for(int i=0; i<256; i++) sum += i * histogram[i];
    
            float sumB = 0;
            int wB = 0;
            int wF = 0;
    
            float varMax = 0;
            int threshold = 0;
    
            for(int i=0 ; i<256 ; i++) {
                wB += histogram[i];
                if(wB == 0) continue;
                wF = total - wB;
    
                if(wF == 0) break;
    
                sumB += (float) (i * histogram[i]);
                float mB = sumB / wB;
                float mF = (sum - sumB) / wF;
    
                float varBetween = (float) wB * (float) wF * (mB - mF) * (mB - mF);
    
                if(varBetween > varMax) {
                    varMax = varBetween;
                    threshold = i;
                }
            }
    
            return threshold;
    
        }
    
        private static BufferedImage binarize(BufferedImage original) {
    
            int red;
            int newPixel;
    
            int threshold = otsuTreshold(original);
    
            BufferedImage binarized = new BufferedImage(original.getWidth(), original.getHeight(), original.getType());
    
            for(int i=0; i<original.getWidth(); i++) {
                for(int j=0; j<original.getHeight(); j++) {
    
                    // Get pixels
                    red = new Color(original.getRGB(i, j)).getRed();
                    int alpha = new Color(original.getRGB(i, j)).getAlpha();
                    if(red > threshold) {
                        newPixel = 255;
                    }
                    else {
                        newPixel = 0;
                    }
                    newPixel = colorToRGB(alpha, newPixel, newPixel, newPixel);
                    binarized.setRGB(i, j, newPixel); 
    
                }
            }
    
            return binarized;
    
        }
    
        // Convert R, G, B, Alpha to standard 8 bit
        private static int colorToRGB(int alpha, int red, int green, int blue) {
    
            int newPixel = 0;
            newPixel += alpha;
            newPixel = newPixel << 8;
            newPixel += red; newPixel = newPixel << 8;
            newPixel += green; newPixel = newPixel << 8;
            newPixel += blue;
    
            return newPixel;
    
        }
    
    }
    
    0 讨论(0)
  • 2020-12-17 04:09

    I have to do a similar task as part of a project for an asignment. I found in my workspace this piece of code, I think this is what you need:

    Bitmap img = BitmapFactory.decodeResource(this.getResources(), drawable.testimage);
    Paint paint = new Paint();
    
    ColorMatrix cm = new ColorMatrix();
    float a = 77f;
    float b = 151f;
    float c = 28f;
    float t = 120 * -256f;
    cm.set(new float[] { a, b, c, 0, t, a, b, c, 0, t, a, b, c, 0, t, 0, 0, 0, 1, 0 });
    paint.setColorFilter(new ColorMatrixColorFilter(cm));
    canvas.drawBitmap(img, 0, 0, paint);
    

    Here I used ColorMatrix to generate a black and white image from a color one. Also I found this piece of code that I used to convert a color image to a gray scale image:

    Bitmap result = Bitmap.createBitmap(destWidth, destHeight,Bitmap.Config.RGB_565);
    RectF destRect = new RectF(0, 0, destWidth, destHeight);
    Canvas canvas = new Canvas(result);
    Paint paint = new Paint();
    ColorMatrix colorMatrix = new ColorMatrix();
    colorMatrix.setSaturation(0);
    ColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
    paint.setColorFilter(filter);
    canvas.drawBitmap(bitmap, sourceRect, destRect, paint); 
    

    Hope this help you.

    0 讨论(0)
  • 2020-12-17 04:09

    You can take a look at the simple approach that Barcode Scanner uses on Android to convert an image to luminance and then to black and white. It would probably work well for OCR.

    https://code.google.com/p/zxing/source/browse/trunk/core/src/com/google/zxing/common/HybridBinarizer.java https://code.google.com/p/zxing/source/browse/trunk/core/src/com/google/zxing/PlanarYUVLuminanceSource.java

    0 讨论(0)
提交回复
热议问题