android-笔记-OpenCV 实例 - 检测胶体金卡1-从图片中识别多个胶体金卡(自动识别)

张开发
2026/6/8 3:57:27 15 分钟阅读
android-笔记-OpenCV 实例 - 检测胶体金卡1-从图片中识别多个胶体金卡(自动识别)
实例1-从大图片中识别出胶体金卡的顺序不固定实例2将解决获取-0-3张卡片图像从原图上通过轮廓识别获取0-3张卡片图像bitmap mSurfaceView.readPicture(img_cha1); //获取原图片 Utils.bitmapToMat(bitmap, mat); Mat binaryImage myOpenCV.getBinaryImage(mat); //获取---二值化图像 myOpenCV.showImage(binaryImage, img_big0); ListBitmap listBitmaps myOpenCV.getContours_multiCards(bitmap,binaryImage,img_big1); Log.e(TAG, auto: 从摄像头照片中获取卡的个数 listBitmaps.size());方法// public ListBitmap getContours_multiCards(Bitmap bitmapRes,Mat binaryImage, ImageView v){ Log.e(TAG, getContours_multiCards---从图片( Mat二值图片)中获取多张胶体金卡 ); try{ //查找轮廓---获取轮廓从原图上通过轮廓识别获取0-3张卡片图像 // binaryImage必须为二值化图像(必须是8位单通道二值图像通常由Canny、阈值分割等得到) // bitmap 原摄像头拍下的图片 //-------------------------------------------------------获取所有轮廓 ListMatOfPoint contours new ArrayList(); Mat hierarchy new Mat(); Imgproc.findContours(binaryImage, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE); Log.e(TAG, 获取所有轮廓总个数 contours.size()); //------------------------------------------------------- // Log.e(TAG, 显示轮廓部分---调试用---可屏蔽 ); // Mat resultMat new Mat(binaryImage.rows(),binaryImage.cols(), CvType.CV_8UC4); // for (MatOfPoint contour : contours) { // // double area Imgproc.contourArea(contour); // if((area 100000)(area 240000)){ //根据 card面积显示图像轮廓 // // Rect rect Imgproc.boundingRect(contour);// 获取这个四边形的边界矩形 // Imgproc.rectangle(resultMat, rect.tl(), rect.br(), new Scalar(255,0,0,255), 3); //红色 // Log.e(TAG, 测试轮廓 宽 * 高 (rect.br().x - rect.tl().x) (rect.br().y - rect.tl().y) 面积 Imgproc.contourArea(contour)); // } // } // if(v ! null)showImage(resultMat,v); Log.e(TAG, ); //----------------------------------------------- //----------------------------------------------- ListRect listRect new ArrayList(); ListMatOfPoint listsquares new ArrayList(); // //------------遍历轮廓 double maxArea 0; Rect roiRect null; for (MatOfPoint contour : contours) { MatOfPoint2f approxCurve new MatOfPoint2f(); //创建浮点型轮廓容器的标准写法用于存储多边形逼近的结果 MatOfPoint2f contour2f new MatOfPoint2f(contour.toArray()); //整数点集转换为 浮点型点集 double arcLength Imgproc.arcLength(contour2f, true); //计算轮廓周长 Imgproc.approxPolyDP(contour2f, approxCurve, 0.02 * arcLength, true); //将轮廓近似为顶点更少的多边形 RotatedRect minRect Imgproc.minAreaRect(approxCurve); Log.e(TAG, minRect.toString() minRect.toString()); maxArea Imgproc.contourArea(contour); if((maxArea 100000)(maxArea 240000)){ //面积 来判度胶体金卡 //Log.e(TAG, approxCurve.total()轮廓边的数量 approxCurve.total()); // 如果4边形-8边都是为矩形 if ((approxCurve.total() 4)(approxCurve.total() 8)){ //if ((approxCurve.total() 4)){ roiRect Imgproc.boundingRect(contour);// 获取这个四边形的边界矩形 double w roiRect.br().x - roiRect.tl().x; double h roiRect.br().y - roiRect.tl().y; Log.e(TAG, getContours_multiCards: roiRect.tl() * roiRect.br() maxArea 宽 * 高 w * h w * h); //卡的面积范围140000-175000 if((w 200 w 300)(h 600 h 800)){ Log.e(TAG, getContours_multiCards: 已选方框 roiRect.tl() * roiRect.br() maxArea ); listRect.add(roiRect); } //宽高 来判度胶体金卡 } } } Log.e(TAG, step2: 方形个数 listRect.size()); //---------------------------------------- ListMat listMats new ArrayList(); ListBitmap listBitmaps new ArrayList(); for(int i 0; i listRect.size(); i){ Rect rect listRect.get(i); Bitmap b Bitmap.createBitmap(bitmapRes,rect.x 10,rect.y 5, rect.width - 10, rect. height - 10); listBitmaps.add(b); } return listBitmaps; }catch (Exception e){ return null; } }public Mat getBinaryImage(Mat mat){ Log.e(TAG, getBinaryImage:------------------获取---二值化图像 ); Mat binaryImage new Mat(); try { //--------------------------------------------------- // 2. 转换为灰度图 Mat grayImage new Mat(); Imgproc.cvtColor(mat, grayImage, Imgproc.COLOR_BGR2GRAY);//灰色图片 //-------------------------------------------------- // 3. 高斯模糊去噪 Mat blurredImage new Mat(); Imgproc.GaussianBlur(grayImage, blurredImage, new Size(5, 5), 0); //-------------------------------------------------- // 4. 图像二值化 (阈值处理) Imgproc.threshold(blurredImage, binaryImage, 0, 255, Imgproc.THRESH_BINARY_INV Imgproc.THRESH_OTSU); //Imgproc.threshold(blurredImage, binaryImage, 0, 255, Imgproc.THRESH_TOZERO); //----------------------------------------------- ///grayImage.release(); blurredImage.release(); return binaryImage; }catch (Exception e){ Log.e(TAG, getBinaryImage:---错误 ); } return null; }实例 2 从大图片中识别出胶体金卡的顺序是固定的推荐ListBitmap listBitmaps myOpenCV.getContours_getMultiCards(bitmap,null);RequiresApi(api Build.VERSION_CODES.N) public ListBitmap getContours_getMultiCards(Bitmap bitmapsrc, ImageView v){ Log.e(TAG, getContours_getMultiCards获取-----多张胶体金卡从图片 ); Mat mat new Mat(); Utils.bitmapToMat(bitmapsrc,mat); Mat grayImage new Mat(); Imgproc.cvtColor(mat, grayImage, Imgproc.COLOR_BGR2GRAY);//灰色图片 //-------------------------------------------------- // 3. 高斯模糊去噪 Mat blurredImage new Mat(); Imgproc.GaussianBlur(grayImage, blurredImage, new Size(5, 5), 0); //-------------------------------------------------- // 4. 图像二值化 (阈值处理) Mat binaryImage new Mat(); Imgproc.threshold(blurredImage, binaryImage, 0, 255, Imgproc.THRESH_BINARY_INV Imgproc.THRESH_OTSU); //----------------------------------------------- ///grayImage.release(); mat.release(); grayImage.release(); blurredImage.release(); ListBitmap listBitmaps new ArrayList(); try{ //查找轮廓---获取轮廓从原图上通过轮廓识别获取0-3张卡片图像 // binaryImage必须为二值化图像(必须是8位单通道二值图像通常由Canny、阈值分割等得到) // bitmap 原摄像头拍下的图片 //-------------------------------------------------------获取所有轮廓 ListMatOfPoint contours new ArrayList(); Mat hierarchy new Mat(); Imgproc.findContours(binaryImage, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE); Log.e(TAG, getContours_getMultiCards 获取所有轮廓总个数 contours.size()); //------------------------------------------------------- // if(v ! null) { // // Log.e(TAG, 显示轮廓部分---调试用---可屏蔽 ); // Mat resultMat new Mat(binaryImage.rows(), binaryImage.cols(), CvType.CV_8UC4); // for (MatOfPoint contour : contours) { // // double area Imgproc.contourArea(contour); // if ((area 100000) (area 240000)) { //根据 card面积显示图像轮廓 // // Rect rect Imgproc.boundingRect(contour);// 获取这个四边形的边界矩形 // Imgproc.rectangle(resultMat, rect.tl(), rect.br(), new Scalar(255, 0, 0, 255), 3); //红色 // Log.e(TAG, 测试轮廓 宽 * 高 (rect.br().x - rect.tl().x) (rect.br().y - rect.tl().y) 面积 Imgproc.contourArea(contour)); // } // } // if (v ! null) showImage(resultMat, v); // } //------------------------------------------------------ //从大图片中识别出胶体金卡的顺序是固定的 ListMatOfPoint validContours new ArrayList(); for (MatOfPoint contour : contours) { double area Imgproc.contourArea(contour); //卡的面积范围140000-175000 //面积 来判度胶体金卡 if ((area 100000) (area 240000)) { //阈值请根据你的图像调整 validContours.add(contour); } } ListMatOfPoint sortedContours sortContoursByPosition(validContours, 20); Log.e(TAG, getContours_multiCards: sortedContours.size() sortedContours.size()); //------------------------------------------------------ Rect rect null; for(int i 0; i sortedContours.size();i){ MatOfPoint contour1 sortedContours.get(i); MatOfPoint2f approxCurve new MatOfPoint2f(); //创建浮点型轮廓容器的标准写法用于存储多边形逼近的结果 MatOfPoint2f contour2f new MatOfPoint2f(contour1.toArray()); //整数点集转换为 浮点型点集 double arcLength Imgproc.arcLength(contour2f, true); //计算轮廓周长 Imgproc.approxPolyDP(contour2f, approxCurve, 0.02 * arcLength, true); //将轮廓近似为顶点更少的多边形 //Log.e(TAG, approxCurve.total()轮廓边的数量 approxCurve.total()); if ((approxCurve.total() 4)(approxCurve.total() 8)){ // 如果4边形-8边都是为矩形 rect Imgproc.boundingRect(contour1);// 获取这个四边形的边界矩形 if((rect.width 200 rect.width 300) (rect.height 600 rect.height 800)){ //宽高 来判度胶体金卡 // Log.e(TAG, getContours_multiCards: 已卡选方框 roiRect.tl() * roiRect.br() maxArea ); Bitmap b Bitmap.createBitmap(bitmapsrc,rect.x 10, rect.y 5, rect.width - 10, rect.height - 10); listBitmaps.add(b); } } } if(listBitmaps.size() 1){Collections.reverse(listBitmaps);} //list倒叙 return listBitmaps; }catch (Exception e){ return null; } }// // // /** * 对轮廓列表进行排序先按Y坐标行分组再按X坐标列排序。 * param contours 待排序的轮廓列表 * param yTolerance Y坐标容差像素差值小于此值的轮廓视为同一行 * return 排序后的轮廓列表 */ RequiresApi(api Build.VERSION_CODES.N) public ListMatOfPoint sortContoursByPosition(ListMatOfPoint contours, int yTolerance) { if (contours null || contours.size() 1) { return contours; } // 1. 为每个轮廓计算边界框并封装成列表 ListContourWithRect contourRects new ArrayList(); for (MatOfPoint contour : contours) { Rect rect Imgproc.boundingRect(contour); contourRects.add(new ContourWithRect(contour, rect)); } // 2. 按Y坐标初步排序以便分组 contourRects.sort((a, b) - Integer.compare(a.rect.y, b.rect.y)); // 3. 根据Y容差分组将Y坐标接近的轮廓归为同一行 ListListContourWithRect rows new ArrayList(); ListContourWithRect currentRow new ArrayList(); int currentRowBaseY -1; for (ContourWithRect item : contourRects) { if (currentRowBaseY -1 || Math.abs(item.rect.y - currentRowBaseY) yTolerance) { // 属于当前行 currentRow.add(item); // 更新当前行的基准Y坐标可选用平均值这里简单使用第一个元素的Y if (currentRowBaseY -1) { currentRowBaseY item.rect.y; } } else { // 新的一行开始 rows.add(new ArrayList(currentRow)); currentRow.clear(); currentRow.add(item); currentRowBaseY item.rect.y; } } if (!currentRow.isEmpty()) { rows.add(currentRow); } // 4. 对每一行内的轮廓按X坐标从左到右排序然后合并结果 ListMatOfPoint sortedContours new ArrayList(); for (ListContourWithRect row : rows) { row.sort((a, b) - Integer.compare(a.rect.x, b.rect.x)); for (ContourWithRect item : row) { sortedContours.add(item.contour); } } return sortedContours; } // 辅助内部类 private static class ContourWithRect { MatOfPoint contour; Rect rect; ContourWithRect(MatOfPoint contour, Rect rect) { this.contour contour; this.rect rect; } } // // //在实例 2中有时会出现问题原测程序正常移植到实际项目中会出现问题共2处解决第1处****************************************************************************************// 2. 按Y坐标初步排序以便分组 把contourRects.sort((a, b) - Integer.compare(a.rect.y, b.rect.y)); 改为以下 // 2. 按Y坐标初步排序使用匿名内部类 Collections.sort(contourRects, new ComparatorContourWithRect() { Override public int compare(ContourWithRect a, ContourWithRect b) { return Integer.compare(a.rect.y, b.rect.y); } });解决第1处把row.sort((a, b) - Integer.compare(a.rect.x, b.rect.x));改为// 使用匿名内部类代替 Lambda Collections.sort(row, new ComparatorContourWithRect() { Override public int compare(ContourWithRect a, ContourWithRect b) { return Integer.compare(a.rect.x, b.rect.x); } });

更多文章