Android Studio中OpenCV应用详解:图像处理、颜色对比与OCR识别_android opencv
文章目录
-
- 一、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) { @Override public void onManagerConnected(int status) { if (status == LoaderCallbackInterface.SUCCESS) { Log.i(\"OpenCV\", \"OpenCV loaded successfully\"); } else { super.onManagerConnected(status); } } }; @Override protected 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); // 计算SSIM Mat 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为Mat Mat 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为Mat Mat 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); // 转换回Bitmap Bitmap 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, // Green pixel & 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; } @Override protected 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); } } } @Override public 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); // 执行OCR return 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); // 执行OCR return mTessOCR.recognizeText(processed); } public void destroy() { if (mTessOCR != null) { mTessOCR.destroy(); } }}