当前位置: 首页 > news >正文

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并将其作为模块导入:

  1. 从OpenCV官网下载Android SDK
  2. 在Android Studio中选择File -> New -> Import Module
  3. 选择OpenCV SDK中的java文件夹
  4. 修改模块的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主要涉及以下几个核心类:

  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中截取指定区域
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支持多种颜色空间,常用的有:

  1. RGB/BGR:默认颜色空间
  2. HSV/HSL:色调、饱和度、亮度/明度
  3. Lab:感知均匀的颜色空间
  4. YCrCb:亮度和色度分量
  5. 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通常有以下几种方式:

  1. 使用Tesseract OCR引擎
  2. 使用Google ML Kit文本识别API
  3. 使用第三方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();}}
}

相关文章:

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

文章目录 一、OpenCV在Android中的集成与配置1.1 OpenCV简介1.2 在Android Studio中集成OpenCV1.2.1 通过Gradle依赖集成1.2.2 通过模块方式集成1.2.3 初始化OpenCV 1.3 OpenCV基础类介绍 二、指定区域图像抓取与对比2.1 图像抓取基础2.2 指定区域图像抓取实现2.2.1 从Bitmap中…...

企业办公协同平台安全一体化生态入住技术架构与接口标准分析报告

全球组织数字化与智能化背景下 企业办公协同平台安全一体化生态入住技术架构与接口标准分析报告 一、背景与市场需求 市场规模与增量 根据Statista数据&#xff0c;全球协同办公平台市场规模预计从2023年的$480亿增长至2027年的$900亿&#xff0c;年复合增长率&#xff08;CAG…...

从零搭建体育比分网站:技术选型与API调用实战(附完整源码)

一、前言&#xff1a;为什么选择体育比分项目&#xff1f; 体育数据网站是练手全栈开发的绝佳项目&#xff0c;涉及&#xff1a; ✅ 前端&#xff08;实时数据渲染、可视化图表&#xff09; ✅ 后端&#xff08;API对接、数据缓存、高并发优化&#xff09; ✅ 数据库&#xff…...

非凸科技受邀出席AI SPARK活动,共探生成式AI驱动金融新生态

4月19日&#xff0c;由AI SPARK社区主办的“生成式AI创新与应用构建”主题沙龙在北京举行。活动聚焦生成式AI的技术突破与产业融合&#xff0c;围绕大模型优化、多模态应用、存内计算等前沿议题展开深度探讨。非凸科技受邀出席并发表主题演讲&#xff0c;深入解析金融垂直大模型…...

深入蜂窝物联网 第五章 EC-GSM-IoT 及其他技术:混合组网与前瞻

1. 前言与应用场景 在一些地区,GSM 网络仍然大面积覆盖且运营成本低廉,运营商可通过 EC-GSM-IoT(Extended Coverage GSM for IoT)在现有GSM基站上升级,实现物联网互联。同时,为了满足不同场景的需求,常常需要与 NB-IoT、LTE-M、5G RedCap 等技术混合组网,形成多层次、…...

2025年深圳软件开发公司推荐

随着移动互联网的深度发展&#xff0c;软件开发已成为企业实现数字化转型的重要途径。作为中国科技创新中心的深圳&#xff0c;汇聚了众多技术实力雄厚的软件开发企业。本文将为您精选推荐6家在深圳表现突出的软件开发服务商&#xff0c;帮助企业找到合适的数字化转型合作伙伴。…...

仿腾讯会议——注册登录UI

1、加载素材 2、新添加资源类 3、加载图片 4、添加左侧图片 在左侧添加一个标签 选择图片 选择图片 勾选保证图片不变形 5、修改组件名称 6、设置密码输入框 5、切换 6、编辑提示框 7、定义提交和清空的槽函数 8、设置页面标题和最先显示页面 9、清空登录信息函数实现 10、清空…...

香港科技大学广州|可持续能源与环境学域博士招生宣讲会—四川大学专场

香港科技大学广州&#xff5c;可持续能源与环境学域博士招生宣讲会—四川大学专场 时间&#xff1a;2025年5月8日&#xff08;星期四&#xff09;16:30开始 地点&#xff1a;四川大学基础教学楼A座504 宣讲嘉宾&#xff1a;肖殿勋 助理教授 一经录取&#xff0c;享全额奖学金…...

设计模式(工厂模式)

工厂设计模式&#xff1a;打造你的代码生产线 引言 想象一下&#xff0c;你正站在一家现代化的玩具工厂门前。工厂内部&#xff0c;各种机器有条不紊地运转&#xff0c;原材料在传送带上流动&#xff0c;最终变成精美的玩具。你不需要了解每个玩具的具体制作工艺&#xff0c;…...

本地大模型编程实战(29)查询图数据库NEO4J(2)

上一篇文章 用大语言模型LLM查询图数据库NEO4J(1) 介绍了使用GraphQACypherChain查询NEO4J。用它实现简单快捷&#xff0c;但是不容易定制&#xff0c;在生产环境中可能会面临挑战。 本文将基于langgraph 框架&#xff0c;用LLM(大语言模型)查询图数据库NEO4J。它可以定义清晰复…...

Python爬虫(10)Python数据存储实战:基于pymongo的MongoDB开发深度指南

目录 一、为什么需要文档型数据库&#xff1f;1.1 数据存储的范式变革1.2 pymongo的核心优势 二、pymongo核心操作全解析2.1 环境准备2.2 数据库连接与CRUD操作2.3 聚合管道实战2.4 分批次插入百万级数据&#xff08;进阶&#xff09;2.5 分批次插入百万级数据&#xff08;进阶…...

从遍历序列构造二叉树:前序+中序与中序+后序的递归解法详解

文章目录 1. 问题背景2. 核心思路3. 从前序与中序遍历序列构造二叉树3.1 递归分治思路3.2 代码实现与注释 4. 从中序与后序遍历序列构造二叉树4.1 递归分治思路4.2 代码实现与注释 5. 复杂度分析6. 总结 1. 问题背景 二叉树的遍历方式包括前序&#xff08;根-左-右&#xff09…...

数据挖掘专栏介绍:用 Python + 大语言模型 (LLM) 重塑电商数据价值

写在前面 —— 不止于挖掘,更要智能涌现:用 Python + 大语言模型 (LLM) 重塑电商数据价值 或许你已经跟随我们之前的 “零基础上手Python数据分析” 专栏,掌握了 Pandas 的数据操纵、Matplotlib/Seaborn 的可视化呈现,甚至对传统的数据挖掘技术如聚类、分类、回归有了初步…...

CSS Transition入门指南

CSS Transition 完全指南 目录 Transition 的作用核心属性代码示例使用场景性能优化常见问题思维导图 1. Transition 的作用 CSS Transition 用于在 属性值变化时 创建平滑的过渡效果。例如&#xff1a; 鼠标悬停时按钮放大元素颜色渐变切换位置移动的缓动效果 2. 核心属性…...

Nginx 核心功能

目录 一、基于授权的访问控制 &#xff08;1&#xff09;使用htpasswd 生成用户认证文件 &#xff08;2&#xff09;修改密码文件权限为400&#xff0c;将所有者改为nginx &#xff0c;设置 Nginx 的运行用户能够读取 &#xff08;3&#xff09;修改主配置文件 nginx.conf&…...

排序版研究方向

姓 名研究方向电子邮箱办公电话办公地点曹培根代数cao2024ustc.edu.cn 新楼412陈洪佳代数hjchenustc.edu.cn0551-636076931529陈小伍代数xwchenmail.ustc.edu.cn0551-636062351321梁永祺代数yqliangustc.edu.cn0551-636006171613欧阳毅代数yiouyangustc.edu.cn0551-63600337…...

AI国学智慧语录视频,条条视频10W+播放量

家人们&#xff01;图书类带货玩法真的非常多&#xff0c;之前也分享过蛮多&#xff0c;例如情感语录、育儿教育、爆款图书金句类、AI历史人物解说类等等。 本期继续来分享一个对于普通人来说&#xff0c;上手相当简单&#xff0c;容易起号&#xff0c;可作为长线深耕的AI带货…...

RN 获取视频封面,获取视频第一帧

严格的说&#xff0c;没有解决这个问题&#xff0c;实际上是绕过了这个问题&#xff0c;严格的说获取的也不是第一帧。 RN的video 视频应用&#xff0c;大多是这样的 1、安装 yarn add react-native-video 2、导入 import Video from react-native-video; 3、使用 …...

缓存分片哈希 vs 一致性哈希:优缺点、区别对比及适用场景(图示版)

&#x1f4dc; 引言 在分布式缓存系统中&#xff0c;数据分布策略是设计的关键之一。缓存分片哈希和一致性哈希是两种常见的数据分布算法&#xff0c;它们各有优缺点和适用场景。本文将通过图示对比表格&#xff0c;深入解析这两种算法的核心原理、优缺点及适用场景。 &#x…...

iOS - 音频: Core Audio - 播放

环境 iOS 18 Xcode 16.3 swift-driver version: 1.120.5 Apple Swift version 6.1 (swiftlang-6.1.0.110.21 clang-1700.0.13.3) Target: x86_64-apple-macosx15.0 Core Audio 的架构 声音数据的描述 sample: 一个声道采样的值,采样率定义了每秒从连续信号中提取并组成离散信号…...

Nerfstudio 环境配置与自有数据集(图片和视频)测试全方位全流程实战【2025最新版!!】

一、引言 神经辐射场(Neural Radiance Fields&#xff0c;简称NeRF)是近年来计算机视觉和图形学领域的一项革命性技术&#xff0c;它能够从2D图像中学习复杂的3D场景表示。然而&#xff0c;NeRF技术的实现和应用门槛较高&#xff0c;需要较为专业的计算机视觉和深度学习知识。…...

【Java学习】动态代理有哪些形式?

Java动态代理的两种主要形式 动态代理在Java中有两种主要的实现方式&#xff0c;它们各有特点和使用场景&#xff1a; 1. JDK动态代理 (基于接口) 特点&#xff1a; Java标准库自带的功能&#xff08;java.lang.reflect.Proxy&#xff09;只能代理接口&#xff0c;不能代理…...

Android Studio 中实现方法和参数显示一行

Android Studio 中实现方法和参数显示一行&#xff0c;可通过以下步骤配置&#xff1a; 一、基础格式化设置 ‌快捷键格式化‌ 选中代码后使用 Ctrl Alt L&#xff08;Windows/Linux&#xff09;或 Cmd Option L&#xff08;Mac&#xff09;进行快速格式化27。 ‌菜单操作…...

SQLyog中DELIMITER执行存储过程时出现的前置缩进问题

在SQLyog中执行存储过程时出现的前置缩进问题&#xff0c;实际上反映了SQLyog对SQL语句解析的一个特殊行为。以下是详细解释和解决方案&#xff1a; 问题根源 SQLyog的语句分隔逻辑&#xff1a; SQLyog默认会根据分号(;)和换行自动分隔SQL语句 当代码有缩进时&#xff0c;SQLy…...

基于Spring Boot 3.0、ShardingSphere、PostgreSQL或达梦数据库的分库分表

要实现基于Spring Boot 3.0、ShardingSphere、PostgreSQL或达梦数据库的分库分表&#xff0c;首先需要对ShardingSphere进行一些基本配置。你提到的溯源码、批次号等数据需要考虑到跨年数据的存储&#xff0c;因此要设计一个能够动态扩展的分表策略 添加ShardingSphere依赖 在…...

vscode chrome调试怎么在所有浏览器都好使

chrome调试时只能在打开的浏览器里进行调试&#xff0c;其它打开的chrome浏览器就不能调试了&#xff0c;怎么解决。 右键点击 Chrome 的快捷方式图标&#xff0c;选择属性 在目标一栏&#xff0c;最后加上--remote-debugging-port9222 注意要用空格隔开 lanch.json 文件配置 …...

20250429在Ubuntu 20.04.6下安装VMware Workstation16

20250429在Ubuntu 20.04.6下安装VMware Workstation16 2025/4/29 20:16 缘起&#xff1a;1、在ubuntu14.04下git clone异常该如何处理呢&#xff1f; 2、请问 现在 编译NanoPi NEO的FriendlyCore系统使用ubuntu哪一个版本比较好&#xff1f; ubuntu14.04 编译异常/下载不了&am…...

Java高频面试之并发编程-10

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本baby今天来报道了&#xff01;哈哈哈哈哈嗝&#x1f436; 面试官&#xff1a;ThreadLocalMap 怎么解决 Hash 冲突的&#xff1f; ThreadLocalMap 是 ThreadLocal 的核心实现&#xff0c;它采用 开放…...

【Tauri2】035——sql和sqlx

前言 这篇就来看看插件sql SQL | Taurihttps://tauri.app/plugin/sql/ 正文 准备 添加依赖 tauri-plugin-sql {version "2.2.0",features ["sqlite"]} features可以是mysql、sqlite、postsql 进去features看看 sqlite ["sqlx/sqlite&quo…...

C++/SDL 进阶游戏开发 —— 双人塔防(代号:村庄保卫战 16)

&#x1f381;个人主页&#xff1a;工藤新一 &#x1f50d;系列专栏&#xff1a;C面向对象&#xff08;类和对象篇&#xff09; &#x1f31f;心中的天空之城&#xff0c;终会照亮我前方的路 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 文章目录 […...

VScode与远端服务器SSH链接

这里写自定义目录标题 简介步骤 简介 这里是使用密钥文件链接 步骤 首先在windows CMD中运行 ssh-keygen -t rsa&#xff0c;生成本机的公钥和私钥 这里id rsa.pub就是公钥&#xff0c;在服务器端使用&#xff0c;id rsa是私钥在用户端使用;通常目录在C:/Users/Your name/…...

数据结构入门:详解顺序表的实现与操作

目录 1.线性表 2.顺序表 2.1概念与结构 2.2分类 2.2.1静态顺序表 2.2.2动态顺序表 3.动态顺序表的实现 3.1.SeqList.h 3.2.SeqList.c 3.2.1初始化 3.2.2销毁 3.2.3打印 3.2.4顺序表扩容 3.2.5尾部插入及尾部删除 3.2.6头部插入及头部删除 3.2.7特定位置插入…...

Reactor框架介绍

Reactor(反应器模式)是一种事件驱动的设计模式,广泛用于高性能网络编程和异步I/O处理。它的核心思想是将事件分发与业务逻辑解耦,通过统一的机制处理多路I/O事件。 这个在android蓝牙中大量使用,如果这里不懂,那么很难看懂底层的逻辑,所以我们在这片文章中做一个介绍 …...

Nacos 3.0 正式发布:MCP Registry、安全零信任、链接更多生态

Nacos 3.0 正式版本发布啦&#xff01;升级 MCP Registry&#xff0c;围绕着 MCP 服务管理&#xff0c;MCP 多种类型注册&#xff0c;包含 MCP Server 注册、编排、动态调试和管理&#xff0c;并且提供 Nacos-MCP-Router 可以进行 MCP 动态发现&#xff0c;可以自动安装、代理 …...

前端安全中的XSS(跨站脚本攻击)

XSS 类型 存储型 XSS 特征&#xff1a;恶意脚本存储在服务器&#xff08;如数据库&#xff09;&#xff0c;用户访问受感染页面时触发。场景&#xff1a;用户评论、论坛帖子等持久化内容。影响范围&#xff1a;所有访问该页面的用户。 反射型 XSS 特征&#xff1a;恶意脚本通过…...

go单向链表

需求 实现单向链表的节点顺序添加、顺序遍历。 实现 package mainimport ("fmt" )type zodiac_sign struct {number intdizhi stringanimal stringyear intnext *zodiac_sign }// 添加 // func add_node_by_order(previous_node zodiac_sign, current_node z…...

Python小程序:上班该做点摸鱼的事情

系统提醒 上班会忘记一些自己的事&#xff0c;所以你需要在上班的的时候突然给你弹窗&#xff0c;你就知道要做啥了 源码 # -*- coding:utf-8 -*- """ 作者:杨桃清 日期: 2025年04日29 21:51:24 """ import datetime import time import thre…...

uni-app中使用RenderJs 使用原生js

RenderJs运行的层叫【视图层】&#xff0c;Uniapp原生Script叫【逻辑层】&#xff0c;逻辑层要调用视图层需要使用一个叫【watcher】&#xff0c;具体怎么调用呢 为了实现这两层之间的通信&#xff0c;uniapp提供了一些特定的机制。以下是对这些通信机制的详细解释&#xff0c…...

51c自动驾驶~合集37

我自己的原文哦~ https://blog.51cto.com/whaosoft/13878933 #DETR->DETR3D->Sparse4D 走向长时序稀疏3D目标检测 一、DETR 图1 DETR架构 DETR是第一篇将Transformer应用到目标检测方向的算法。DETR是一个经典的Encoder-Decoder结构的算法&#xff0c;它的骨干网…...

uniapp 小程序 安卓苹果 短视频解决方案

需求 要做类似抖音小程序的功能 思路 uniapp 使用swiper滑块 实现滑动 使用video播放视频 遇到的问题 1 video组件在小程序可以使用 uni.createVideoContext api控制 2 但是在app端会有层级问题&#xff08;因为使用的原生组件具体看官方文档&#xff09;导致无法正常滑动…...

LeetCode58_最后一个单词的长度

LeetCode58_最后一个单词的长度 标签&#xff1a;#字符串Ⅰ. 题目Ⅱ. 示例 0. 个人方法 标签&#xff1a;#字符串 Ⅰ. 题目 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、…...

深入理解Spring AI框架的核心概念

深入理解Spring AI框架的核心概念 前言 在当今人工智能飞速发展的时代&#xff0c;将AI技术集成到应用程序中已成为众多开发者关注的焦点。Spring AI框架为Java开发者提供了便捷的途径来实现这一目标。理解其核心概念对于充分发挥框架的潜力至关重要。本文将详细探讨Spring A…...

技术驱动与模式创新:开源AI大模型与S2B2C商城重构零售生态

摘要&#xff1a;在移动互联网与人工智能技术深度融合的背景下&#xff0c;零售行业正经历从“人找货”到“货找人”的范式转移。本文以开源AI大模型、AI智能名片、S2B2C商城小程序源码为核心技术要素&#xff0c;结合无人便利店、盒马鲜生、王府井二次元业态等商业实践&#x…...

精益数据分析(30/126):电商商业模式的深度剖析与关键指标解读

精益数据分析&#xff08;30/126&#xff09;&#xff1a;电商商业模式的深度剖析与关键指标解读 在创业与数据分析的漫漫征途中&#xff0c;我们都在不断探寻如何更好地理解和运用商业数据&#xff0c;以实现业务的蓬勃发展。今天&#xff0c;我依旧带着和大家共同进步的初心…...

玩玩OCR

一、Tesseract: 1.下载windows版&#xff1a; tesseract 2. 安装并记下路径&#xff0c;等会要填 3.保存.py文件 import pytesseract from PIL import Image def ocr_local_image(image_path):try:pytesseract.pytesseract.tesseract_cmd rD:\Programs\Tesseract-OCR\tesse…...

gradle 下载的tencent的镜像

distributionUrlhttps://mirrors.cloud.tencent.com/gradle/gradle-5.4.1-all.zip distributionUrlhttps://mirrors.aliyun.com/gradle/distributions/v5.4.1/gradle-5.4.1-all.zip 参考&#xff1a; gradle 镜像地址,解决 AS 下载缓慢或者下不下来的问题-CSDN博客...

【含文档+PPT+源码】基于微信小程序的乡村振兴民宿管理系统

项目介绍 本课程演示的是一款基于微信小程序的乡村振兴民宿管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套系统 3.该…...

【Redis——数据类型和内部编码和Redis使用单线程模型的分析】

文章目录 Redis的数据类型和内部编码单线程模型的工作过程Redis虽然是一个单线程模型&#xff0c;为啥效率那么高&#xff0c;速度快呢&#xff1f; 总而言之&#xff0c;Redis提供的哈希表容器并不一定真的是真的哈希表&#xff0c;而是在特点的场景下&#xff0c;用别的容器去…...

leetcode day37 474

474 一和零 给你一个二进制字符串数组 strs 和两个整数 m 和 n 。 请你找出并返回 strs 的最大子集的长度&#xff0c;该子集中 最多 有 m 个 0 和 n 个 1 。 如果 x 的所有元素也是 y 的元素&#xff0c;集合 x 是集合 y 的 子集 。 示例 1&#xff1a; 输入&#xff1a;s…...

【计算机视觉】目标检测:深度解析YOLOv9:下一代实时目标检测架构的创新与实战

深度解析YOLOv9&#xff1a;下一代实时目标检测架构的创新与实战 架构演进与技术创新YOLOv9的设计哲学核心创新解析1. 可编程梯度信息&#xff08;PGI&#xff09;2. 广义高效层聚合网络&#xff08;GELAN&#xff09;3. 轻量级设计 环境配置与快速开始硬件需求建议详细安装步骤…...