Android Studio中OpenCV应用详解:图像处理、颜色对比与OCR识别

文章目录

    • 一、OpenCV在Android中的集成与配置
      • [1.1 OpenCV简介](#1.1 OpenCV简介)
      • [1.2 在Android Studio中集成OpenCV](#1.2 在Android Studio中集成OpenCV)
        • [1.2.1 通过Gradle依赖集成](#1.2.1 通过Gradle依赖集成)
        • [1.2.2 通过模块方式集成](#1.2.2 通过模块方式集成)
        • [1.2.3 初始化OpenCV](#1.2.3 初始化OpenCV)
      • [1.3 OpenCV基础类介绍](#1.3 OpenCV基础类介绍)
    • 二、指定区域图像抓取与对比
      • [2.1 图像抓取基础](#2.1 图像抓取基础)
      • [2.2 指定区域图像抓取实现](#2.2 指定区域图像抓取实现)
        • [2.2.1 从Bitmap中截取指定区域](#2.2.1 从Bitmap中截取指定区域)
        • [2.2.2 使用OpenCV截取指定区域](#2.2.2 使用OpenCV截取指定区域)
      • [2.3 图像对比技术](#2.3 图像对比技术)
        • [2.3.1 均方误差(MSE)对比](#2.3.1 均方误差(MSE)对比)
        • [2.3.2 结构相似性(SSIM)对比](#2.3.2 结构相似性(SSIM)对比)
        • [2.3.3 特征点匹配对比](#2.3.3 特征点匹配对比)
      • [2.4 图像对比应用实例](#2.4 图像对比应用实例)
        • [2.4.1 图像相似度检测](#2.4.1 图像相似度检测)
        • [2.4.2 图像差异可视化](#2.4.2 图像差异可视化)
    • 三、指定点颜色提取与对比
      • [3.1 颜色空间基础](#3.1 颜色空间基础)
      • [3.2 指定点颜色提取](#3.2 指定点颜色提取)
        • [3.2.1 从Bitmap中获取像素颜色](#3.2.1 从Bitmap中获取像素颜色)
        • [3.2.2 使用OpenCV获取像素颜色](#3.2.2 使用OpenCV获取像素颜色)
      • [3.3 颜色空间转换](#3.3 颜色空间转换)
      • [3.4 颜色对比技术](#3.4 颜色对比技术)
        • [3.4.1 欧氏距离颜色对比](#3.4.1 欧氏距离颜色对比)
        • [3.4.2 CIEDE2000颜色差异算法](#3.4.2 CIEDE2000颜色差异算法)
        • [3.4.3 颜色相似度判断](#3.4.3 颜色相似度判断)
      • [3.5 颜色对比应用实例](#3.5 颜色对比应用实例)
        • [3.5.1 屏幕取色器实现](#3.5.1 屏幕取色器实现)
        • [3.5.2 颜色匹配检测](#3.5.2 颜色匹配检测)
    • 四、指定区域OCR内容提取
      • [4.1 OCR技术简介](#4.1 OCR技术简介)
      • [4.2 Tesseract OCR集成](#4.2 Tesseract OCR集成)
        • [4.2.1 添加Tesseract依赖](#4.2.1 添加Tesseract依赖)
        • [4.2.2 初始化Tesseract](#4.2.2 初始化Tesseract)
      • [4.3 图像预处理优化OCR结果](#4.3 图像预处理优化OCR结果)
        • [4.3.1 基本预处理流程](#4.3.1 基本预处理流程)
        • [4.3.2 高级预处理技术](#4.3.2 高级预处理技术)
      • [4.4 指定区域OCR实现](#4.4 指定区域OCR实现)
        • [4.4.1 从指定区域提取文本](#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依赖:

gradle 复制代码
dependencies {
    implementation 'org.opencv:opencv-android:4.5.5'
}
1.2.2 通过模块方式集成

更灵活的方式是下载OpenCV Android SDK并将其作为模块导入:

  1. 从OpenCV官网下载Android SDK
  2. 在Android Studio中选择File -> New -> Import Module
  3. 选择OpenCV SDK中的java文件夹
  4. 修改模块的build.gradle文件,确保与主项目兼容
1.2.3 初始化OpenCV

在应用启动时需要初始化OpenCV库:

java 复制代码
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主要涉及以下几个核心类:

  1. Mat:OpenCV的基础矩阵类,用于存储图像数据
  2. Bitmap:Android的位图类,需要与Mat相互转换
  3. Imgproc:图像处理类,包含各种图像处理算法
  4. Core:核心功能类,提供基本数学运算和矩阵操作
  5. Feature2d:特征检测与描述类

二、指定区域图像抓取与对比

2.1 图像抓取基础

在Android中获取图像主要有以下几种方式:

  1. 从相机捕获
  2. 从图库选择
  3. 从资源文件加载
  4. 从网络下载

2.2 指定区域图像抓取实现

2.2.1 从Bitmap中截取指定区域
java 复制代码
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截取指定区域
java 复制代码
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)对比
java 复制代码
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)对比
java 复制代码
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 特征点匹配对比
java 复制代码
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 图像相似度检测
java 复制代码
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 图像差异可视化
java 复制代码
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支持多种颜色空间,常用的有:

  1. RGB/BGR:默认颜色空间
  2. HSV/HSL:色调、饱和度、亮度/明度
  3. Lab:感知均匀的颜色空间
  4. YCrCb:亮度和色度分量
  5. Grayscale:灰度图像

3.2 指定点颜色提取

3.2.1 从Bitmap中获取像素颜色
java 复制代码
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获取像素颜色
java 复制代码
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 颜色空间转换

java 复制代码
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 欧氏距离颜色对比
java 复制代码
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颜色差异算法
java 复制代码
// 需要实现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 颜色相似度判断
java 复制代码
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 屏幕取色器实现
java 复制代码
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 颜色匹配检测
java 复制代码
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通常有以下几种方式:

  1. 使用Tesseract OCR引擎
  2. 使用Google ML Kit文本识别API
  3. 使用第三方OCR服务API

4.2 Tesseract OCR集成

4.2.1 添加Tesseract依赖

在build.gradle中添加:

gradle 复制代码
dependencies {
    implementation 'com.rmtheis:tess-two:9.1.0'
}
4.2.2 初始化Tesseract
java 复制代码
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 基本预处理流程
java 复制代码
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 高级预处理技术
java 复制代码
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 从指定区域提取文本
java 复制代码
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();
        }
    }
}
相关推荐
啊喜拔牙8 分钟前
如何在idea中写spark程序
java·spark·intellij-idea
橘猫云计算机设计26 分钟前
springboot基于推荐算法的景点推荐系统(源码+lw+部署文档+讲解),源码可白嫖!
java·spring boot·后端·毕业设计·推荐算法
维基框架44 分钟前
Spring Boot Jpa封装快速构建Specification、OrderBy、Pageable的查询条件
java·spring boot
爱炸薯条的小朋友1 小时前
C#将Mat或Byte快速转换为Bitmap格式
开发语言·opencv·c#
Tanecious.1 小时前
C++--入门基础
java·开发语言·c++
等什么君!1 小时前
学习springboot-条件化配置@Conditional(条件注解)
java·spring boot·学习
qq_3841368442 小时前
Mybatis中的一级二级缓存扫盲
java·spring·oracle
Ya-Jun3 小时前
性能优化实践:启动优化方案
android·flutter·ios·性能优化
小梦白3 小时前
RPG7.准备GAS的工作
java·开发语言
武昌库里写JAVA3 小时前
【iview】icon样式
java·开发语言·spring boot·学习·课程设计