我正在开发使用 tesseract OCR 从图像中扫描文本的 android 应用程序,
我听说在对其执行 OCR 之前对图像进行二值化会产生更好的结果,
所以我开始寻找执行该操作的代码,
我发现很少,但它实际上在 java 中并且需要 awt 库......所以它们不能在 android 上工作。
那你能不能帮我找一个。
谢谢你
我正在开发使用 tesseract OCR 从图像中扫描文本的 android 应用程序,
我听说在对其执行 OCR 之前对图像进行二值化会产生更好的结果,
所以我开始寻找执行该操作的代码,
我发现很少,但它实际上在 java 中并且需要 awt 库......所以它们不能在 android 上工作。
那你能不能帮我找一个。
谢谢你
作为分配项目的一部分,我必须执行类似的任务。我在我的工作区中找到了这段代码,我认为这就是您所需要的:
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);
在这里,我使用 ColorMatrix 从彩色图像生成黑白图像。我还发现了这段用于将彩色图像转换为灰度图像的代码:
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);
希望这对您有所帮助。
一个简单的解决方案
在下文中,我将根据正常的 3 维空间距离公式简单地更改图像中的每个像素。我根据像素与这些颜色中的每一种的距离来决定像素应该是黑色还是白色。例如,(1,2,3) 比 (255,255,255) 更接近 (0,0,0),因此它被判定为黑色。我敢肯定那里有更聪明的算法。这只是一个简单的
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>
您可以查看 Barcode Scanner 在 Android 上使用的简单方法,将图像转换为亮度,然后转换为黑白。它可能适用于 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
我有一个类似的涉及颜色的项目,虽然在另一个平台上。
尽管它们可能是其他更好的算法,但我使用了一个函数 (GetColorDistance) 来通过勾股定理计算 3D RGB 空间中两种颜色之间的距离。GetNewColor 计算颜色是否更接近白色或黑色,然后相应地返回黑色或白色。最后,GetBitmapBinary 函数处理位图上的像素并将它们转换为黑白。
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;
}
}
您可以修改 GetNewColor 函数以在不同的光密度下获得更好的结果。例如,您可以将 dblack 乘以 1.5,以便在黑暗环境中较暗的像素可以变为白色。
将它从 java 移植到 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;
}
}
你可以使用 Catalano 框架,它很简单,有 60 多个过滤器
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();
简单干净,首先将图像转换为灰度(如果你不这样做,你会得到输入图像错误)转换后使用自适应阈值方法完成任务代码:
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);