diff --git a/pom.xml b/pom.xml
index f809088..0b29940 100644
--- a/pom.xml
+++ b/pom.xml
@@ -228,7 +228,12 @@
javase
3.3.0
-
+
+
+ cn.hutool
+ hutool-all
+ 4.6.1
+
diff --git a/src/main/java/com/acts/opencv/base/BaseMethodController.java b/src/main/java/com/acts/opencv/base/BaseMethodController.java
index 4fdf482..1362a31 100644
--- a/src/main/java/com/acts/opencv/base/BaseMethodController.java
+++ b/src/main/java/com/acts/opencv/base/BaseMethodController.java
@@ -3,6 +3,8 @@ package com.acts.opencv.base;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -17,6 +19,7 @@ import org.opencv.core.Mat;
import org.opencv.core.MatOfFloat;
import org.opencv.core.MatOfInt;
import org.opencv.core.MatOfPoint;
+import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
@@ -42,6 +45,8 @@ import com.google.zxing.Result;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer;
+import cn.hutool.core.util.NumberUtil;
+
@Controller
@RequestMapping(value = "base")
@@ -1083,4 +1088,221 @@ public class BaseMethodController extends BaseController {
//
// }
+
+ public static void main(String[] args) {
+ System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
+
+ }
+
+
+ @RequestMapping(value = "picTransform")
+ public void picTransform(HttpServletResponse response, String imagefile) {
+ System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
+ logger.info("\n 图像转换开始");
+ //我们假设我们识别的图片如样例一样有明显的边界,那我们可以用边缘检测算法将真正有效区域抽离出来,
+ //以此来提高识别准确度和识别精度
+ //先进行边缘检测
+// String sourcePath = "d:\\test\\abc\\a.png";
+ String sourcePath = Constants.PATH + imagefile;
+ Mat source = Highgui.imread(sourcePath);
+// Mat destination = new Mat(source.rows(), source.cols(), source.type());
+ //复制一个source作为四点转换的原图,因为source在轮廓识别时会被覆盖,建议图像处理时都将原图复制一份,
+ //因为opencv的很多算法都会更改传入的soure图片,如果不注意可能就会导致各种异常。
+ Mat orign = source.clone();
+ //为了加速图像处理,以及使我们的边缘检测步骤更加准确,我们将扫描图像的大小调整为具有500像素的高度。
+ Mat dst = source.clone();
+ //缩放比例
+ double ratio = NumberUtil.div(500, orign.height());
+ System.out.println("----------"+ratio);
+ double width = ratio*orign.width();
+ Imgproc.resize(source, dst, new Size(width,500));
+ // 灰度化,加载为灰度图显示
+ Mat gray = dst.clone();
+ Imgproc.cvtColor(dst,gray,Imgproc.COLOR_BGR2GRAY);
+ Highgui.imwrite("d:\\test\\abc\\o1.png", gray);
+ //高斯滤波,去除杂点等干扰
+ Imgproc.GaussianBlur(gray,gray, new Size(5, 5), 0);
+ //canny边缘检测算法,经过canny算法或的图像会变成二值化效果
+ Mat edges = gray.clone();
+ Imgproc.Canny(gray,edges,75, 200);
+ Highgui.imwrite("d:\\test\\abc\\o2.png", edges);
+
+ String destPath = "d:\\test\\abc\\dst.png";
+ Mat hierarchy = new Mat(gray.rows(), gray.cols(), CvType.CV_8UC1, new Scalar(0));
+ Vector contours = new Vector();
+ //轮廓识别,查找外轮廓
+ Imgproc.findContours(edges, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE, new Point());
+ List listPoint = new ArrayList<>();
+ for(int i =0; i listPoint) {
+ //获得点的顺序
+ List newOrderList = orderPoints(listPoint);
+ for (Point point : newOrderList) {
+ System.out.println(point);
+ }
+ //计算新图像的宽度,它将是右下角和左下角x坐标之间或右上角和左上角x坐标之间的最大距离
+ //此处的顺序别搞错0,1,2,3依次是左上[0],右上[1],右下[2],左下[3]
+ Point leftTop = newOrderList.get(0);
+ Point rightTop = newOrderList.get(1);
+ Point rightBottom = newOrderList.get(2);
+ Point leftBottom = newOrderList.get(3);
+ double widthA = Math.sqrt(Math.pow(rightBottom.x-leftBottom.x, 2)
+ +Math.pow(rightBottom.y-leftBottom.y, 2));
+ double widthB = Math.sqrt(Math.pow(rightTop.x-leftTop.x, 2)
+ +Math.pow(rightTop.y-leftTop.y, 2));
+ int maxWidth = Math.max((int)widthA, (int)widthB);
+
+ //计算新图像的高度,这将是右上角和右下角y坐标或左上角和左下角y坐标之间的最大距离,
+ //这里用到的初中数学知识点和点的距离计算(x1,y1),(x2,y2)距离=√((x2-x1)^2+(y2-y1)^2)
+ double heightA = Math.sqrt(Math.pow(rightTop.x-rightBottom.x, 2)
+ +Math.pow(rightTop.y-rightBottom.y, 2));
+ double heightB = Math.sqrt(Math.pow(leftTop.x-leftBottom.x, 2)
+ +Math.pow(leftTop.y-leftBottom.y, 2));
+ int maxHeight = Math.max((int)heightA, (int)heightB);
+ System.out.println("宽度:"+maxWidth);
+ System.out.println("高度:"+maxHeight);
+ //现在我们指定目标图像的尺寸,构造目标点集以获得图像的“鸟瞰图”(即自上而下的视图),
+ //再次指定左上角,右上角的点,右下角和左下角的顺序
+ Point dstPoint1 = new Point(0,0);
+ Point dstPoint2 = new Point(maxWidth-1,0);
+ Point dstPoint3 = new Point(maxWidth-1,maxHeight-1);
+ Point dstPoint4 = new Point(0,maxHeight-1);
+
+ //计算透视变换矩阵rectMat原四顶点位置,dstMat目标顶点位置
+ MatOfPoint2f rectMat = new MatOfPoint2f(leftTop,rightTop,rightBottom,leftBottom);
+ MatOfPoint2f dstMat = new MatOfPoint2f(dstPoint1, dstPoint2, dstPoint3, dstPoint4);
+
+ //opencv透视转换方法
+ Mat transmtx = Imgproc.getPerspectiveTransform(rectMat, dstMat);
+ //注意定义的新图像宽高设置
+ Mat resultMat = Mat.zeros((int)maxHeight-1, (int)maxWidth-1, CvType.CV_8UC3);
+ Imgproc.warpPerspective(source, resultMat, transmtx, resultMat.size());
+ Highgui.imwrite("D:\\test\\abc\\t2.png", resultMat);
+
+ //返回矫正后的图像
+ return resultMat;
+ }
+
+ /**
+ * 4点排序,四个点按照左上、右上、右下、左下组织返回
+ * @author song.wang
+ * @date 2019年8月16日
+ * @param listPoint
+ * @return List
+ *
+ * 更新日志
+ * 2019年8月16日 song.wang 首次创建
+ */
+ private static List orderPoints(List listPoint) {
+ //python中有很多关于数组的函数处理如排序、比较、加减乘除等,在这里我们使用List进行操作
+ //如numpy.argsort;numpy.argmin;numpy.argmax;sum(axis = 1);diff(pts, axis = 1)等等,有兴趣的可以查阅相关资料
+ //四个点按照左上、右上、右下、左下组织返回
+ //直接在这里添加我们的排序规则,按照x坐标轴升序排列,小的放前面
+ Collections.sort(listPoint, new Comparator() {
+ public int compare(Point arg0, Point arg1) {
+ if(arg0.x < arg1.x){
+ return -1;
+ }else if (arg0.x> arg1.x){
+ return 1;
+ }else{
+ return 0;
+ }
+ }
+ });
+ //排序之后前2个点就是左侧的点,后2个点为右侧的点
+ //对比Y轴,y值小的是左上的点,y大的是左下的点
+ Point top_left = new Point();
+ Point bottom_left = new Point();
+ Point top_right = new Point();
+ Point bottom_right = new Point();
+
+ Point leftPoint1 = listPoint.get(0);
+ Point leftPoint2 = listPoint.get(1);
+ Point rightPoint1 = listPoint.get(2);
+ Point rightPoint2 = listPoint.get(3);
+ if(leftPoint1.y > leftPoint2.y){
+ top_left = leftPoint2;
+ bottom_left = leftPoint1;
+ }else{
+ top_left = leftPoint1;
+ bottom_left = leftPoint2;
+ }
+ //定位右侧的2个点右上和右下使用方法是毕达哥拉斯定理,就是勾股定理距离长的认为是右下角
+ //计算左上方点和右侧两个点的欧氏距离
+ //(y2-y1)^2+(x2-x1)^2 开根号
+ double rightLength1 = Math.sqrt(Math.pow((rightPoint1.y - top_left.y), 2)
+ + Math.pow((rightPoint1.x - top_left.x), 2));
+ double rightLength2 = Math.sqrt(Math.pow((rightPoint2.y - top_left.y), 2)
+ + Math.pow((rightPoint2.x - top_left.x), 2));
+ if(rightLength1>rightLength2){
+ //长度长的那个是右下角,短的为右上角;这个算法有一种情况会有可能出问题,比如倒梯形,但是在正常的俯角拍摄时不会出现这种情况
+ //还有一种方案是按照左侧的那种对比方案,根据y轴的高度判断。
+ top_right = rightPoint2;
+ bottom_right = rightPoint1;
+ }else{
+ top_right = rightPoint1;
+ bottom_right = rightPoint2;
+ }
+ //按照左上,右上,右下,左下的顺时针顺序排列,这点很重要,透视变换时根据这个顺序进行对应
+ List newListPoint = new ArrayList<>();
+ newListPoint.add(top_left);
+ newListPoint.add(top_right);
+ newListPoint.add(bottom_right);
+ newListPoint.add(bottom_left);
+
+ return newListPoint;
+ }
}
diff --git a/src/main/resources/spring-context.xml b/src/main/resources/spring-context.xml
index 455f557..76f82a7 100644
--- a/src/main/resources/spring-context.xml
+++ b/src/main/resources/spring-context.xml
@@ -34,61 +34,61 @@
-
-
+
+ 初始化连接数量
-
+ 最大并发连接数
-
+ 最小空闲连接数
-
+ 配置获取连接等待超时的时间
-
+ 超过时间限制是否回收
-
+ 超过时间限制多长;
-
+ 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
-
+ 配置一个连接在池中最小生存的时间,单位是毫秒
-
-
-
+ 用来检测连接是否有效的sql,要求是一个查询语句 ||抛弃此种方式的连接检查
+
+ 申请连接的时候检测
-
+ 申请连接时执行validationQuery检测连接是否有效,配置为true会降低性能
-
+ 归还连接时执行validationQuery检测连接是否有效,配置为true会降低性能
-
+ 打开PSCache,并且指定每个连接上PSCache的大小
-
+ 防御SQL注入的filter:wall
-
+ -->
-
-
+
+ 指定Jpa持久化实现厂商类,这里以Hibernate为例
-
+ 指定Entity实体类包路径
-
+ 指定JPA属性;如Hibernate中指定是否显示SQL的是否显示、方言等
-
-
+ org.hibernate.cfg.ImprovedNamingStrategy
+ update
true
false
org.hibernate.dialect.MySQL5InnoDBDialect
@@ -99,20 +99,20 @@
-
+ -->
-
+
-
+
+ 配置 SpringData
-
+ -->
+
+
+
+
+
+ 效果测试
+
+
+
+
+
+
+
+
+
+
+
原文件。
+
+
+
+
+
+
+
+
+
点击处理按钮后,将显示处理后的文件。
+
+
+
+
+
+
+
+
+