@@ -228,7 +228,12 @@ | |||
<artifactId>javase</artifactId> | |||
<version>3.3.0</version> | |||
</dependency> | |||
<!-- hutool工具类 --> | |||
<dependency> | |||
<groupId>cn.hutool</groupId> | |||
<artifactId>hutool-all</artifactId> | |||
<version>4.6.1</version> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
@@ -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<MatOfPoint> contours = new Vector<MatOfPoint>(); | |||
//轮廓识别,查找外轮廓 | |||
Imgproc.findContours(edges, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE, new Point()); | |||
List<Point> listPoint = new ArrayList<>(); | |||
for(int i =0; i<contours.size();i++) { | |||
MatOfPoint2f newPoint = new MatOfPoint2f(contours.get(i).toArray()); | |||
// 周长,第1个参数是轮廓,第二个参数代表是否是闭环的图形 | |||
double peri = 0.01 * Imgproc.arcLength(newPoint, true); | |||
MatOfPoint2f approx = new MatOfPoint2f(); | |||
// approx.convertTo(approx, CvType.CV_32F); | |||
//近似轮廓逼近,然后通过获取多边形的所有定点,如果是四个定点,就代表是矩形 | |||
Imgproc.approxPolyDP(newPoint,approx, peri, true); | |||
//只考虑矩形,如果近似轮廓有4个点,我们就认为已经找到了该矩形。 | |||
if (approx.rows() == 4) { | |||
//通过reshape函数将4个点取出来(4行2列的矩阵) | |||
Mat points = approx.reshape(2, 4); | |||
System.out.println(points.dump()); | |||
double[] point1 = points.get(0, 0); | |||
double[] point2 = points.get(1, 0); | |||
double[] point3 = points.get(2, 0); | |||
double[] point4 = points.get(3, 0); | |||
//之前因为我们已经将图片进行了缩放,所以此处要将图片尺寸还原 | |||
listPoint.add(new Point(point1[0]/ratio,point1[1]/ratio)); | |||
listPoint.add(new Point(point2[0]/ratio,point2[1]/ratio)); | |||
listPoint.add(new Point(point3[0]/ratio,point3[1]/ratio)); | |||
listPoint.add(new Point(point4[0]/ratio,point4[1]/ratio)); | |||
for (Point d : listPoint) { | |||
System.out.println(d); | |||
} | |||
System.out.println("######################"); | |||
break; | |||
} | |||
} | |||
//绘制轮廓,注意是在缩放过的图片上绘制的,别在原图上画,肯定画的不对。 | |||
Imgproc.drawContours(dst, contours, -1, new Scalar(0, 255, 0), 2); | |||
Highgui.imwrite("d:\\test\\abc\\o3.png", dst); | |||
Mat resullt = fourPointTransform(orign, listPoint); | |||
Highgui.imwrite(destPath, resullt); | |||
// 方式2,回写页面图片流 | |||
try { | |||
byte[] imgebyte = OpenCVUtil.covertMat2Byte1(resullt); | |||
renderImage(response, imgebyte); | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
/** | |||
* py中imutils中经典的4点转换方法的java实现 | |||
* @author song.wang | |||
* @date 2019年8月20日 | |||
* @param source | |||
* @param listPoint | |||
* @return Mat | |||
* | |||
* 更新日志 | |||
* 2019年8月20日 song.wang 首次创建 | |||
*/ | |||
private static Mat fourPointTransform(Mat source,List<Point> listPoint) { | |||
//获得点的顺序 | |||
List<Point> 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<Point> | |||
* | |||
* 更新日志 | |||
* 2019年8月16日 song.wang 首次创建 | |||
*/ | |||
private static List<Point> orderPoints(List<Point> listPoint) { | |||
//python中有很多关于数组的函数处理如排序、比较、加减乘除等,在这里我们使用List进行操作 | |||
//如numpy.argsort;numpy.argmin;numpy.argmax;sum(axis = 1);diff(pts, axis = 1)等等,有兴趣的可以查阅相关资料 | |||
//四个点按照左上、右上、右下、左下组织返回 | |||
//直接在这里添加我们的排序规则,按照x坐标轴升序排列,小的放前面 | |||
Collections.sort(listPoint, new Comparator<Point>() { | |||
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<Point> newListPoint = new ArrayList<>(); | |||
newListPoint.add(top_left); | |||
newListPoint.add(top_right); | |||
newListPoint.add(bottom_right); | |||
newListPoint.add(bottom_left); | |||
return newListPoint; | |||
} | |||
} |
@@ -34,61 +34,61 @@ | |||
</bean> | |||
<!--dataSource--> | |||
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> | |||
<!-- 数据库基本信息配置 --> | |||
<!-- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> | |||
数据库基本信息配置 | |||
<property name="driverClassName" value="${driverClassName}" /> | |||
<property name="Url" value="${url}" /> | |||
<property name="username" value="${username}" /> | |||
<property name="password" value="${password}" /> | |||
<!-- 初始化连接数量 --> | |||
初始化连接数量 | |||
<property name="initialSize" value="${initialSize}" /> | |||
<!-- 最大并发连接数 --> | |||
最大并发连接数 | |||
<property name="maxActive" value="${maxActive}" /> | |||
<!-- 最小空闲连接数 --> | |||
最小空闲连接数 | |||
<property name="minIdle" value="${minIdle}" /> | |||
<!-- 配置获取连接等待超时的时间 --> | |||
配置获取连接等待超时的时间 | |||
<property name="maxWait" value="${maxWait}" /> | |||
<!-- 超过时间限制是否回收 --> | |||
超过时间限制是否回收 | |||
<property name="removeAbandoned" value="${removeAbandoned}" /> | |||
<!-- 超过时间限制多长; --> | |||
超过时间限制多长; | |||
<property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" /> | |||
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> | |||
配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 | |||
<property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" /> | |||
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> | |||
配置一个连接在池中最小生存的时间,单位是毫秒 | |||
<property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" /> | |||
<!-- 用来检测连接是否有效的sql,要求是一个查询语句 ||抛弃此种方式的连接检查--> | |||
<!-- <property name="validationQuery" value="${validationQuery}" /> --> | |||
<!-- 申请连接的时候检测 --> | |||
用来检测连接是否有效的sql,要求是一个查询语句 ||抛弃此种方式的连接检查 | |||
<property name="validationQuery" value="${validationQuery}" /> | |||
申请连接的时候检测 | |||
<property name="testWhileIdle" value="${testWhileIdle}" /> | |||
<!-- 申请连接时执行validationQuery检测连接是否有效,配置为true会降低性能 --> | |||
申请连接时执行validationQuery检测连接是否有效,配置为true会降低性能 | |||
<property name="testOnBorrow" value="${testOnBorrow}" /> | |||
<!-- 归还连接时执行validationQuery检测连接是否有效,配置为true会降低性能 --> | |||
归还连接时执行validationQuery检测连接是否有效,配置为true会降低性能 | |||
<property name="testOnReturn" value="${testOnReturn}" /> | |||
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 --> | |||
打开PSCache,并且指定每个连接上PSCache的大小 | |||
<property name="poolPreparedStatements" value="${poolPreparedStatements}" /> | |||
<property name="maxPoolPreparedStatementPerConnectionSize" value="${maxPoolPreparedStatementPerConnectionSize}" /> | |||
<!--属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: | |||
属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: | |||
监控统计用的filter:stat | |||
日志用的filter:log4j | |||
防御SQL注入的filter:wall --> | |||
防御SQL注入的filter:wall | |||
<property name="filters" value="${filters}" /> | |||
</bean> | |||
</bean> --> | |||
<!-- Hibernate对Jpa的实现 --> | |||
<bean id="hibernateJpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/> | |||
<!-- 配置 JPA 的 EntityManagerFactory --> | |||
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> | |||
<!-- 指定数据源 --> | |||
<!-- <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> | |||
指定数据源 | |||
<property name="dataSource" ref="dataSource"/> | |||
<!-- 指定Jpa持久化实现厂商类,这里以Hibernate为例 --> | |||
指定Jpa持久化实现厂商类,这里以Hibernate为例 | |||
<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter"/> | |||
<!-- 指定Entity实体类包路径 --> | |||
指定Entity实体类包路径 | |||
<property name="packagesToScan" value="com.acts.opencv"></property> | |||
<!-- 指定JPA属性;如Hibernate中指定是否显示SQL的是否显示、方言等 --> | |||
指定JPA属性;如Hibernate中指定是否显示SQL的是否显示、方言等 | |||
<property name="jpaProperties"> | |||
<props> | |||
<!-- <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop> --> | |||
<!-- <prop key="hibernate.hbm2ddl.auto">update</prop> --> | |||
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop> | |||
<prop key="hibernate.hbm2ddl.auto">update</prop> | |||
<prop key="hibernate.show_sql">true</prop> | |||
<prop key="hibernate.format_sql">false</prop> | |||
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop> | |||
@@ -99,20 +99,20 @@ | |||
</props> | |||
</property> | |||
<property name="sharedCacheMode" value="ENABLE_SELECTIVE"></property> | |||
</bean> | |||
</bean> --> | |||
<!-- 定义事务 --> | |||
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> | |||
<!-- <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> | |||
<property name="entityManagerFactory" ref="entityManagerFactory"/> | |||
</bean> | |||
</bean> --> | |||
<!-- 配置 Annotation 驱动,扫描@Transactional注解的类定义事务 --> | |||
<tx:annotation-driven transaction-manager="transactionManager"/> | |||
<!-- <tx:annotation-driven transaction-manager="transactionManager"/> | |||
<!-- 配置 SpringData --> | |||
配置 SpringData | |||
<jpa:repositories base-package="com.acts.opencv" entity-manager-factory-ref="entityManagerFactory"/> | |||
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> | |||
<property name="dataSource" ref="dataSource"/> | |||
</bean> | |||
</bean> --> | |||
<!-- redis 配置 | |||
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig" /> | |||
@@ -141,6 +141,7 @@ desired effect | |||
<li><a href="#view/base/contours.jsp"><i class="fa fa-circle-o"></i>轮廓</a></li> | |||
<li><a href="#view/base/findtemplate.jsp"><i class="fa fa-circle-o"></i>模板查找</a></li> | |||
<li><a href="#view/base/grayHistogram.jsp"><i class="fa fa-circle-o"></i>灰度直方图</a></li> | |||
<li><a href="#view/base/fourPointTransform.jsp"><i class="fa fa-circle-o"></i>4点转换矫正</a></li> | |||
</ul> | |||
</li> | |||
@@ -0,0 +1,112 @@ | |||
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> | |||
<%@include file="/module/include/common.jsp"%> | |||
<!DOCTYPE html> | |||
<html> | |||
<head> | |||
<sys:header title="首页" extLibs=""></sys:header> | |||
<link rel="stylesheet" href="${ctxStatic}/plugins/iCheck/all.css?t=${version}"> | |||
<link rel="stylesheet" href="${ctxStatic}/plugins/bootstrap-slider/slider.css"> | |||
<link rel="stylesheet" href="${ctxStatic}/plugins/iCheck/minimal/blue.css?t=${version}"> | |||
<script type="text/javascript"> | |||
$(function(){ | |||
var baseImageFile = "/statics/sourceimage/pic_transform.png" | |||
//var baseImageFile = "/statics/sourceimage/ada.png" | |||
var newImagePath = "/statics/distimage/pic_transform.png" | |||
$("#oldimg").attr("src",baseUrl+baseImageFile); | |||
//$("#newimg").attr("src",baseUrl+baseImageFile); | |||
//二值化 | |||
$("#handle").click(function(){ | |||
var imagefile = baseImageFile; | |||
//方式2,实时传输图片流的方式 | |||
var srcurl = ctxPath+"/base/picTransform?_" + $.now()+"&imagefile="+baseImageFile; | |||
$("#newimg").attr("src",srcurl); | |||
}); | |||
//重置 | |||
$("#reset").click(function(){ | |||
var baseImageFile = "/statics/sourceimage/pic_transform.png"; | |||
$("#oldimg").attr("src",baseUrl+baseImageFile); | |||
$("#newimg").attr("src",''); | |||
layer.msg('重置成功!', {icon: 1}); | |||
}); | |||
}); | |||
</script> | |||
</head> | |||
<body> | |||
<div class="box-body"> | |||
<div class="box-group" id="accordion"> | |||
<!-- we are adding the .panel class so bootstrap.js collapse plugin detects it --> | |||
<div class="panel box box-primary"> | |||
<div class="box-header with-border"> | |||
<h4 class="box-title"> | |||
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne"> | |||
python中经典的四点透视变换方法的java实现。轻松实现图像的透视变换 | |||
</a> | |||
</h4> | |||
</div> | |||
<div id="collapseOne" class="panel-collapse collapse in"><!--class="panel-collapse collapse in"中的 in 控制展开 --> | |||
<div class="box-body"> | |||
<h4>参考资料:<br> | |||
<a href="https://www.pyimagesearch.com/2014/08/25/4-point-opencv-getperspective-transform-example/"> | |||
https://www.pyimagesearch.com/2014/08/25/4-point-opencv-getperspective-transform-example/</a> <br> | |||
<a href="https://www.pyimagesearch.com/2016/03/21/ordering-coordinates-clockwise-with-python-and-opencv/"> | |||
https://www.pyimagesearch.com/2016/03/21/ordering-coordinates-clockwise-with-python-and-opencv/</a> <br> | |||
<a href="https://www.pyimagesearch.com/2014/09/01/build-kick-ass-mobile-document-scanner-just-5-minutes/"> | |||
https://www.pyimagesearch.com/2014/09/01/build-kick-ass-mobile-document-scanner-just-5-minutes/</a> <br> | |||
</h4> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- /.box-body --> | |||
<h4> 效果测试</h4> | |||
<div class="box-body"> | |||
<a class="btn btn-info" id="handle"><i class="fa fa-object-ungroup"></i>处理</a> | |||
<a class="btn btn-info" id="reset"><i class="fa fa-refresh"></i>重置</a> | |||
</div> | |||
<div class="row"> | |||
<div class="col-sm-8"> | |||
<div class="box box-primary"> | |||
<div class="box-header with-border"> | |||
<h3 class="box-title">原图</h3> | |||
<span class="label label-primary pull-right"><i class="fa fa-html5"></i></span> | |||
</div><!-- /.box-header --> | |||
<div class="box-body"> | |||
<p>原文件。</p> | |||
<img id="oldimg" src="" alt="原图" /> | |||
</div><!-- /.box-body --> | |||
</div><!-- /.box --> | |||
</div><!-- /.col --> | |||
<div class="col-sm-4"> | |||
<div class="box box-danger"> | |||
<div class="box-header with-border"> | |||
<h3 class="box-title">处理后的图片</h3> | |||
<span class="label label-danger pull-right"><i class="fa fa-database"></i></span> | |||
</div><!-- /.box-header --> | |||
<div class="box-body"> | |||
<p>点击处理按钮后,将显示处理后的文件。</p> | |||
<img id="newimg" src="" alt="处理后的图" /> | |||
</div><!-- /.box-body --> | |||
</div><!-- /.box --> | |||
</div><!-- /.col --> | |||
</div> | |||
</body> | |||
<script src="${ctxStatic}/plugins/bootstrap-slider/bootstrap-slider.js?t=${version}"></script> | |||
<script src="${ctxStatic}/plugins/iCheck/icheck.js?t=${version}"></script> | |||
</html> |