Android Studio中OpenCV应用详解:图像处理、颜色对比与OCR识别
文章目录
- 一、OpenCV在Android中的集成与配置
- 1.1 OpenCV简介
- 1.2 在Android Studio中集成OpenCV
- 1.2.1 通过Gradle依赖集成
- 1.2.2 通过模块方式集成
- 1.2.3 初始化OpenCV
- 1.3 OpenCV基础类介绍
- 二、指定区域图像抓取与对比
- 2.1 图像抓取基础
- 2.2 指定区域图像抓取实现
- 2.2.1 从Bitmap中截取指定区域
- 2.2.2 使用OpenCV截取指定区域
- 2.3 图像对比技术
- 2.3.1 均方误差(MSE)对比
- 2.3.2 结构相似性(SSIM)对比
- 2.3.3 特征点匹配对比
- 2.4 图像对比应用实例
- 2.4.1 图像相似度检测
- 2.4.2 图像差异可视化
- 三、指定点颜色提取与对比
- 3.1 颜色空间基础
- 3.2 指定点颜色提取
- 3.2.1 从Bitmap中获取像素颜色
- 3.2.2 使用OpenCV获取像素颜色
- 3.3 颜色空间转换
- 3.4 颜色对比技术
- 3.4.1 欧氏距离颜色对比
- 3.4.2 CIEDE2000颜色差异算法
- 3.4.3 颜色相似度判断
- 3.5 颜色对比应用实例
- 3.5.1 屏幕取色器实现
- 3.5.2 颜色匹配检测
- 四、指定区域OCR内容提取
- 4.1 OCR技术简介
- 4.2 Tesseract OCR集成
- 4.2.1 添加Tesseract依赖
- 4.2.2 初始化Tesseract
- 4.3 图像预处理优化OCR结果
- 4.3.1 基本预处理流程
- 4.3.2 高级预处理技术
- 4.4 指定区域OCR实现
- 4.4.1 从指定区域提取文本
一、OpenCV在Android中的集成与配置
1.1 OpenCV简介
OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库,包含超过2500种优化算法,涵盖图像处理、模式识别、机器学习等众多领域。在Android平台上,OpenCV为开发者提供了强大的图像处理能力,可以用于实现各种复杂的计算机视觉应用。
1.2 在Android Studio中集成OpenCV
1.2.1 通过Gradle依赖集成
最简单的方式是通过Gradle添加OpenCV依赖:
dependencies {implementation 'org.opencv:opencv-android:4.5.5'
}
1.2.2 通过模块方式集成
更灵活的方式是下载OpenCV Android SDK并将其作为模块导入:
- 从OpenCV官网下载Android SDK
- 在Android Studio中选择File -> New -> Import Module
- 选择OpenCV SDK中的java文件夹
- 修改模块的build.gradle文件,确保与主项目兼容
1.2.3 初始化OpenCV
在应用启动时需要初始化OpenCV库:
public class MainActivity extends AppCompatActivity {private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {@Overridepublic void onManagerConnected(int status) {if (status == LoaderCallbackInterface.SUCCESS) {Log.i("OpenCV", "OpenCV loaded successfully");} else {super.onManagerConnected(status);}}};@Overrideprotected void onResume() {super.onResume();if (!OpenCVLoader.initDebug()) {OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);} else {mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);}}
}
1.3 OpenCV基础类介绍
在Android中使用OpenCV主要涉及以下几个核心类:
- Mat:OpenCV的基础矩阵类,用于存储图像数据
- Bitmap:Android的位图类,需要与Mat相互转换
- Imgproc:图像处理类,包含各种图像处理算法
- Core:核心功能类,提供基本数学运算和矩阵操作
- Feature2d:特征检测与描述类
二、指定区域图像抓取与对比
2.1 图像抓取基础
在Android中获取图像主要有以下几种方式:
- 从相机捕获
- 从图库选择
- 从资源文件加载
- 从网络下载
2.2 指定区域图像抓取实现
2.2.1 从Bitmap中截取指定区域
public static Bitmap cropBitmap(Bitmap srcBitmap, Rect region) {if (srcBitmap == null || region == null) return null;try {// 确保区域在图像范围内int x = Math.max(0, region.left);int y = Math.max(0, region.top);int width = Math.min(srcBitmap.getWidth() - x, region.width());int height = Math.min(srcBitmap.getHeight() - y, region.height());return Bitmap.createBitmap(srcBitmap, x, y, width, height);} catch (Exception e) {e.printStackTrace();return null;}
}
2.2.2 使用OpenCV截取指定区域
public static Mat cropImage(Mat srcMat, Rect roi) {if (srcMat.empty() || roi == null) return null;try {// 确保ROI在图像范围内Rect adjustedRoi = new Rect(Math.max(0, roi.x),Math.max(0, roi.y),Math.min(srcMat.cols() - roi.x, roi.width),Math.min(srcMat.rows() - roi.y, roi.height));return new Mat(srcMat, adjustedRoi);} catch (Exception e) {e.printStackTrace();return new Mat();}
}
2.3 图像对比技术
2.3.1 均方误差(MSE)对比
public static double compareImagesMSE(Mat img1, Mat img2) {if (img1.rows() != img2.rows() || img1.cols() != img2.cols()) {throw new IllegalArgumentException("Images must have same dimensions");}Mat diff = new Mat();Core.absdiff(img1, img2, diff);diff.convertTo(diff, CvType.CV_32F);diff = diff.mul(diff);Scalar mse = Core.mean(diff);return (mse.val[0] + mse.val[1] + mse.val[2]) / 3;
}
2.3.2 结构相似性(SSIM)对比
public static double compareImagesSSIM(Mat img1, Mat img2) {// 转换为灰度图像Mat gray1 = new Mat();Mat gray2 = new Mat();Imgproc.cvtColor(img1, gray1, Imgproc.COLOR_BGR2GRAY);Imgproc.cvtColor(img2, gray2, Imgproc.COLOR_BGR2GRAY);// 参数设置final double C1 = 6.5025, C2 = 58.5225;int d = CvType.CV_32F;Mat I1 = new Mat(), I2 = new Mat();gray1.convertTo(I1, d);gray2.convertTo(I2, d);Mat I1_2 = I1.mul(I1);Mat I2_2 = I2.mul(I2);Mat I1_I2 = I1.mul(I2);// 计算均值Mat mu1 = new Mat(), mu2 = new Mat();Imgproc.GaussianBlur(I1, mu1, new Size(11, 11), 1.5);Imgproc.GaussianBlur(I2, mu2, new Size(11, 11), 1.5);Mat mu1_2 = mu1.mul(mu1);Mat mu2_2 = mu2.mul(mu2);Mat mu1_mu2 = mu1.mul(mu2);// 计算方差Mat sigma1_2 = new Mat(), sigma2_2 = new Mat(), sigma12 = new Mat();Imgproc.GaussianBlur(I1_2, sigma1_2, new Size(11, 11), 1.5);Core.subtract(sigma1_2, mu1_2, sigma1_2);Imgproc.GaussianBlur(I2_2, sigma2_2, new Size(11, 11), 1.5);Core.subtract(sigma2_2, mu2_2, sigma2_2);Imgproc.GaussianBlur(I1_I2, sigma12, new Size(11, 11), 1.5);Core.subtract(sigma12, mu1_mu2, sigma12);// 计算SSIMMat t1 = new Mat(), t2 = new Mat(), t3 = new Mat();Core.multiply(mu1_mu2, new Scalar(2), t1);Core.add(t1, new Scalar(C1), t1);Core.multiply(sigma12, new Scalar(2), t2);Core.add(t2, new Scalar(C2), t2);Core.add(mu1_2, mu2_2, t3);Core.add(t3, new Scalar(C1), t3);Mat ssim_map = new Mat();Core.multiply(t1, t2, ssim_map);Core.divide(ssim_map, t3, ssim_map);Scalar mssim = Core.mean(ssim_map);return mssim.val[0];
}
2.3.3 特征点匹配对比
public static double compareImagesFeatureMatching(Mat img1, Mat img2) {// 转换为灰度图像Mat gray1 = new Mat();Mat gray2 = new Mat();Imgproc.cvtColor(img1, gray1, Imgproc.COLOR_BGR2GRAY);Imgproc.cvtColor(img2, gray2, Imgproc.COLOR_BGR2GRAY);// 检测ORB特征点ORB orb = ORB.create();MatOfKeyPoint keypoints1 = new MatOfKeyPoint();MatOfKeyPoint keypoints2 = new MatOfKeyPoint();Mat descriptors1 = new Mat();Mat descriptors2 = new Mat();orb.detectAndCompute(gray1, new Mat(), keypoints1, descriptors1);orb.detectAndCompute(gray2, new Mat(), keypoints2, descriptors2);// 使用BFMatcher进行匹配BFMatcher matcher = BFMatcher.create(BFMatcher.BRUTEFORCE_HAMMING);MatOfDMatch matches = new MatOfDMatch();matcher.match(descriptors1, descriptors2, matches);// 计算匹配质量List<DMatch> matchesList = matches.toList();double maxDist = 0;double minDist = 100;for (DMatch match : matchesList) {double dist = match.distance;if (dist < minDist) minDist = dist;if (dist > maxDist) maxDist = dist;}// 筛选好的匹配点List<DMatch> goodMatches = new ArrayList<>();for (DMatch match : matchesList) {if (match.distance <= Math.max(2 * minDist, 30.0)) {goodMatches.add(match);}}// 计算匹配率double matchRatio = (double)goodMatches.size() / matchesList.size();return matchRatio;
}
2.4 图像对比应用实例
2.4.1 图像相似度检测
public class ImageComparator {private static final double MSE_THRESHOLD = 1000;private static final double SSIM_THRESHOLD = 0.8;private static final double FEATURE_MATCH_THRESHOLD = 0.5;public enum ComparisonResult {VERY_SIMILAR,SIMILAR,DIFFERENT,INVALID}public static ComparisonResult compareImages(Bitmap bmp1, Bitmap bmp2, Rect roi1, Rect roi2) {try {// 转换Bitmap为MatMat mat1 = new Mat();Mat mat2 = new Mat();Utils.bitmapToMat(bmp1, mat1);Utils.bitmapToMat(bmp2, mat2);// 裁剪指定区域Mat cropped1 = cropImage(mat1, roi1);Mat cropped2 = cropImage(mat2, roi2);if (cropped1.empty() || cropped2.empty()) {return ComparisonResult.INVALID;}// 调整大小一致if (cropped1.size().width != cropped2.size().width || cropped1.size().height != cropped2.size().height) {Imgproc.resize(cropped2, cropped2, cropped1.size());}// 计算各种相似度指标double mse = compareImagesMSE(cropped1, cropped2);double ssim = compareImagesSSIM(cropped1, cropped2);double featureMatch = compareImagesFeatureMatching(cropped1, cropped2);// 综合判断if (mse < MSE_THRESHOLD && ssim > SSIM_THRESHOLD && featureMatch > FEATURE_MATCH_THRESHOLD) {return ComparisonResult.VERY_SIMILAR;} else if (ssim > SSIM_THRESHOLD || featureMatch > FEATURE_MATCH_THRESHOLD) {return ComparisonResult.SIMILAR;} else {return ComparisonResult.DIFFERENT;}} catch (Exception e) {e.printStackTrace();return ComparisonResult.INVALID;}}
}
2.4.2 图像差异可视化
public static Bitmap visualizeImageDifference(Bitmap bmp1, Bitmap bmp2) {try {// 转换Bitmap为MatMat mat1 = new Mat();Mat mat2 = new Mat();Utils.bitmapToMat(bmp1, mat1);Utils.bitmapToMat(bmp2, mat2);// 确保图像大小一致if (mat1.size().width != mat2.size().width || mat1.size().height != mat2.size().height) {Imgproc.resize(mat2, mat2, mat1.size());}// 计算差异Mat diff = new Mat();Core.absdiff(mat1, mat2, diff);// 增强差异可视化Core.normalize(diff, diff, 0, 255, Core.NORM_MINMAX);Imgproc.cvtColor(diff, diff, Imgproc.COLOR_BGR2RGB);// 转换回BitmapBitmap result = Bitmap.createBitmap(diff.cols(), diff.rows(), Bitmap.Config.ARGB_8888);Utils.matToBitmap(diff, result);return result;} catch (Exception e) {e.printStackTrace();return null;}
}
三、指定点颜色提取与对比
3.1 颜色空间基础
OpenCV支持多种颜色空间,常用的有:
- RGB/BGR:默认颜色空间
- HSV/HSL:色调、饱和度、亮度/明度
- Lab:感知均匀的颜色空间
- YCrCb:亮度和色度分量
- Grayscale:灰度图像
3.2 指定点颜色提取
3.2.1 从Bitmap中获取像素颜色
public static int getPixelColor(Bitmap bitmap, int x, int y) {if (bitmap == null || x < 0 || y < 0 || x >= bitmap.getWidth() || y >= bitmap.getHeight()) {return 0;}return bitmap.getPixel(x, y);
}public static int[] getPixelColorARGB(Bitmap bitmap, int x, int y) {int pixel = getPixelColor(bitmap, x, y);return new int[] {(pixel >> 24) & 0xff, // Alpha(pixel >> 16) & 0xff, // Red(pixel >> 8) & 0xff, // Greenpixel & 0xff // Blue};
}
3.2.2 使用OpenCV获取像素颜色
public static double[] getPixelColor(Mat mat, int x, int y) {if (mat.empty() || x < 0 || y < 0 || x >= mat.cols() || y >= mat.rows()) {return new double[]{0, 0, 0};}double[] pixel = mat.get(y, x); // OpenCV中使用(row, col)顺序return pixel;
}public static Scalar getPixelColorScalar(Mat mat, int x, int y) {double[] pixel = getPixelColor(mat, x, y);if (pixel == null || pixel.length < 3) {return new Scalar(0, 0, 0);}return new Scalar(pixel[0], pixel[1], pixel[2]);
}
3.3 颜色空间转换
public static Mat convertColorSpace(Mat src, int conversionCode) {if (src.empty()) return new Mat();Mat dst = new Mat();try {Imgproc.cvtColor(src, dst, conversionCode);} catch (Exception e) {e.printStackTrace();}return dst;
}// 常用颜色空间转换代码
public static final int COLOR_BGR2RGB = Imgproc.COLOR_BGR2RGB;
public static final int COLOR_BGR2HSV = Imgproc.COLOR_BGR2HSV;
public static final int COLOR_BGR2Lab = Imgproc.COLOR_BGR2Lab;
public static final int COLOR_BGR2YCrCb = Imgproc.COLOR_BGR2YCrCb;
public static final int COLOR_BGR2GRAY = Imgproc.COLOR_BGR2GRAY;
3.4 颜色对比技术
3.4.1 欧氏距离颜色对比
public static double colorDistanceEuclidean(Scalar color1, Scalar color2) {double diffR = color1.val[0] - color2.val[0];double diffG = color1.val[1] - color2.val[1];double diffB = color1.val[2] - color2.val[2];return Math.sqrt(diffR * diffR + diffG * diffG + diffB * diffB);
}
3.4.2 CIEDE2000颜色差异算法
// 需要实现Lab颜色空间的Delta E计算
public static double colorDistanceCIEDE2000(Scalar lab1, Scalar lab2) {// 简化的Delta E 2000计算double L1 = lab1.val[0];double a1 = lab1.val[1];double b1 = lab1.val[2];double L2 = lab2.val[0];double a2 = lab2.val[1];double b2 = lab2.val[2];double deltaL = L2 - L1;double meanL = (L1 + L2) / 2;double C1 = Math.sqrt(a1 * a1 + b1 * b1);double C2 = Math.sqrt(a2 * a2 + b2 * b2);double meanC = (C1 + C2) / 2;double G = 0.5 * (1 - Math.sqrt(Math.pow(meanC, 7) / (Math.pow(meanC, 7) + Math.pow(25, 7))));double a1Prime = a1 * (1 + G);double a2Prime = a2 * (1 + G);double C1Prime = Math.sqrt(a1Prime * a1Prime + b1 * b1);double C2Prime = Math.sqrt(a2Prime * a2Prime + b2 * b2);double meanCPrime = (C1Prime + C2Prime) / 2;double h1Prime = Math.toDegrees(Math.atan2(b1, a1Prime));if (h1Prime < 0) h1Prime += 360;double h2Prime = Math.toDegrees(Math.atan2(b2, a2Prime));if (h2Prime < 0) h2Prime += 360;double deltaHPrime;if (Math.abs(h1Prime - h2Prime) <= 180) {deltaHPrime = h2Prime - h1Prime;} else if (h2Prime <= h1Prime) {deltaHPrime = h2Prime - h1Prime + 360;} else {deltaHPrime = h2Prime - h1Prime - 360;}double deltaH = 2 * Math.sqrt(C1Prime * C2Prime) * Math.sin(Math.toRadians(deltaHPrime / 2));double meanHPrime;if (Math.abs(h1Prime - h2Prime) <= 180) {meanHPrime = (h1Prime + h2Prime) / 2;} else if (h1Prime + h2Prime < 360) {meanHPrime = (h1Prime + h2Prime + 360) / 2;} else {meanHPrime = (h1Prime + h2Prime - 360) / 2;}double T = 1 - 0.17 * Math.cos(Math.toRadians(meanHPrime - 30))+ 0.24 * Math.cos(Math.toRadians(2 * meanHPrime))+ 0.32 * Math.cos(Math.toRadians(3 * meanHPrime + 6))- 0.20 * Math.cos(Math.toRadians(4 * meanHPrime - 63));double SL = 1 + (0.015 * Math.pow(meanL - 50, 2)) / Math.sqrt(20 + Math.pow(meanL - 50, 2));double SC = 1 + 0.045 * meanCPrime;double SH = 1 + 0.015 * meanCPrime * T;double RT = -2 * Math.sqrt(Math.pow(meanCPrime, 7) / (Math.pow(meanCPrime, 7) + Math.pow(25, 7)))* Math.sin(Math.toRadians(60 * Math.exp(-Math.pow((meanHPrime - 275) / 25, 2))));double deltaE = Math.sqrt(Math.pow(deltaL / SL, 2)+ Math.pow(deltaHPrime / SC, 2)+ Math.pow(deltaH / SH, 2)+ RT * (deltaCPrime / SC) * (deltaH / SH));return deltaE;
}
3.4.3 颜色相似度判断
public static boolean areColorsSimilar(Scalar color1, Scalar color2, double threshold, int colorSpace) {// 转换为指定颜色空间Mat mat1 = new Mat(1, 1, CvType.CV_8UC3, color1);Mat mat2 = new Mat(1, 1, CvType.CV_8UC3, color2);if (colorSpace != Imgproc.COLOR_BGR2RGB) {Imgproc.cvtColor(mat1, mat1, colorSpace);Imgproc.cvtColor(mat2, mat2, colorSpace);}Scalar c1 = new Scalar(mat1.get(0, 0));Scalar c2 = new Scalar(mat2.get(0, 0));// 根据颜色空间选择合适的比较方法if (colorSpace == Imgproc.COLOR_BGR2Lab) {double deltaE = colorDistanceCIEDE2000(c1, c2);return deltaE < threshold;} else {double distance = colorDistanceEuclidean(c1, c2);return distance < threshold;}
}
3.5 颜色对比应用实例
3.5.1 屏幕取色器实现
public class ColorPickerView extends View {private Bitmap mBitmap;private int mSelectedX = -1;private int mSelectedY = -1;private OnColorSelectedListener mListener;public interface OnColorSelectedListener {void onColorSelected(int color);}public ColorPickerView(Context context) {super(context);}public ColorPickerView(Context context, AttributeSet attrs) {super(context, attrs);}public void setBitmap(Bitmap bitmap) {mBitmap = bitmap;invalidate();}public void setOnColorSelectedListener(OnColorSelectedListener listener) {mListener = listener;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (mBitmap != null) {// 绘制图像Rect src = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());Rect dst = new Rect(0, 0, getWidth(), getHeight());canvas.drawBitmap(mBitmap, src, dst, null);// 绘制选择点标记if (mSelectedX >= 0 && mSelectedY >= 0) {Paint paint = new Paint();paint.setColor(Color.WHITE);paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(3);canvas.drawCircle(mSelectedX, mSelectedY, 20, paint);// 获取并显示颜色值int color = getPixelColor(mBitmap, mSelectedX, mSelectedY);paint.setStyle(Paint.Style.FILL);paint.setColor(color);canvas.drawRect(getWidth() - 100, 20, getWidth() - 20, 100, paint);paint.setColor(Color.BLACK);paint.setTextSize(30);canvas.drawText(String.format("#%06X", (0xFFFFFF & color)), getWidth() - 180, 70, paint);}}}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (mBitmap == null) return super.onTouchEvent(event);int x = (int)(event.getX() * mBitmap.getWidth() / getWidth());int y = (int)(event.getY() * mBitmap.getHeight() / getHeight());if (x >= 0 && x < mBitmap.getWidth() && y >= 0 && y < mBitmap.getHeight()) {mSelectedX = x;mSelectedY = y;if (mListener != null) {int color = getPixelColor(mBitmap, x, y);mListener.onColorSelected(color);}invalidate();return true;}return super.onTouchEvent(event);}private int getPixelColor(Bitmap bitmap, int x, int y) {if (x < 0 || y < 0 || x >= bitmap.getWidth() || y >= bitmap.getHeight()) {return Color.TRANSPARENT;}return bitmap.getPixel(x, y);}
}
3.5.2 颜色匹配检测
public class ColorMatchDetector {private Scalar mTargetColor;private double mThreshold;private int mColorSpace;public ColorMatchDetector(Scalar targetColor, double threshold, int colorSpace) {mTargetColor = targetColor;mThreshold = threshold;mColorSpace = colorSpace;}public Mat findColorRegions(Mat inputImage) {// 转换为目标颜色空间Mat converted = new Mat();Imgproc.cvtColor(inputImage, converted, mColorSpace);// 创建目标颜色矩阵Mat targetMat = new Mat(inputImage.size(), converted.type(), mTargetColor);// 计算差异Mat diff = new Mat();Core.absdiff(converted, targetMat, diff);// 分割通道List<Mat> channels = new ArrayList<>();Core.split(diff, channels);// 计算总差异Mat totalDiff = new Mat(channels.get(0).size(), CvType.CV_32F);for (Mat channel : channels) {Mat floatChannel = new Mat();channel.convertTo(floatChannel, CvType.CV_32F);Core.add(totalDiff, floatChannel.mul(floatChannel), totalDiff);}Core.sqrt(totalDiff, totalDiff);// 创建掩码Mat mask = new Mat();Core.inRange(totalDiff, new Scalar(0), new Scalar(mThreshold), mask);// 清理小区域Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(5, 5));Imgproc.morphologyEx(mask, mask, Imgproc.MORPH_OPEN, kernel);Imgproc.morphologyEx(mask, mask, Imgproc.MORPH_CLOSE, kernel);return mask;}public List<Rect> findColorContours(Mat inputImage) {Mat mask = findColorRegions(inputImage);// 查找轮廓List<MatOfPoint> contours = new ArrayList<>();Mat hierarchy = new Mat();Imgproc.findContours(mask, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);// 转换为矩形List<Rect> rects = new ArrayList<>();for (MatOfPoint contour : contours) {Rect rect = Imgproc.boundingRect(contour);if (rect.area() > 100) { // 忽略小区域rects.add(rect);}}return rects;}
}
四、指定区域OCR内容提取
4.1 OCR技术简介
OCR(Optical Character Recognition,光学字符识别)是将图像中的文字转换为可编辑文本的技术。在Android中实现OCR通常有以下几种方式:
- 使用Tesseract OCR引擎
- 使用Google ML Kit文本识别API
- 使用第三方OCR服务API
4.2 Tesseract OCR集成
4.2.1 添加Tesseract依赖
在build.gradle中添加:
dependencies {implementation 'com.rmtheis:tess-two:9.1.0'
}
4.2.2 初始化Tesseract
public class TessOCR {private TessBaseAPI mTess;public TessOCR(Context context, String language) {mTess = new TessBaseAPI();// 训练数据路径String datapath = context.getFilesDir() + "/tesseract/";File dir = new File(datapath + "tessdata/");if (!dir.exists()) {dir.mkdirs();}// 检查训练数据文件是否存在File tessdataFile = new File(datapath + "tessdata/" + language + ".traineddata");if (!tessdataFile.exists()) {try {// 从assets复制训练数据InputStream in = context.getAssets().open("tessdata/" + language + ".traineddata");OutputStream out = new FileOutputStream(tessdataFile);byte[] buffer = new byte[1024];int read;while ((read = in.read(buffer)) != -1) {out.write(buffer, 0, read);}in.close();out.flush();out.close();} catch (IOException e) {e.printStackTrace();}}mTess.init(datapath, language);}public String recognizeText(Bitmap bitmap) {if (bitmap == null) return "";mTess.setImage(bitmap);return mTess.getUTF8Text();}public String recognizeText(Mat mat) {if (mat.empty()) return "";Bitmap bitmap = Bitmap.createBitmap(mat.cols(), mat.rows(), Bitmap.Config.ARGB_8888);Utils.matToBitmap(mat, bitmap);return recognizeText(bitmap);}public void destroy() {if (mTess != null) {mTess.end();}}
}
4.3 图像预处理优化OCR结果
4.3.1 基本预处理流程
public static Mat prepareImageForOCR(Mat src) {if (src.empty()) return new Mat();Mat processed = new Mat();// 1. 转换为灰度图像Imgproc.cvtColor(src, processed, Imgproc.COLOR_BGR2GRAY);// 2. 应用自适应阈值Imgproc.adaptiveThreshold(processed, processed, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,Imgproc.THRESH_BINARY, 11, 2);// 3. 降噪Imgproc.medianBlur(processed, processed, 3);// 4. 锐化Mat kernel = new Mat(3, 3, CvType.CV_32F) {{put(0, 0, 0);put(0, 1, -1);put(0, 2, 0);put(1, 0, -1);put(1, 1, 5);put(1, 2, -1);put(2, 0, 0);put(2, 1, -1);put(2, 2, 0);}};Imgproc.filter2D(processed, processed, -1, kernel);return processed;
}
4.3.2 高级预处理技术
public static Mat advancedOCRPreprocessing(Mat src) {if (src.empty()) return new Mat();Mat processed = new Mat();// 1. 转换为灰度图像Imgproc.cvtColor(src, processed, Imgproc.COLOR_BGR2GRAY);// 2. 应用CLAHE (对比度受限的自适应直方图均衡化)CLAHE clahe = Imgproc.createCLAHE();clahe.setClipLimit(2);clahe.apply(processed, processed);// 3. 非局部均值去噪Photo.fastNlMeansDenoising(processed, processed, 10, 7, 21);// 4. 边缘增强Mat edges = new Mat();Imgproc.Sobel(processed, edges, CvType.CV_8U, 1, 1);Core.add(processed, edges, processed);// 5. 局部自适应阈值Imgproc.adaptiveThreshold(processed, processed, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,Imgproc.THRESH_BINARY, 15, 5);// 6. 形态学操作去除小噪点Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(2, 2));Imgproc.morphologyEx(processed, processed, Imgproc.MORPH_OPEN, kernel);return processed;
}
4.4 指定区域OCR实现
4.4.1 从指定区域提取文本
public class RegionOCR {private TessOCR mTessOCR;public RegionOCR(Context context, String language) {mTessOCR = new TessOCR(context, language);}public String extractTextFromRegion(Bitmap srcBitmap, Rect region) {if (srcBitmap == null || region == null) return "";// 裁剪指定区域Bitmap cropped = Bitmap.createBitmap(srcBitmap, region.left, region.top,region.width(), region.height());// 预处理图像Mat mat = new Mat();Utils.bitmapToMat(cropped, mat);mat = prepareImageForOCR(mat);// 执行OCRreturn mTessOCR.recognizeText(mat);}public String extractTextFromRegion(Mat srcMat, Rect region) {if (srcMat.empty() || region == null) return "";// 调整区域确保在图像范围内Rect adjusted = new Rect(Math.max(0, region.x),Math.max(0, region.y),Math.min(srcMat.cols() - region.x, region.width),Math.min(srcMat.rows() - region.y, region.height));// 裁剪指定区域Mat cropped = new Mat(srcMat, adjusted);// 预处理图像Mat processed = prepareImageForOCR(cropped);// 执行OCRreturn mTessOCR.recognizeText(processed);}public void destroy() {if (mTessOCR != null) {mTessOCR.destroy();}}
}