Java使用opencv进行二维码定位、矫正和裁剪

例子使用的版本为3.4.0,安装配置网上资料比较多。

代码为本地测试时候的版本,所以会有点乱。

1import org.opencv.core.*; 2import org.opencv.imgcodecs.Imgcodecs; 3import org.opencv.imgproc.Imgproc; 4import org.opencv.utils.Converters; 5 6import java.awt.image.BufferedImage; 7import java.awt.image.DataBufferByte; 8import java.util.ArrayList; 9import java.util.Arrays; 10import java.util.List; 11 12/** 13 * @author liuxf 14 * @date 2018/10/22 14:28 15 * @param 16 * @return 17 */ 18public class OpenCVJavaTest2 { 19 20 static{ System.loadLibrary(Core.NATIVE_LIBRARY_NAME); } 21 public static void main(String[] args) { 22 String imgUrl = "F:\\1.jpg"; 23 Mat src = Imgcodecs.imread(imgUrl ,1); 24 Mat src_gray = new Mat(); 25 test1(src,src_gray); 26 } 27 28 public static void test1(Mat src ,Mat src_gray){ 29 List<MatOfPoint> contours = new ArrayList<MatOfPoint>(); 30 List<MatOfPoint> markContours = new ArrayList<MatOfPoint>(); 31 System.loadLibrary(Core.NATIVE_LIBRARY_NAME); 32 /**图片太小就放大**/ 33 if (src.width()*src.height()<90000){ 34 Imgproc.resize(src,src,new Size(800,600)); 35 } 36 Mat src_all=src.clone(); 37 //彩色图转灰度图 38 Imgproc.cvtColor(src ,src_gray ,Imgproc.COLOR_RGB2GRAY); 39 //对图像进行平滑处理 40 Imgproc.GaussianBlur(src_gray, src_gray, new Size(3,3), 0); 41 /**Imgcodecs.imwrite("F:\\output\\EH.jpg", src_gray);**/ 42 Imgproc.Canny(src_gray,src_gray,112,255); 43 44 /**Imgcodecs.imwrite("F:\\output\\1-2.jpg", src_gray);**/ 45 Mat hierarchy = new Mat(); 46 Imgproc.findContours(src_gray ,contours ,hierarchy ,Imgproc.RETR_TREE ,Imgproc.CHAIN_APPROX_NONE); 47 48 for ( int i = 0; i< contours.size(); i++ ) { 49 MatOfPoint2f newMtx = new MatOfPoint2f( contours.get(i).toArray() ); 50 RotatedRect rotRect = Imgproc.minAreaRect( newMtx ); 51 double w = rotRect.size.width; 52 double h = rotRect.size.height; 53 double rate = Math.max(w, h)/Math.min(w, h) ; 54 /*** 55 * 长短轴比小于1.3,总面积大于60 56 */ 57 if (rate < 1.3 && w < src_gray.cols()/4 && h<src_gray.rows()/4 && Imgproc.contourArea(contours.get(i))>60) { 58 /*** 59 * 计算层数,二维码角框有五层轮廓(有说六层),这里不计自己这一层,有4个以上子轮廓则标记这一点 60 */ 61 double[] ds = hierarchy.get(0, i); 62 if (ds != null && ds.length>3){ 63 int count =0; 64 if (ds[3] == -1){/**最外层轮廓排除*/ 65 continue; 66 } 67 /*** 68 * 计算所有子轮廓数量 69 */ 70 while ((int) ds[2] !=-1){ 71 ++count; 72 ds = hierarchy.get(0 ,(int) ds[2]); 73 } 74 if (count >= 4){ 75 markContours.add(contours.get(i)); 76 } 77 } 78 } 79 } 80 /** 81 * 这部分代码画框,调试用**/ 82 for(int i=0; i<markContours.size(); i++){ 83 Imgproc.drawContours(src_all,markContours,i,new Scalar(0,255,0) ,-1); 84 } 85 Imgcodecs.imwrite("F:\\output\\2-1.jpg", src_all); 86 87 /*** 88 * 二维码有三个角轮廓,少于三个的无法定位放弃,多余三个的循环裁剪出来 89 */ 90 if (markContours.size() < 3){ 91 return; 92 }else{ 93 for (int i=0; i<markContours.size()-2; i++){ 94 List<MatOfPoint> threePointList = new ArrayList<>(); 95 for (int j=i+1;j<markContours.size()-1; j++){ 96 for (int k=j+1;k<markContours.size();k++){ 97 threePointList.add(markContours.get(i)); 98 threePointList.add(markContours.get(j)); 99 threePointList.add(markContours.get(k)); 100 capture(threePointList ,src ,i+"-"+j+"-"+k); 101 threePointList.clear(); 102 } 103 } 104 } 105 } 106 } 107 108 /*** 109 * 另一种实现,识别能力比第一种弱 110 * @param src 111 * @param src_gray 112 */ 113 public static void test2(Mat src,Mat src_gray){ 114 List<MatOfPoint> contours = new ArrayList<MatOfPoint>(); 115 List<MatOfPoint> markContours = new ArrayList<MatOfPoint>(); 116 if (src.width()*src.height()<90000){ 117 Imgproc.resize(src,src,new Size(800,600)); 118 } 119 Mat src_all=src.clone(); 120 Mat threshold_output = new Mat(); 121 122 //彩色图转灰度图 123 Imgproc.cvtColor(src ,src_gray ,Imgproc.COLOR_RGB2GRAY); 124 //对图像进行平滑处理 125 Imgproc.blur(src_gray ,src_gray ,new Size(3,3)); 126 Imgproc.equalizeHist(src_gray,src_gray); 127 //指定112阀值进行二值化 128 Imgproc.threshold(src_gray ,threshold_output,112,255 ,Imgproc.THRESH_BINARY ); 129 130 /**Imgcodecs.imwrite("F:\\output\\1-2.jpg", threshold_output);**/ 131 Mat hierarchy = new Mat(); 132 Imgproc.findContours(threshold_output ,contours ,hierarchy ,Imgproc.RETR_TREE ,Imgproc.CHAIN_APPROX_SIMPLE); 133 134 int c=0,ic=0,area=0; 135 int parentIdx=-1; 136 for ( int i = 0; i< contours.size(); i++ ) { 137 double[] ds = hierarchy.get(0, i); 138 int k=i; 139 if (ds == null) { 140 continue; 141 } 142 if (ds != null && ds.length>3){ 143 int count =0; 144 if (ds[3] == -1){ 145 continue; 146 } 147 while ((int) ds[2] !=-1){ 148 ++count; 149 ds = hierarchy.get(0 ,(int) ds[2]); 150 } 151 if (count >= 2){ 152 markContours.add(contours.get(i)); 153 } 154 } 155 } 156 Point[] point = new Point[markContours.size()]; 157 for(int i=0; i<markContours.size(); i++) 158 { 159 point[i] = centerCal(markContours.get(i)); 160 } 161 162 } 163 164 165 /** 166 * 对图片进行矫正,裁剪 167 * @param contours 168 * @param src 169 * @param idx 170 */ 171 public static void capture(List<MatOfPoint> contours ,Mat src ,String idx){ 172 Point[] pointthree = new Point[3]; 173 for(int i=0; i<contours.size(); i++) 174 { 175 pointthree[i] = centerCal(contours.get(i)); 176 } 177 178 /**画线 179 * **/ 180 Mat sline = src.clone(); 181 Imgproc.line(sline ,pointthree[0],pointthree[1] ,new Scalar(0,0,255),2); 182 Imgproc.line(sline ,pointthree[1],pointthree[2] ,new Scalar(0,0,255),2); 183 Imgproc.line(sline ,pointthree[0],pointthree[2] ,new Scalar(0,0,255),2); 184 Imgcodecs.imwrite("F:\\output\\cvRio-"+idx+".jpg", sline); 185 186 double[] ca = new double[2]; 187 double[] cb = new double[2]; 188 189 ca[0] = pointthree[1].x - pointthree[0].x; 190 ca[1] = pointthree[1].y - pointthree[0].y; 191 cb[0] = pointthree[2].x - pointthree[0].x; 192 cb[1] = pointthree[2].y - pointthree[0].y; 193 /* 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){ 194 return; 195 }*/ 196 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]))); 197 double ccw1; 198 if(ca[0]*cb[1] - ca[1]*cb[0] > 0) { 199 ccw1 = 0; 200 } else { 201 ccw1 = 1; 202 } 203 ca[0] = pointthree[0].x - pointthree[1].x; 204 ca[1] = pointthree[0].y - pointthree[1].y; 205 cb[0] = pointthree[2].x - pointthree[1].x; 206 cb[1] = pointthree[2].y - pointthree[1].y; 207 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]))); 208 double ccw2; 209 if(ca[0]*cb[1] - ca[1]*cb[0] > 0) { 210 ccw2 = 0; 211 }else { 212 ccw2 = 1; 213 } 214 215 ca[0] = pointthree[1].x - pointthree[2].x; 216 ca[1] = pointthree[1].y - pointthree[2].y; 217 cb[0] = pointthree[0].x - pointthree[2].x; 218 cb[1] = pointthree[0].y - pointthree[2].y; 219 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]))); 220 int ccw3; 221 if(ca[0]*cb[1] - ca[1]*cb[0] > 0) { 222 ccw3 = 0; 223 }else { 224 ccw3 = 1; 225 } 226 227 System.out.println("angle1:"+angle1+",angle2:"+angle2+",angle3:"+angle3); 228 if (Double.isNaN(angle1) || Double.isNaN(angle2) || Double.isNaN(angle3)){ 229 return; 230 } 231 232 Point[] poly= new Point[4]; 233 if(angle3>angle2 && angle3>angle1) 234 { 235 if(ccw3==1) 236 { 237 poly[1] = pointthree[1]; 238 poly[3] = pointthree[0]; 239 } 240 else 241 { 242 poly[1] = pointthree[0]; 243 poly[3] = pointthree[1]; 244 } 245 poly[0] = pointthree[2]; 246 Point temp = new Point(pointthree[0].x + pointthree[1].x - pointthree[2].x , pointthree[0].y + pointthree[1].y - pointthree[2].y ); 247 poly[2] = temp; 248 } else if(angle2>angle1 && angle2>angle3) 249 { 250 if(ccw2==1) 251 { 252 poly[1] = pointthree[0]; 253 poly[3] = pointthree[2]; 254 } 255 else 256 { 257 poly[1] = pointthree[2]; 258 poly[3] = pointthree[0]; 259 } 260 poly[0] = pointthree[1]; 261 Point temp = new Point(pointthree[0].x + pointthree[2].x - pointthree[1].x , pointthree[0].y + pointthree[2].y - pointthree[1].y ); 262 poly[2] = temp; 263 } else if(angle1>angle2 && angle1 > angle3) 264 { 265 if(ccw1==1) 266 { 267 poly[1] = pointthree[1]; 268 poly[3] = pointthree[2]; 269 } 270 else 271 { 272 poly[1] = pointthree[2]; 273 poly[3] = pointthree[1]; 274 } 275 poly[0] = pointthree[0]; 276 Point temp = new Point(pointthree[1].x + pointthree[2].x - pointthree[0].x , pointthree[1].y + pointthree[2].y - pointthree[0].y ); 277 poly[2] = temp; 278 } 279 280 Point[] trans=new Point[4]; 281 282 int temp =50; 283 trans[0] = new Point(0+temp,0+temp); 284 trans[1] = new Point(0+temp,100+temp); 285 trans[2] = new Point(100+temp,100+temp); 286 trans[3] = new Point(100+temp,0+temp); 287 288 double maxAngle = Math.max(angle3,Math.max(angle1,angle2)); 289 System.out.println(maxAngle); 290 if (maxAngle<75 || maxAngle>115){ /**二维码为直角,最大角过大或者过小都判断为不是二维码*/ 291 return; 292 } 293 294 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 295 Mat dst = new Mat(); 296 //计算变换结果 297 Imgproc.warpPerspective(src,dst ,perspectiveMmat,src.size(),Imgproc.INTER_LINEAR); 298 299 Rect roiArea = new Rect(0, 0, 200, 200); 300 Mat dstRoi = new Mat(dst, roiArea); 301 Imgcodecs.imwrite("F:\\output\\dstRoi-"+idx+".jpg", dstRoi); 302 } 303 304 public static BufferedImage toBufferedImage(Mat m) { 305 int type = BufferedImage.TYPE_BYTE_GRAY; 306 307 if (m.channels() > 1) { 308 type = BufferedImage.TYPE_3BYTE_BGR; 309 } 310 311 int bufferSize = m.channels() * m.cols() * m.rows(); 312 byte[] b = new byte[bufferSize]; 313 m.get(0, 0, b); // get all the pixels 314 BufferedImage image = new BufferedImage(m.cols(), m.rows(), type); 315 316 final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); 317 System.arraycopy(b, 0, targetPixels, 0, b.length); 318 319 return image; 320 } 321 322 public static Point centerCal(MatOfPoint matOfPoint){ 323 double centerx=0,centery=0; 324 int size = matOfPoint.cols(); 325 MatOfPoint2f mat2f = new MatOfPoint2f( matOfPoint.toArray() ); 326 RotatedRect rect = Imgproc.minAreaRect( mat2f ); 327 Point vertices[] = new Point[4]; 328 rect.points(vertices); 329 centerx = ((vertices[0].x + vertices[1].x)/2 + (vertices[2].x + vertices[3].x)/2)/2; 330 centery = ((vertices[0].y + vertices[1].y)/2 + (vertices[2].y + vertices[3].y)/2)/2; 331 Point point= new Point(centerx,centery); 332 return point; 333 } 334}

需要说明一下:

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

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )