Binarizar imagen en Android

Estoy desarrollando la aplicación android que utiliza tesseract OCR para escanear un texto de la imagen,
He oído que la imagen binarizing antes de realizar OCR en él dará mejor resultado,
Así que empiezo a buscar un código que haga la operación,

Encontré pocos, pero en realidad en java y necesita awt biblioteca … por lo que no funcionan en android.
Así que puedes ayudarme a encontrar uno.
gracias

Una solución simple

En lo que sigue, simplemente altero cada píxel en una imagen basada en la fórmula de distancia de 3 dimensiones-espacio normal. Decido si un píxel debe ser blanco o negro basado en lo lejos que está de cada uno de estos colores. Por ejemplo, (1,2,3) está más cerca de (0,0,0) de lo que es (255,255,255) y por lo tanto se decide ser negro. Estoy seguro de que hay algoritmos más inteligentes por ahí. Esto es solo uno simple

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> 

Tengo que hacer una tarea similar como parte de un proyecto para un asignment. He encontrado en mi espacio de trabajo este pedazo de código, creo que esto es lo que necesita:

 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); 

Aquí he utilizado ColorMatrix para generar una imagen en blanco y negro de un color uno. También encontré este pedazo de código que utilicé para convertir una imagen de color en una imagen de escala de grises:

 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); 

Espero que esto te ayude.

Puedes echar un vistazo al enfoque sencillo que Barcode Scanner utiliza en Android para convertir una imagen en luminancia y luego en blanco y negro. Probablemente funcionaría bien para 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

No sería difícil de portar esto desde java a 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; } } 

Tuve un proyecto similar que implicaba colores, aunque en otra plataforma.

Aunque podrían ser otros algoritmos mejores, utilicé una función (GetColorDistance) para calcular las distancias entre dos colores, en el espacio 3D RGB, usando el teorema de Pitágoras. GetNewColor calcula si el color a está más cerca de blanco o negro y, a continuación, devuelve un negro o blanco en consecuencia. Por último, la función GetBitmapBinary procesa los píxeles en el mapa de bits y los convierte en blanco y negro.

 private Bitmap GetBinaryBitmap(Bitmap bitmap_src) { Bitmap bitmap_new=bitmap_src.copy(bitmap_src.getConfig(), true); for(int x=0; x<bitmap_new.getWidth(); x++) { for(int y=0; y<bitmap_new.getHeight(); y++) { int color=bitmap_new.getPixel(x, y); color=GetNewColor(color); bitmap_new.setPixel(x, y, color); } } return bitmap_new; } private double GetColorDistance(int c1, int c2) { int db=Color.blue(c1)-Color.blue(c2); int dg=Color.green(c1)-Color.green(c2); int dr=Color.red(c1)-Color.red(c2); double d=Math.sqrt( Math.pow(db, 2) + Math.pow(dg, 2) +Math.pow(dr, 2) ); return d; } private int GetNewColor(int c) { double dwhite=GetColorDistance(c,Color.WHITE); double dblack=GetColorDistance(c,Color.BLACK); if(dwhite<=dblack) { return Color.WHITE; } else { return Color.BLACK; } } 

Puede modificar la función GetNewColor para obtener mejores resultados en diferentes densidades de luz. Por ejemplo, puede multiplicar dblack por 1,5 para que los píxeles más oscuros se vuelvan blancos, en un entorno oscuro.

Simple limpiar y primero convertir la imagen a escala de grises (si u no obtendrá un error de imagen de entrada) después de la conversión utilizar el método de umbral adaptativo para completar el código de tarea:

  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); 

Puede utilizar Catalano Framework, es simple y hay más de 60 filtros

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(); 
  • Warp Área de imagen en el toque de un área de punto?
  • Enderezamiento de la imagen en Android
  • Android - convertir el array byte rgb_565 en array argb o rgb
  • Cómo implementar efectos de distorsión de imagen en Android
  • Detección de datos periódicos desde el acelerómetro del teléfono
  • Android - ¿Es posible superponer texto en texto?
  • Reconocimiento de imágenes para la aplicación Android de realidad aumentada
  • Cómo implementar efectos de distorsión de imagen en Android
  • Rendimiento de procesamiento en android
  • Objeto de fecha SimpleDateFormat no analiza correctamente la cadena de hora en el entorno Java (Android)
  • Sugerencias para el reconocimiento de dígitos
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.