1. 原图像大小调整,提高运算效率
2. 转化为灰度图
3. 高斯平滑滤波
4.求得水平和垂直方向灰度图像的梯度差,使用Sobel算子
5.均值滤波,消除高频噪声
6.二值化
7.闭运算,填充条形码间隙
8. 腐蚀,去除孤立的点
9. 膨胀,填充条形码间空隙,根据核的大小,有可能需要2~3次膨胀操作
10.通过findContours找到条形码区域的矩形边界
实现:
- #include "core/core.hpp"
- #include "highgui/highgui.hpp"
- #include "imgproc/imgproc.hpp"
- using namespace cv;
- int main(int argc,char *argv[])
- {
- Mat image,imageGray,imageGuussian;
- Mat imageSobelX,imageSobelY,imageSobelOut;
- image=imread(argv[1]);
- //1. 原图像大小调整,提高运算效率
- resize(image,image,Size(500,300));
- imshow("1.原图像",image);
- //2. 转化为灰度图
- cvtColor(image,imageGray,CV_RGB2GRAY);
- imshow("2.灰度图",imageGray);
- //3. 高斯平滑滤波
- GaussianBlur(imageGray,imageGuussian,Size(3,3),0);
- imshow("3.高斯平衡滤波",imageGuussian);
- //4.求得水平和垂直方向灰度图像的梯度差,使用Sobel算子
- Mat imageX16S,imageY16S;
- Sobel(imageGuussian,imageX16S,CV_16S,1,0,3,1,0,4);
- Sobel(imageGuussian,imageY16S,CV_16S,0,1,3,1,0,4);
- convertScaleAbs(imageX16S,imageSobelX,1,0);
- convertScaleAbs(imageY16S,imageSobelY,1,0);
- imageSobelOut=imageSobelX-imageSobelY;
- imshow("4.X方向梯度",imageSobelX);
- imshow("4.Y方向梯度",imageSobelY);
- imshow("4.XY方向梯度差",imageSobelOut);
- //5.均值滤波,消除高频噪声
- blur(imageSobelOut,imageSobelOut,Size(3,3));
- imshow("5.均值滤波",imageSobelOut);
- //6.二值化
- Mat imageSobleOutThreshold;
- threshold(imageSobelOut,imageSobleOutThreshold,180,255,CV_THRESH_BINARY);
- imshow("6.二值化",imageSobleOutThreshold);
- //7.闭运算,填充条形码间隙
- Mat element=getStructuringElement(0,Size(7,7));
- morphologyEx(imageSobleOutThreshold,imageSobleOutThreshold,MORPH_CLOSE,element);
- imshow("7.闭运算",imageSobleOutThreshold);
- //8. 腐蚀,去除孤立的点
- erode(imageSobleOutThreshold,imageSobleOutThreshold,element);
- imshow("8.腐蚀",imageSobleOutThreshold);
- //9. 膨胀,填充条形码间空隙,根据核的大小,有可能需要2~3次膨胀操作
- dilate(imageSobleOutThreshold,imageSobleOutThreshold,element);
- dilate(imageSobleOutThreshold,imageSobleOutThreshold,element);
- dilate(imageSobleOutThreshold,imageSobleOutThreshold,element);
- imshow("9.膨胀",imageSobleOutThreshold);
- vector<vector<Point>> contours;
- vector<Vec4i> hiera;
- //10.通过findContours找到条形码区域的矩形边界
- findContours(imageSobleOutThreshold,contours,hiera,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);
- for(int i=0;i<contours.size();i++)
- {
- Rect rect=boundingRect((Mat)contours[i]);
- rectangle(image,rect,Scalar(255),2);
- }
- imshow("10.找出二维码矩形区域",image);
- waitKey();
- }
使用另一幅图片的效果如下:
底部的二维码左侧边界定位错位,检测发现在二值化的时候左侧第二个条码部分被归零了,导致在之后的腐蚀操作中被腐蚀掉了。调整阈值分界值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; }