如何在Android中使用OpenCV计数堆叠的卡片?
堆叠薄卡片计数问题
我有一张相机拍摄的堆叠卡片原图,想用OpenCV计数其中的卡片数量,但尝试后结果始终为1。已经完成灰度化+阈值处理,也试过形态学操作与Canny边缘检测,但处理后图像损坏。当前使用的代码如下:
Imgproc.cvtColor(srcMat, gray2, Imgproc.COLOR_BGR2GRAY); //Imgproc.threshold(gray2, ada, 0, 255, Imgproc.THRESH_OTSU); Imgproc.threshold(gray2,thresh,165,255,THRESH_BINARY); Imgproc.HoughLinesP(thresh, lines, 1.0, Math.PI / 180, 50, 100.0, 10.0); double num_lines = lines.cols(); for(int j =0; j <= lines.rows();j++){ if(lines.get(j,0) != null){ double [] dArr = lines.get(j,0); Point point1 = new Point(dArr[0],dArr[1]); Point point2 = new Point(dArr[2],dArr[3]); Imgproc.line( thresh, point1, point2, new Scalar(0.0, 255.0, 0.0), 1 ); } }
想咨询是否能对这类较薄的堆叠物体进行计数,且实际要计数的物体更薄,希望得到代码修正方案或可行的解决代码。
解决方案与代码修正
薄堆叠物体(比如卡片)完全可以通过OpenCV实现计数,核心是精准提取每张卡片的边缘线条,关键在于优化预处理流程和线条检测参数。
原代码问题分析
- 固定阈值
165适应性差,光照变化时会丢失薄边缘细节 - 未做噪声去除,直接用阈值图做霍夫线检测易误检/漏检
- 霍夫线参数(阈值、最小线长、最大间隙)设置不合理,无法捕捉薄边缘
- 循环边界错误:
j <= lines.rows()会导致数组越界,应为j < lines.rows()
修正后的可行代码
以下是针对薄堆叠卡片优化的Java代码,核心流程:灰度化→自适应阈值降噪→形态学增强边缘→霍夫线检测→统计有效边缘数
import org.opencv.core.*; import org.opencv.imgproc.Imgproc; public class StackedCardsCounter { public static int countStackedCards(Mat srcMat) { // 1. 灰度化 Mat gray = new Mat(); Imgproc.cvtColor(srcMat, gray, Imgproc.COLOR_BGR2GRAY); // 2. 自适应阈值处理(适配光照不均,保留薄边缘) Mat thresh = new Mat(); Imgproc.adaptiveThreshold(gray, thresh, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY_INV, 11, 2); // 3. 形态学操作:腐蚀+膨胀,去噪同时增强边缘 Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(2, 2)); Imgproc.erode(thresh, thresh, kernel); Imgproc.dilate(thresh, thresh, kernel); // 4. 霍夫线检测:调整参数捕捉薄边缘 Mat lines = new Mat(); // 参数:rho=1, theta=π/180, 检测阈值=30, 最小线长=50, 最大间隙=5 Imgproc.HoughLinesP(thresh, lines, 1, Math.PI / 180, 30, 50, 5); // 5. 统计有效水平边缘(卡片堆叠边缘为水平方向) int validEdges = 0; if (!lines.empty()) { for (int j = 0; j < lines.rows(); j++) { double[] dArr = lines.get(j, 0); if (dArr != null) { // 过滤非水平线条:计算线条斜率,只保留接近水平的 double yDiff = Math.abs(dArr[1] - dArr[3]); double xDiff = Math.abs(dArr[0] - dArr[2]); if (xDiff > 0 && (yDiff / xDiff) < 0.1) { validEdges++; // 可选:在原图绘制检测到的边缘 Imgproc.line(srcMat, new Point(dArr[0], dArr[1]), new Point(dArr[2], dArr[3]), new Scalar(0, 255, 0), 2); } } } } // 6. 计算卡片数量:每张卡片对应一条上/下边缘,数量=有效边缘数 +1 return validEdges + 1; } }
针对更薄物体的优化建议
- 调整自适应阈值参数:将
blockSize改为7,C值改为1,增强薄边缘的提取效果 - 替换预处理步骤:用Canny边缘检测替代自适应阈值,参数设置为
Imgproc.Canny(gray, edges, 50, 150),再对边缘图做霍夫线检测 - 拍摄时保证光照均匀,避免阴影干扰,能大幅提升检测精度
内容的提问来源于stack exchange,提问作者Handy Siswanto
相关产品推荐
相关产品推荐

