1. 原图像大小调整,提高运算效率
2. 转化为灰度图
3. 高斯平滑滤波
4.求得水平和垂直方向灰度图像的梯度差,使用Sobel算子
5.均值滤波,消除高频噪声
6.二值化
7.闭运算,填充条形码间隙
8. 腐蚀,去除孤立的点
9. 膨胀,填充条形码间空隙,根据核的大小,有可能需要2~3次膨胀操作
10.通过findContours找到条形码区域的矩形边界
实现:
使用另一幅图片的效果如下:
底部的二维码左侧边界定位错位,检测发现在二值化的时候左侧第二个条码部分被归零了,导致在之后的腐蚀操作中被腐蚀掉了。调整阈值分界值180到160,重新运行正确:
安卓:
* 提取区域 * */ public Bitmap getImageDiscriminatePoint() { Mat imageSobelX = new Mat(); Mat imageSobelY = new Mat(); Mat imageSobelOut = new Mat(); Mat image = new Mat(); Mat imageGray = new Mat(); Mat imageGuussian = new Mat(); Utils.bitmapToMat(source, image); //1:调整图片大小 Imgproc.resize(image, image, new Size(image.rows() / 4, image.cols() / 4)); //2:灰度化 Imgproc.cvtColor(image, imageGray, Imgproc.COLOR_BGR2GRAY); //3:高斯平滑, Imgproc.getGaussianKernel();高斯滤波 Imgproc.GaussianBlur(imageGray, imageGuussian, new Size(3,3), 0); //4:求得水平和垂直方向灰度图像的梯度差,使用Sobel算子 Mat imageX16S = new Mat(); Mat imageY16S = new Mat(); Imgproc.Sobel(imageGuussian, imageX16S, CvType.CV_16S, 1, 0, 3, 1, 0, 4); Imgproc.Sobel(imageGuussian, imageY16S, CvType.CV_16S, 0, 1, 3, 1, 0, 4); Core.convertScaleAbs(imageX16S, imageSobelX, 1, 0); Core.convertScaleAbs(imageY16S, imageSobelY, 1, 0); // imageSobelOut = imageSobelX - imageSobelY; Core.addWeighted(imageSobelX, 0.5, imageSobelY, 0.5, 1, imageSobelOut);//计算梯度和 //Core.divide(imageSobelX, imageSobelY, imageSobelOut); //5:均值滤波,消除高频噪声 Imgproc.blur(imageSobelOut, imageSobelOut, new Size(3, 3)); //6:二值化 // Mat imageSobleOutThreshold = new Mat(); // Imgproc.threshold(imageSobelOut, imageSobleOutThreshold, 100, 255, Imgproc.THRESH_BINARY); //7.闭运算,填充条形码间隙 // Mat element = Imgproc.getStructuringElement(0, new Size(7, 7)); // Imgproc.morphologyEx(imageSobleOutThreshold, imageSobleOutThreshold, Imgproc.MORPH_CLOSE, element); // //8. 腐蚀,去除孤立的点 // // Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(8, 8)); // Imgproc.erode(imageSobleOutThreshold, imageSobleOutThreshold, element); // // //9. 膨胀,填充条形码间空隙,根据核的大小,有可能需要2~3次膨胀操作 // Imgproc.dilate(imageSobleOutThreshold,imageSobleOutThreshold,element); image = imageSobelOut; //10.通过findContours找到条形码区域的矩形边界 // List<MatOfPoint> contours = new ArrayList<MatOfPoint>(); // Mat hierarcy = new Mat(); // Imgproc.findContours(imageSobleOutThreshold, contours, hierarcy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_NONE); // // Log.d("-----------------", "求灰度图"+contours.size()); // for(int i=0;i<contours.size();i++) { // Rect rect = Imgproc.boundingRect(contours.get(i)); // Log.d("-----------------", "这里知心了"+rect.x+"------"+rect.y+"-----"+rect.width+"-------"+rect.height); // Imgproc.rectangle(image, new Point(rect.x + rect.width, rect.y + rect.height),new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(177)); // } idcardBit = Bitmap.createBitmap(image.cols(), image.rows(), Bitmap.Config.RGB_565);//ARGB_8888,RGB_565 Utils.matToBitmap(image, idcardBit); //腐蚀 // Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(8, 8)); // Imgproc.erode(idcardMat, idcardMat, kernel); // Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(50, 50)); // Imgproc.erode(idcardMat, idcardMat, kernel); hanler.sendMessage(new Message()); // File file = new File(Environment.getExternalStorageDirectory()+"/AiLingGong/", "test.jpg"); // try { // FileOutputStream out = new FileOutputStream(file); // idcardBit.compress(Bitmap.CompressFormat.JPEG, 100, out); // out.flush(); // out.close(); // } catch (FileNotFoundException e) { // Log.d("-----------------", "111111"); // e.printStackTrace(); // } catch (IOException e) { // Log.d("----------------", "2222222"); // e.printStackTrace(); // } // List<MatOfPoint> contours = new ArrayList<MatOfPoint>(); // Mat hierarcy = new Mat(); // Imgproc.findContours(idcardMat, contours, hierarcy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE); // // int idCardNumberY = 0; // // for(int i=0;i<contours.size();i++){ // Rect rect = Imgproc.boundingRect(contours.get(i)); // Imgproc.rectangle(idcardMat, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(255, 0, 0), 3); // if(rect.width > 500 && rect.width/rect.height >= 6){ // //这里可能取到多个轮廓噢,“地址的轮廓也可能会进来”,需要简单筛选一下下面的轮廓,(之前bug原因,腐蚀不够高,大量轮廓进来了) // if (idCardNumberY < rect.y) { // idCardNumberY = rect.y; // idcardBit = cropDownPart(source, rect.x, rect.y, rect.width, rect.height); // } // // File file = new File(Environment.getExternalStorageDirectory()+"/AiLingGong/", "w"+rect.width+"h"+rect.height+".jpg"); // try { // FileOutputStream out = new FileOutputStream(file); // idcardBit.compress(Bitmap.CompressFormat.JPEG, 100, out); // out.flush(); // out.close(); // } catch (FileNotFoundException e) { // e.printStackTrace(); // } catch (IOException e) { // e.printStackTrace(); // } // } // } return idcardBit; }