例子使用的版本为3.4.0,安装配置网上资料比较多。
代码为本地测试时候的版本,所以会有点乱。
import org.opencv.core.*; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; import org.opencv.utils.Converters; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * @author liuxf * @date 2018/10/22 14:28 * @param * @return */ public class OpenCVJavaTest2 { static{ System.loadLibrary(Core.NATIVE_LIBRARY_NAME); } public static void main(String[] args) { String imgUrl = "F:\\1.jpg"; Mat src = Imgcodecs.imread(imgUrl ,1); Mat src_gray = new Mat(); test1(src,src_gray); } public static void test1(Mat src ,Mat src_gray){ Listcontours = new ArrayList (); List markContours = new ArrayList (); System.loadLibrary(Core.NATIVE_LIBRARY_NAME); /**图片太小就放大**/ if (src.width()*src.height()<90000){ Imgproc.resize(src,src,new Size(800,600)); } Mat src_all=src.clone(); //彩色图转灰度图 Imgproc.cvtColor(src ,src_gray ,Imgproc.COLOR_RGB2GRAY); //对图像进行平滑处理 Imgproc.GaussianBlur(src_gray, src_gray, new Size(3,3), 0); /**Imgcodecs.imwrite("F:\\output\\EH.jpg", src_gray);**/ Imgproc.Canny(src_gray,src_gray,112,255); /**Imgcodecs.imwrite("F:\\output\\1-2.jpg", src_gray);**/ Mat hierarchy = new Mat(); Imgproc.findContours(src_gray ,contours ,hierarchy ,Imgproc.RETR_TREE ,Imgproc.CHAIN_APPROX_NONE); for ( int i = 0; i< contours.size(); i++ ) { MatOfPoint2f newMtx = new MatOfPoint2f( contours.get(i).toArray() ); RotatedRect rotRect = Imgproc.minAreaRect( newMtx ); double w = rotRect.size.width; double h = rotRect.size.height; double rate = Math.max(w, h)/Math.min(w, h) ; /*** * 长短轴比小于1.3,总面积大于60 */ if (rate < 1.3 && w < src_gray.cols()/4 && h 60) { /*** * 计算层数,二维码角框有五层轮廓(有说六层),这里不计自己这一层,有4个以上子轮廓则标记这一点 */ double[] ds = hierarchy.get(0, i); if (ds != null && ds.length>3){ int count =0; if (ds[3] == -1){/**最外层轮廓排除*/ continue; } /*** * 计算所有子轮廓数量 */ while ((int) ds[2] !=-1){ ++count; ds = hierarchy.get(0 ,(int) ds[2]); } if (count >= 4){ markContours.add(contours.get(i)); } } } } /** * 这部分代码画框,调试用**/ for(int i=0; i ){ Imgproc.drawContours(src_all,markContours,i,new Scalar(0,255,0) ,-1); } Imgcodecs.imwrite("F:\\output\\2-1.jpg", src_all); /*** * 二维码有三个角轮廓,少于三个的无法定位放弃,多余三个的循环裁剪出来 */ if (markContours.size() < 3){ return; }else{ for (int i=0; i ){ List threePointList = new ArrayList<>(); for (int j=i+1;j ){ for (int k=j+1;k ){ threePointList.add(markContours.get(i)); threePointList.add(markContours.get(j)); threePointList.add(markContours.get(k)); capture(threePointList ,src ,i+"-"+j+"-"+k); threePointList.clear(); } } } } } /*** * 另一种实现,识别能力比第一种弱 * @param src * @param src_gray */ public static void test2(Mat src,Mat src_gray){ List contours = new ArrayList (); List markContours = new ArrayList (); if (src.width()*src.height()<90000){ Imgproc.resize(src,src,new Size(800,600)); } Mat src_all=src.clone(); Mat threshold_output = new Mat(); //彩色图转灰度图 Imgproc.cvtColor(src ,src_gray ,Imgproc.COLOR_RGB2GRAY); //对图像进行平滑处理 Imgproc.blur(src_gray ,src_gray ,new Size(3,3)); Imgproc.equalizeHist(src_gray,src_gray); //指定112阀值进行二值化 Imgproc.threshold(src_gray ,threshold_output,112,255 ,Imgproc.THRESH_BINARY ); /**Imgcodecs.imwrite("F:\\output\\1-2.jpg", threshold_output);**/ Mat hierarchy = new Mat(); Imgproc.findContours(threshold_output ,contours ,hierarchy ,Imgproc.RETR_TREE ,Imgproc.CHAIN_APPROX_SIMPLE); int c=0,ic=0,area=0; int parentIdx=-1; for ( int i = 0; i< contours.size(); i++ ) { double[] ds = hierarchy.get(0, i); int k=i; if (ds == null) { continue; } if (ds != null && ds.length>3){ int count =0; if (ds[3] == -1){ continue; } while ((int) ds[2] !=-1){ ++count; ds = hierarchy.get(0 ,(int) ds[2]); } if (count >= 2){ markContours.add(contours.get(i)); } } } Point[] point = new Point[markContours.size()]; for(int i=0; i ) { point[i] = centerCal(markContours.get(i)); } } /** * 对图片进行矫正,裁剪 * @param contours * @param src * @param idx */ public static void capture(List contours ,Mat src ,String idx){ Point[] pointthree = new Point[3]; for(int i=0; i ) { pointthree[i] = centerCal(contours.get(i)); } /**画线 * **/ Mat sline = src.clone(); Imgproc.line(sline ,pointthree[0],pointthree[1] ,new Scalar(0,0,255),2); Imgproc.line(sline ,pointthree[1],pointthree[2] ,new Scalar(0,0,255),2); Imgproc.line(sline ,pointthree[0],pointthree[2] ,new Scalar(0,0,255),2); Imgcodecs.imwrite("F:\\output\\cvRio-"+idx+".jpg", sline); double[] ca = new double[2]; double[] cb = new double[2]; ca[0] = pointthree[1].x - pointthree[0].x; ca[1] = pointthree[1].y - pointthree[0].y; cb[0] = pointthree[2].x - pointthree[0].x; cb[1] = pointthree[2].y - pointthree[0].y; /* if (Math.max(ca[0],cb[0])/Math.min(ca[0],cb[0]) > 1.5 || Math.max(ca[1],cb[1])/Math.min(ca[1],cb[1])>1.3){ return; }*/ double angle1 = 180/3.1415*Math.acos((ca[0]*cb[0]+ca[1]*cb[1])/(Math.sqrt(ca[0]*ca[0]+ca[1]*ca[1])*Math.sqrt(cb[0]*cb[0]+cb[1]*cb[1]))); double ccw1; if(ca[0]*cb[1] - ca[1]*cb[0] > 0) { ccw1 = 0; } else { ccw1 = 1; } ca[0] = pointthree[0].x - pointthree[1].x; ca[1] = pointthree[0].y - pointthree[1].y; cb[0] = pointthree[2].x - pointthree[1].x; cb[1] = pointthree[2].y - pointthree[1].y; double angle2 = 180/3.1415*Math.acos((ca[0]*cb[0]+ca[1]*cb[1])/(Math.sqrt(ca[0]*ca[0]+ca[1]*ca[1])*Math.sqrt(cb[0]*cb[0]+cb[1]*cb[1]))); double ccw2; if(ca[0]*cb[1] - ca[1]*cb[0] > 0) { ccw2 = 0; }else { ccw2 = 1; } ca[0] = pointthree[1].x - pointthree[2].x; ca[1] = pointthree[1].y - pointthree[2].y; cb[0] = pointthree[0].x - pointthree[2].x; cb[1] = pointthree[0].y - pointthree[2].y; double angle3 = 180/3.1415*Math.acos((ca[0]*cb[0]+ca[1]*cb[1])/(Math.sqrt(ca[0]*ca[0]+ca[1]*ca[1])*Math.sqrt(cb[0]*cb[0]+cb[1]*cb[1]))); int ccw3; if(ca[0]*cb[1] - ca[1]*cb[0] > 0) { ccw3 = 0; }else { ccw3 = 1; } System.out.println("angle1:"+angle1+",angle2:"+angle2+",angle3:"+angle3); if (Double.isNaN(angle1) || Double.isNaN(angle2) || Double.isNaN(angle3)){ return; } Point[] poly= new Point[4]; if(angle3>angle2 && angle3>angle1) { if(ccw3==1) { poly[1] = pointthree[1]; poly[3] = pointthree[0]; } else { poly[1] = pointthree[0]; poly[3] = pointthree[1]; } poly[0] = pointthree[2]; Point temp = new Point(pointthree[0].x + pointthree[1].x - pointthree[2].x , pointthree[0].y + pointthree[1].y - pointthree[2].y ); poly[2] = temp; } else if(angle2>angle1 && angle2>angle3) { if(ccw2==1) { poly[1] = pointthree[0]; poly[3] = pointthree[2]; } else { poly[1] = pointthree[2]; poly[3] = pointthree[0]; } poly[0] = pointthree[1]; Point temp = new Point(pointthree[0].x + pointthree[2].x - pointthree[1].x , pointthree[0].y + pointthree[2].y - pointthree[1].y ); poly[2] = temp; } else if(angle1>angle2 && angle1 > angle3) { if(ccw1==1) { poly[1] = pointthree[1]; poly[3] = pointthree[2]; } else { poly[1] = pointthree[2]; poly[3] = pointthree[1]; } poly[0] = pointthree[0]; Point temp = new Point(pointthree[1].x + pointthree[2].x - pointthree[0].x , pointthree[1].y + pointthree[2].y - pointthree[0].y ); poly[2] = temp; } Point[] trans=new Point[4]; int temp =50; trans[0] = new Point(0+temp,0+temp); trans[1] = new Point(0+temp,100+temp); trans[2] = new Point(100+temp,100+temp); trans[3] = new Point(100+temp,0+temp); double maxAngle = Math.max(angle3,Math.max(angle1,angle2)); System.out.println(maxAngle); if (maxAngle<75 || maxAngle>115){ /**二维码为直角,最大角过大或者过小都判断为不是二维码*/ return; } Mat perspectiveMmat=Imgproc.getPerspectiveTransform(Converters.vector_Point_to_Mat(Arrays.asList(poly),CvType.CV_32F),Converters.vector_Point_to_Mat(Arrays.asList(trans),CvType.CV_32F)); //warp_mat Mat dst = new Mat(); //计算变换结果 Imgproc.warpPerspective(src,dst ,perspectiveMmat,src.size(),Imgproc.INTER_LINEAR); Rect roiArea = new Rect(0, 0, 200, 200); Mat dstRoi = new Mat(dst, roiArea); Imgcodecs.imwrite("F:\\output\\dstRoi-"+idx+".jpg", dstRoi); } public static BufferedImage toBufferedImage(Mat m) { int type = BufferedImage.TYPE_BYTE_GRAY; if (m.channels() > 1) { type = BufferedImage.TYPE_3BYTE_BGR; } int bufferSize = m.channels() * m.cols() * m.rows(); byte[] b = new byte[bufferSize]; m.get(0, 0, b); // get all the pixels BufferedImage image = new BufferedImage(m.cols(), m.rows(), type); final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); System.arraycopy(b, 0, targetPixels, 0, b.length); return image; } public static Point centerCal(MatOfPoint matOfPoint){ double centerx=0,centery=0; int size = matOfPoint.cols(); MatOfPoint2f mat2f = new MatOfPoint2f( matOfPoint.toArray() ); RotatedRect rect = Imgproc.minAreaRect( mat2f ); Point vertices[] = new Point[4]; rect.points(vertices); centerx = ((vertices[0].x + vertices[1].x)/2 + (vertices[2].x + vertices[3].x)/2)/2; centery = ((vertices[0].y + vertices[1].y)/2 + (vertices[2].y + vertices[3].y)/2)/2; Point point= new Point(centerx,centery); return point; } }
需要说明一下:
1.
double[] ds = hierarchy.get(0, i);
网上的资料ds[0]是后一个轮廓,ds[1]是前一个轮廓,ds[2]是父轮廓,ds[3]是内嵌轮廓, 但是通过实际测试ds[2]是内嵌轮廓 ds[3]是父轮廓 ,不知道跟版本有没有关系,还请自测。
2.网上大部分例子都是只定位3点的,但是实际图片如果模糊或者有干扰的话定位出来会是多个点,所以这里进行了循环的裁剪。
3.通过长短轴、面积和角度丢弃的点是没经过数值测试的
参考资料:
https://blog.csdn.net/iamqianrenzhan/article/details/79117119
https://www.jianshu.com/p/957f83f646cf?nomobile=yes
https://blog.csdn.net/marooon/article/details/81332487