Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
I
image-backend
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
陈皓
image-backend
Commits
a0252026
Commit
a0252026
authored
Nov 28, 2023
by
陈皓
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
init
parent
7f1d964c
Show whitespace changes
Inline
Side-by-side
Showing
39 changed files
with
143 additions
and
4327 deletions
+143
-4327
file-server/src/main/resources/bootstrap.yml
+1
-1
gateway-server/src/main/resources/application.yml
+4
-0
gateway-server/src/main/resources/bootstrap.yml
+1
-1
imgproc-server/pom.xml
+45
-79
imgproc-server/src/main/java/com/zq/imgproc/ImgprocApplication.java
+6
-2
imgproc-server/src/main/java/com/zq/imgproc/config/InitConfig.java
+0
-26
imgproc-server/src/main/java/com/zq/imgproc/constant/Threshold.java
+0
-45
imgproc-server/src/main/java/com/zq/imgproc/controller/ApiController.java
+0
-162
imgproc-server/src/main/java/com/zq/imgproc/controller/FileController.java
+0
-51
imgproc-server/src/main/java/com/zq/imgproc/controller/ImgProcController.java
+0
-211
imgproc-server/src/main/java/com/zq/imgproc/exception/BusinessException.java
+0
-32
imgproc-server/src/main/java/com/zq/imgproc/server/ImgProcService.java
+0
-657
imgproc-server/src/main/java/com/zq/imgproc/utils/AssertUtils.java
+0
-279
imgproc-server/src/main/java/com/zq/imgproc/utils/BendUtil.java
+0
-82
imgproc-server/src/main/java/com/zq/imgproc/utils/DecompressUtil.java
+0
-154
imgproc-server/src/main/java/com/zq/imgproc/utils/DemoUtil.java
+0
-64
imgproc-server/src/main/java/com/zq/imgproc/utils/Deskew.java
+0
-165
imgproc-server/src/main/java/com/zq/imgproc/utils/ImageCorrectionUtil.java
+0
-168
imgproc-server/src/main/java/com/zq/imgproc/utils/ImageUtil.java
+0
-561
imgproc-server/src/main/java/com/zq/imgproc/utils/RemoveBlackUtil.java
+0
-210
imgproc-server/src/main/java/com/zq/imgproc/utils/RemoveBlackUtil2.java
+0
-142
imgproc-server/src/main/java/com/zq/imgproc/utils/ResultVo.java
+0
-89
imgproc-server/src/main/java/com/zq/imgproc/utils/Test2.java
+0
-170
imgproc-server/src/main/java/com/zq/imgproc/utils/TestUtil.java
+0
-166
imgproc-server/src/main/java/com/zq/imgproc/utils/UploadUtils.java
+0
-45
imgproc-server/src/main/java/com/zq/imgproc/utils/UuidUtils.java
+0
-61
imgproc-server/src/main/java/com/zq/imgproc/utils/ValidateUtil.java
+0
-320
imgproc-server/src/main/java/com/zq/imgproc/vo/BendResult.java
+0
-39
imgproc-server/src/main/java/com/zq/imgproc/vo/CompressReq.java
+0
-22
imgproc-server/src/main/java/com/zq/imgproc/vo/DetectionDTO.java
+0
-43
imgproc-server/src/main/java/com/zq/imgproc/vo/DetectionResVo.java
+0
-42
imgproc-server/src/main/java/com/zq/imgproc/vo/DetectionVO.java
+0
-47
imgproc-server/src/main/java/com/zq/imgproc/vo/ImgVO.java
+0
-30
imgproc-server/src/main/java/com/zq/imgproc/vo/OptimizationReq.java
+0
-20
imgproc-server/src/main/java/com/zq/imgproc/vo/OptimizationVO.java
+0
-56
imgproc-server/src/main/java/com/zq/imgproc/vo/RotateReq.java
+0
-31
imgproc-server/src/main/java/com/zq/imgproc/vo/SettingVO.java
+0
-32
imgproc-server/src/main/resources/application.yml
+68
-18
imgproc-server/src/main/resources/bootstrap.yml
+18
-4
No files found.
file-server/src/main/resources/bootstrap.yml
View file @
a0252026
...
@@ -9,7 +9,7 @@ spring:
...
@@ -9,7 +9,7 @@ spring:
enabled
:
true
enabled
:
true
service-id
:
CONFIG-SERVER
service-id
:
CONFIG-SERVER
username
:
admin
username
:
admin
password
:
123456
password
:
GXfy2022
eureka
:
eureka
:
instance
:
instance
:
...
...
gateway-server/src/main/resources/application.yml
View file @
a0252026
...
@@ -37,4 +37,8 @@ spring:
...
@@ -37,4 +37,8 @@ spring:
uri
:
lb://USER-SERVER
uri
:
lb://USER-SERVER
predicates
:
predicates
:
-
Path=/user/**
-
Path=/user/**
-
id
:
imgproc
uri
:
lb://IMGPROC-SERVER
predicates
:
-
Path=/imgproc/**
gateway-server/src/main/resources/bootstrap.yml
View file @
a0252026
...
@@ -9,7 +9,7 @@ spring:
...
@@ -9,7 +9,7 @@ spring:
enabled
:
true
enabled
:
true
service-id
:
CONFIG-SERVER
service-id
:
CONFIG-SERVER
username
:
admin
username
:
admin
password
:
123456
password
:
GXfy2022
eureka
:
eureka
:
instance
:
instance
:
...
...
imgproc-server/pom.xml
View file @
a0252026
...
@@ -2,30 +2,42 @@
...
@@ -2,30 +2,42 @@
<project
xmlns=
"http://maven.apache.org/POM/4.0.0"
<project
xmlns=
"http://maven.apache.org/POM/4.0.0"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
xsi:schemaLocation=
"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>
4.0.0
</modelVersion>
<parent>
<parent>
<groupId>
com.zq
</groupId>
<groupId>
com.zq
</groupId>
<artifactId>
image-backend
</artifactId>
<artifactId>
image-backend
</artifactId>
<version>
1.0.0
</version>
<version>
1.0.0
</version>
</parent>
</parent>
<modelVersion>
4.0.0
</modelVersion>
<artifactId>
imgproc-server
</artifactId>
<artifactId>
imgproc-server
</artifactId>
<version>
1.0.0
</version>
<name>
imgproc-server
</name>
<description>
imgproc-server
</description>
<properties>
<properties>
<java.version>
1.8
</java.version>
<maven.compiler.source>
8
</maven.compiler.source>
<maven.compiler.target>
8
</maven.compiler.target>
<project.build.sourceEncoding>
UTF-8
</project.build.sourceEncoding>
<project.build.sourceEncoding>
UTF-8
</project.build.sourceEncoding>
<project.reporting.outputEncoding>
UTF-8
</project.reporting.outputEncoding>
<spring-boot.version>
2.4.2
</spring-boot.version>
</properties>
</properties>
<dependencies>
<dependencies>
<dependency>
<dependency>
<groupId>
com.zq
</groupId>
<artifactId>
img-common-utils
</artifactId>
<version>
1.0.0
</version>
</dependency>
<dependency>
<groupId>
com.zq
</groupId>
<artifactId>
logging-server
</artifactId>
<version>
1.0.0
</version>
</dependency>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-netflix-eureka-client
</artifactId>
<artifactId>
spring-cloud-starter-netflix-eureka-client
</artifactId>
</dependency>
</dependency>
<!--Spring boot 安全框架-->
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-security
</artifactId>
</dependency>
<!-- 连接配置中心 -->
<dependency>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-config
</artifactId>
<artifactId>
spring-cloud-starter-config
</artifactId>
...
@@ -41,86 +53,33 @@
...
@@ -41,86 +53,33 @@
<artifactId>
spring-boot-starter-test
</artifactId>
<artifactId>
spring-boot-starter-test
</artifactId>
<scope>
test
</scope>
<scope>
test
</scope>
</dependency>
</dependency>
<!-- 远程调用cloud feign -->
<dependency>
<dependency>
<groupId>
org.apache.commons
</groupId>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
commons-io
</artifactId>
<artifactId>
spring-cloud-starter-openfeign
</artifactId>
<version>
1.3.2
</version>
</dependency>
<!-- OpenCv(Linux) -->
<!-- mvn install:install-file -Dfile=./src/main/resources/lib/opencv-460.jar -DgroupId=org.opencv -DartifactId=opencv -Dversion=4.6.0 -Dpackaging=jar -->
<dependency>
<groupId>
org.opencv
</groupId>
<artifactId>
opencv
</artifactId>
<version>
4.6.0
</version>
</dependency>
<!-- Webp图片格式支持 -->
<dependency>
<groupId>
org.sejda.imageio
</groupId>
<artifactId>
webp-imageio
</artifactId>
<version>
0.1.6
</version>
</dependency>
<!-- JPEG图片格式支持 -->
<dependency>
<groupId>
com.twelvemonkeys.imageio
</groupId>
<artifactId>
imageio-jpeg
</artifactId>
<version>
3.9.4
</version>
</dependency>
<!-- TIFF图片格式支持 -->
<dependency>
<groupId>
com.twelvemonkeys.imageio
</groupId>
<artifactId>
imageio-tiff
</artifactId>
<version>
3.9.4
</version>
</dependency>
<dependency>
<groupId>
org.apache.commons
</groupId>
<artifactId>
commons-imaging
</artifactId>
<version>
1.0-alpha3
</version>
</dependency>
<dependency>
<groupId>
org.apache.commons
</groupId>
<artifactId>
commons-compress
</artifactId>
<version>
1.22
</version>
</dependency>
</dependency>
<!--Spring boot Redis-->
<dependency>
<dependency>
<groupId>
org.tukaani
</groupId>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
xz
</artifactId>
<artifactId>
spring-boot-starter-data-redis
</artifactId>
<version>
1.9
</version>
</dependency>
</dependency>
<!--Mybatis plus-->
<dependency>
<dependency>
<groupId>
c
n.hutool
</groupId>
<groupId>
c
om.baomidou
</groupId>
<artifactId>
hutool-all
</artifactId>
<artifactId>
mybatis-plus-boot-starter
</artifactId>
<version>
5.8.19
</version>
<version>
${mybatis.plus.version}
</version>
</dependency>
</dependency>
<!-- druid数据源驱动 -->
<dependency>
<dependency>
<groupId>
com.alibaba
</groupId>
<groupId>
com.alibaba
</groupId>
<artifactId>
easyexcel
</artifactId>
<artifactId>
druid-spring-boot-starter
</artifactId>
<version>
3.3.2
</version>
<version>
${alibaba.druid.version}
</version>
</dependency>
</dependency>
<!-- mysql -->
<dependency>
<dependency>
<groupId>
com.drewnoakes
</groupId>
<groupId>
mysql
</groupId>
<artifactId>
metadata-extractor
</artifactId>
<artifactId>
mysql-connector-java
</artifactId>
<version>
2.18.0
</version>
<scope>
runtime
</scope>
</dependency>
<dependency>
<groupId>
org.projectlombok
</groupId>
<artifactId>
lombok
</artifactId>
<version>
1.18.28
</version>
</dependency>
<dependency>
<groupId>
io.springfox
</groupId>
<artifactId>
springfox-swagger2
</artifactId>
<version>
2.9.2
</version>
</dependency>
<dependency>
<groupId>
io.springfox
</groupId>
<artifactId>
springfox-swagger-ui
</artifactId>
<version>
2.9.2
</version>
</dependency>
<!-- guava -->
<dependency>
<groupId>
com.google.guava
</groupId>
<artifactId>
guava
</artifactId>
<version>
32.1.3-jre
</version>
</dependency>
</dependency>
</dependencies>
</dependencies>
...
@@ -129,16 +88,22 @@
...
@@ -129,16 +88,22 @@
<plugin>
<plugin>
<groupId>
org.springframework.boot
</groupId>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
<configuration>
<includeSystemScope>
true
</includeSystemScope>
</configuration>
</plugin>
</plugin>
</plugins>
</plugins>
<!-- 使用@profiles.active@需要添加以下内容 -->
<!-- 使用@profiles.active@需要添加以下内容 -->
<resources>
<resources>
<resource>
<resource>
<directory>
src/main/resources
</directory>
<directory>
src/main/resources
</directory>
<excludes>
<exclude>
**/*.jar
</exclude>
</excludes>
<!--开启过滤,用指定的参数替换directory下的文件中的参数-->
<!--开启过滤,用指定的参数替换directory下的文件中的参数-->
<filtering>
true
</filtering>
<filtering>
true
</filtering>
</resource>
</resource>
</resources>
</resources>
</build>
</build>
</project>
</project>
\ No newline at end of file
imgproc-server/src/main/java/com/zq/imgproc/ImgprocApplication.java
View file @
a0252026
package
com
.
zq
.
imgproc
;
package
com
.
zq
.
imgproc
;
import
org.mybatis.spring.annotation.MapperScan
;
import
org.springframework.boot.SpringApplication
;
import
org.springframework.boot.SpringApplication
;
import
org.springframework.boot.autoconfigure.SpringBootApplication
;
import
org.springframework.boot.autoconfigure.SpringBootApplication
;
import
org.springframework.cloud.client.discovery.EnableDiscoveryClient
;
import
org.springframework.cloud.client.discovery.EnableDiscoveryClient
;
import
org.springframework.cloud.openfeign.EnableFeignClients
;
/**
/**
* @author
wilmiam
* @author
chenhao
* @since 2022/10/13 11:11
* @since 2022/10/13 11:11
*/
*/
@EnableFeignClients
@MapperScan
({
"com.zq.imgproc.dao"
,
"com.zq.logging.mapper"
})
@EnableDiscoveryClient
@EnableDiscoveryClient
@SpringBootApplication
@SpringBootApplication
(
scanBasePackages
=
{
"com.zq.imgproc"
,
"com.zq.logging"
,
"com.zq.common.config"
})
public
class
ImgprocApplication
{
public
class
ImgprocApplication
{
public
static
void
main
(
String
[]
args
)
{
public
static
void
main
(
String
[]
args
)
{
...
...
imgproc-server/src/main/java/com/zq/imgproc/config/InitConfig.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
config
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.context.annotation.Configuration
;
import
javax.annotation.PostConstruct
;
/**
* @author wilmiam
* @since 2023/8/11 10:30
*/
@Slf4j
@Configuration
public
class
InitConfig
{
@Value
(
"${imgconfig.opencv}"
)
String
opencvUrl
;
@PostConstruct
public
void
asposeRegister
()
{
System
.
load
(
opencvUrl
);
log
.
info
(
">> opencv 加载成功..."
);
}
}
imgproc-server/src/main/java/com/zq/imgproc/constant/Threshold.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
constant
;
import
lombok.Getter
;
/**
* <p>
* 常用的阈值
* </P>
*
* @author chenhao
* @since 2023/11/26
*/
@Getter
public
enum
Threshold
{
/**
* 亮度过低阈值
*/
START_VALUE
(
"img-start"
,
75
),
/**
* 亮度过低阈值
*/
END_VALUE
(
"img-end"
,
175
),
/**
* 清晰度阈值
*/
CLARITY
(
"img-clarity"
,
2.5
),
/**
* 弯曲度阈值
*/
BEND
(
"img-clarity"
,
2.5
);
private
final
String
key
;
private
final
double
value
;
Threshold
(
String
key
,
double
value
)
{
this
.
key
=
key
;
this
.
value
=
value
;
}
}
imgproc-server/src/main/java/com/zq/imgproc/controller/ApiController.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
controller
;
import
cn.hutool.core.codec.Base64Encoder
;
import
cn.hutool.core.io.FileUtil
;
import
cn.hutool.http.HttpUtil
;
import
cn.hutool.json.JSONObject
;
import
cn.hutool.json.JSONUtil
;
import
com.zq.imgproc.server.ImgProcService
;
import
com.zq.imgproc.utils.*
;
import
com.zq.imgproc.vo.DetectionVO
;
import
com.zq.imgproc.vo.OptimizationReq
;
import
com.zq.imgproc.vo.OptimizationVO
;
import
io.swagger.annotations.ApiOperation
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.web.bind.annotation.*
;
import
org.springframework.web.multipart.MultipartFile
;
import
java.io.IOException
;
import
java.text.SimpleDateFormat
;
import
java.util.Date
;
import
java.util.HashMap
;
/**
* <p>
* 图片处理API
* </p>
*
* @author chenhao
* @since 2023/3/18 9:32
*/
@io
.
swagger
.
annotations
.
Api
(
tags
=
"图片处理API"
)
@RequestMapping
(
"/imgproc/v1"
)
@RestController
public
class
ApiController
{
@Value
(
"${imgconfig.opencv}"
)
String
opencvUrl
;
@Value
(
"${imgconfig.deskew}"
)
String
deskewUrl
;
private
final
ImgProcService
service
;
@Autowired
public
ApiController
(
ImgProcService
service
)
{
this
.
service
=
service
;
}
@ApiOperation
(
"测试"
)
@PostMapping
(
"/ping"
)
public
ResultVo
<?>
ping
()
{
return
ResultVo
.
success
(
"测试成功"
);
}
@ApiOperation
(
"图片检测"
)
@PostMapping
(
"/detection"
)
public
ResultVo
<
DetectionVO
>
detection
(
@RequestBody
OptimizationReq
req
)
throws
Exception
{
AssertUtils
.
hasText
(
req
.
getFileContent
(),
"缺少文件内容"
);
AssertUtils
.
hasText
(
req
.
getFilename
(),
"缺少文件名"
);
return
ResultVo
.
success
(
service
.
detection2
(
req
));
}
@ApiOperation
(
"图片校正(deskew工具)"
)
@PostMapping
(
"/imageCorrection"
)
public
ResultVo
<?>
imageCorrection
(
@RequestPart
MultipartFile
file
)
{
if
(
file
.
isEmpty
())
{
return
ResultVo
.
fail
(
"文件不能为空"
);
}
String
imgPath
=
UploadUtils
.
saveTempFile
(
file
,
"api"
);
String
ext
=
FileUtil
.
extName
(
file
.
getOriginalFilename
());
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/file/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"."
+
ext
;
// 图片矫正
ImageCorrectionUtil
.
deskew
(
imgPath
,
savePath
,
deskewUrl
,
false
);
return
ResultVo
.
success
(
Base64Encoder
.
encode
(
FileUtil
.
readBytes
(
savePath
)));
}
@ApiOperation
(
"去黑边"
)
@PostMapping
(
"/removeBlack"
)
public
ResultVo
<?>
removeBlack
(
@RequestPart
MultipartFile
file
)
{
if
(
file
.
isEmpty
())
{
return
ResultVo
.
fail
(
"文件不能为空"
);
}
System
.
load
(
opencvUrl
);
String
imgPath
=
UploadUtils
.
saveTempFile
(
file
,
"api"
);
String
ext
=
FileUtil
.
extName
(
file
.
getOriginalFilename
());
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/file/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"."
+
ext
;
// 去黑边
RemoveBlackUtil2
.
remove
(
imgPath
,
savePath
);
return
ResultVo
.
success
(
Base64Encoder
.
encode
(
savePath
));
}
@ApiOperation
(
"图片灰度化"
)
@PostMapping
(
"/gray"
)
public
ResultVo
<?>
gray
(
@RequestPart
MultipartFile
file
)
{
if
(
file
.
isEmpty
())
{
return
ResultVo
.
fail
(
"文件不能为空"
);
}
System
.
load
(
opencvUrl
);
String
imgPath
=
UploadUtils
.
saveTempFile
(
file
,
"api"
);
String
ext
=
FileUtil
.
extName
(
file
.
getOriginalFilename
());
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/file/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"."
+
ext
;
// 灰度化
ImageUtil
.
gray
(
imgPath
,
savePath
);
return
ResultVo
.
success
(
Base64Encoder
.
encode
(
FileUtil
.
readBytes
(
savePath
)));
}
@ApiOperation
(
"图片边缘检测"
)
@PostMapping
(
"/canny"
)
public
ResultVo
<?>
canny
(
@RequestPart
MultipartFile
file
)
{
if
(
file
.
isEmpty
())
{
return
ResultVo
.
fail
(
"文件不能为空"
);
}
System
.
load
(
opencvUrl
);
String
imgPath
=
UploadUtils
.
saveTempFile
(
file
,
"api"
);
String
ext
=
FileUtil
.
extName
(
file
.
getOriginalFilename
());
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/file/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"."
+
ext
;
// 边缘检测
ImageUtil
.
canny
(
imgPath
,
savePath
);
return
ResultVo
.
success
(
Base64Encoder
.
encode
(
FileUtil
.
readBytes
(
savePath
)));
}
@ApiOperation
(
"图片弯曲矫正"
)
@PostMapping
(
"/correct"
)
public
ResultVo
<?>
correct
(
@RequestPart
MultipartFile
file
)
{
if
(
file
.
isEmpty
())
{
return
ResultVo
.
fail
(
"文件不能为空"
);
}
String
imgPath
=
UploadUtils
.
saveTempFile
(
file
,
"api"
);
// 图片弯曲矫正
HashMap
<
String
,
Object
>
map
=
new
HashMap
<>();
map
.
put
(
"file"
,
FileUtil
.
file
(
imgPath
));
String
response
=
HttpUtil
.
post
(
"http://ddns.gxmailu.com:18888/api/correct"
,
map
);
JSONObject
res
=
JSONUtil
.
parseObj
(
response
);
if
(
res
.
get
(
"success"
,
Boolean
.
class
))
{
String
base64
=
res
.
get
(
"data"
,
String
.
class
);
return
ResultVo
.
success
(
base64
);
}
else
{
return
ResultVo
.
fail
(
res
.
get
(
"msg"
,
String
.
class
));
}
}
@ApiOperation
(
"图片优化"
)
@PostMapping
(
"/imageOptimization"
)
public
ResultVo
<
OptimizationVO
>
imageOptimization
(
@RequestBody
OptimizationReq
req
)
throws
IOException
{
AssertUtils
.
hasText
(
req
.
getFileContent
(),
"缺少文件内容"
);
AssertUtils
.
hasText
(
req
.
getFilename
(),
"缺少文件名"
);
return
ResultVo
.
success
(
service
.
optimization
(
req
));
}
}
imgproc-server/src/main/java/com/zq/imgproc/controller/FileController.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
controller
;
import
cn.hutool.core.util.URLUtil
;
import
com.zq.imgproc.utils.AssertUtils
;
import
io.swagger.annotations.Api
;
import
org.springframework.core.io.FileSystemResource
;
import
org.springframework.core.io.Resource
;
import
org.springframework.http.ContentDisposition
;
import
org.springframework.http.HttpHeaders
;
import
org.springframework.http.MediaType
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.web.bind.annotation.CrossOrigin
;
import
org.springframework.web.bind.annotation.GetMapping
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
javax.servlet.http.HttpServletRequest
;
import
java.io.File
;
/**
* <p>
*
* </p>
*
* @author zq
* @since 2023/11/15
*/
@Api
(
tags
=
"访问文件"
)
@RestController
@CrossOrigin
@RequestMapping
(
"/"
)
public
class
FileController
{
@GetMapping
(
value
=
"/file/**"
)
public
ResponseEntity
<
Resource
>
archive
(
HttpServletRequest
request
)
{
String
requestURI
=
request
.
getRequestURI
();
requestURI
=
URLUtil
.
decode
(
requestURI
);
File
file
=
new
File
(
requestURI
);
AssertUtils
.
isTrue
(
file
.
exists
(),
"访问文件不存在"
);
String
contentDisposition
=
ContentDisposition
.
builder
(
"attachment"
)
.
filename
(
requestURI
)
.
build
().
toString
();
return
ResponseEntity
.
ok
()
.
header
(
HttpHeaders
.
CONTENT_DISPOSITION
,
contentDisposition
)
.
contentType
(
MediaType
.
APPLICATION_OCTET_STREAM
)
.
body
(
new
FileSystemResource
(
file
));
}
}
imgproc-server/src/main/java/com/zq/imgproc/controller/ImgProcController.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
controller
;
import
cn.hutool.core.io.FileUtil
;
import
cn.hutool.extra.servlet.ServletUtil
;
import
com.google.common.cache.Cache
;
import
com.google.common.cache.CacheBuilder
;
import
com.zq.imgproc.constant.Threshold
;
import
com.zq.imgproc.server.ImgProcService
;
import
com.zq.imgproc.utils.AssertUtils
;
import
com.zq.imgproc.utils.DecompressUtil
;
import
com.zq.imgproc.utils.ImageUtil
;
import
com.zq.imgproc.utils.ResultVo
;
import
com.zq.imgproc.vo.*
;
import
io.swagger.annotations.Api
;
import
io.swagger.annotations.ApiOperation
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.core.io.Resource
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.web.bind.annotation.*
;
import
org.springframework.web.multipart.MultipartFile
;
import
javax.servlet.http.HttpServletResponse
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.nio.charset.StandardCharsets
;
import
java.time.Duration
;
import
java.util.ArrayList
;
import
java.util.List
;
/**
* <p>
* 图片检测接口
* </p>
*
* @author chenhao
* @since 2022/10/24 14:58
*/
@Api
(
tags
=
"图片检测接口"
)
@RequestMapping
(
"/imgproc"
)
@RestController
public
class
ImgProcController
{
/**
* 普通的缓存 Cache
*/
private
final
Cache
<
String
,
Double
>
cache
=
CacheBuilder
.
newBuilder
()
.
expireAfterAccess
(
Duration
.
ofMinutes
(
30
))
.
expireAfterWrite
(
Duration
.
ofHours
(
48
))
.
maximumSize
(
1024
)
.
concurrencyLevel
(
4
)
.
initialCapacity
(
1024
)
.
build
();
private
final
ImgProcService
service
;
@Autowired
public
ImgProcController
(
ImgProcService
service
)
{
this
.
service
=
service
;
}
@ApiOperation
(
"图片检测"
)
@PostMapping
(
"/detection"
)
public
ResultVo
<
DetectionResVo
>
detection
(
@RequestPart
MultipartFile
file
)
throws
Exception
{
AssertUtils
.
notNull
(
file
,
"图片不能为空"
);
return
ResultVo
.
success
(
service
.
detection
(
file
));
}
@ApiOperation
(
"文件解压"
)
@PostMapping
(
"/decompress"
)
public
ResultVo
<?>
decompress
(
@RequestPart
MultipartFile
[]
file
)
throws
Exception
{
List
<
ImgVO
>
imgs
=
new
ArrayList
<>();
for
(
MultipartFile
one
:
file
)
{
if
(
DecompressUtil
.
isCompress
(
one
))
{
String
path
=
DecompressUtil
.
decompress
(
one
);
AssertUtils
.
hasText
(
path
,
"解压失败!"
);
imgs
.
addAll
(
DecompressUtil
.
loopFiles
(
path
));
}
else
{
String
url
=
ImageUtil
.
saveTempFile
(
one
);
imgs
.
add
(
ImgVO
.
builder
().
fileName
(
one
.
getOriginalFilename
()).
url
(
url
).
build
());
}
}
return
ResultVo
.
success
(
imgs
);
}
@ApiOperation
(
"文件压缩(zip)"
)
@PostMapping
(
"/compressZip"
)
public
void
compressZip
(
@RequestBody
CompressReq
req
,
HttpServletResponse
response
)
{
String
path
=
DecompressUtil
.
compress
(
req
.
getPathList
());
InputStream
inputStream
=
FileUtil
.
getInputStream
(
path
);
String
name
=
new
String
(
req
.
getName
().
getBytes
(),
StandardCharsets
.
ISO_8859_1
);
ServletUtil
.
write
(
response
,
inputStream
,
"application/octet-stream;charset=utf-8"
,
name
);
}
@ApiOperation
(
"图片检测"
)
@PostMapping
(
"/detectionByPath"
)
public
ResultVo
<?>
detection
(
@RequestBody
List
<
ImgVO
>
pathList
)
throws
Exception
{
if
(
pathList
.
isEmpty
())
{
return
ResultVo
.
success
();
}
return
ResultVo
.
success
(
service
.
detection
(
pathList
));
}
@ApiOperation
(
"导出检测结果"
)
@PostMapping
(
"/getDetection"
)
public
ResponseEntity
<
Resource
>
getDetection
(
@RequestBody
List
<
ImgVO
>
pathList
)
throws
Exception
{
return
service
.
getDetection
(
pathList
);
}
@ApiOperation
(
"图片校正(deskew工具)"
)
@PostMapping
(
"/imageCorrection"
)
public
ResultVo
<?>
imageCorrection
(
@RequestBody
List
<
ImgVO
>
pathList
)
{
if
(
pathList
.
isEmpty
())
{
return
ResultVo
.
success
();
}
return
ResultVo
.
success
(
service
.
deskew
(
pathList
));
}
@ApiOperation
(
"去黑边"
)
@PostMapping
(
"/removeBlack"
)
public
ResultVo
<?>
removeBlack
(
@RequestBody
List
<
ImgVO
>
pathList
)
{
if
(
pathList
.
isEmpty
())
{
return
ResultVo
.
success
();
}
return
ResultVo
.
success
(
service
.
removeBlack
(
pathList
));
}
@ApiOperation
(
"获取配置"
)
@GetMapping
(
"/getSetting"
)
public
ResultVo
<?>
getSetting
()
{
Double
temp1
=
cache
.
getIfPresent
(
Threshold
.
START_VALUE
.
getKey
());
Double
temp2
=
cache
.
getIfPresent
(
Threshold
.
END_VALUE
.
getKey
());
Double
temp3
=
cache
.
getIfPresent
(
Threshold
.
CLARITY
.
getKey
());
double
start
=
temp1
==
null
?
Threshold
.
START_VALUE
.
getValue
()
:
temp1
;
double
end
=
temp2
==
null
?
Threshold
.
END_VALUE
.
getValue
()
:
temp2
;
double
clarity
=
temp3
==
null
?
Threshold
.
CLARITY
.
getValue
()
:
temp3
;
return
ResultVo
.
success
(
SettingVO
.
builder
().
startValue
(
start
).
endValue
(
end
).
clarity
(
clarity
).
build
());
}
@ApiOperation
(
"设置配置"
)
@PostMapping
(
"/setSetting"
)
public
ResultVo
<?>
setSetting
(
@RequestBody
SettingVO
setting
)
{
if
(
setting
==
null
)
{
return
ResultVo
.
fail
(
"配置不能为空!"
);
}
cache
.
put
(
Threshold
.
START_VALUE
.
getKey
(),
setting
.
getStartValue
());
cache
.
put
(
Threshold
.
END_VALUE
.
getKey
(),
setting
.
getEndValue
());
cache
.
put
(
Threshold
.
CLARITY
.
getKey
(),
setting
.
getClarity
());
return
ResultVo
.
success
();
}
@ApiOperation
(
"图片旋转"
)
@PostMapping
(
"/rotate"
)
public
ResultVo
<?>
rotate
(
@RequestBody
RotateReq
req
)
{
if
(
req
==
null
)
{
return
ResultVo
.
success
();
}
return
ResultVo
.
success
(
service
.
rotate
(
req
));
}
@ApiOperation
(
"图片灰度化"
)
@PostMapping
(
"/gray"
)
public
ResultVo
<?>
rotate
(
@RequestBody
List
<
ImgVO
>
pathList
)
{
if
(
pathList
.
isEmpty
())
{
return
ResultVo
.
success
();
}
return
ResultVo
.
success
(
service
.
gray
(
pathList
));
}
@ApiOperation
(
"图片边缘检测"
)
@PostMapping
(
"/canny"
)
public
ResultVo
<?>
canny
(
@RequestBody
List
<
ImgVO
>
pathList
)
{
if
(
pathList
.
isEmpty
())
{
return
ResultVo
.
success
();
}
return
ResultVo
.
success
(
service
.
canny
(
pathList
));
}
@ApiOperation
(
"图片上传"
)
@PostMapping
(
"/upload"
)
public
ResultVo
<?>
upload
(
@RequestPart
MultipartFile
file
)
{
if
(
file
.
isEmpty
())
{
return
ResultVo
.
fail
(
"文件不能为空"
);
}
return
ResultVo
.
success
(
ImageUtil
.
saveTempFile
(
file
));
}
@ApiOperation
(
"识别红色"
)
@PostMapping
(
"/recognizeRed"
)
public
ResultVo
<
Boolean
>
recognizeRed
(
@RequestBody
OptimizationReq
req
)
{
ResultVo
<
Boolean
>
resultVo
;
try
{
resultVo
=
ResultVo
.
success
(
service
.
recognizeRed
(
req
));
}
catch
(
Exception
e
)
{
resultVo
=
ResultVo
.
success
(
false
);
}
return
resultVo
;
}
@ApiOperation
(
"去除红色"
)
@PostMapping
(
"/removeRed"
)
public
ResultVo
<?>
removeRed
(
@RequestBody
OptimizationReq
req
)
{
ResultVo
<?>
resultVo
;
try
{
resultVo
=
ResultVo
.
success
(
service
.
removeRed
(
req
));
}
catch
(
Exception
e
)
{
resultVo
=
ResultVo
.
fail
(
"处理失败"
);
}
return
resultVo
;
}
}
imgproc-server/src/main/java/com/zq/imgproc/exception/BusinessException.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
exception
;
/**
* 业务错误
*
* @author wilmiam
* @since 2021-07-09 17:58
*/
public
class
BusinessException
extends
RuntimeException
{
private
int
code
=
400
;
public
BusinessException
(
String
message
)
{
super
(
message
);
}
public
BusinessException
(
int
code
,
String
message
)
{
super
(
message
);
this
.
code
=
code
;
}
public
BusinessException
(
int
code
,
String
message
,
Throwable
cause
)
{
super
(
message
,
cause
);
this
.
code
=
code
;
}
public
int
getCode
()
{
return
code
;
}
}
\ No newline at end of file
imgproc-server/src/main/java/com/zq/imgproc/server/ImgProcService.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
server
;
import
cn.hutool.core.codec.Base64Decoder
;
import
cn.hutool.core.codec.Base64Encoder
;
import
cn.hutool.core.convert.Convert
;
import
cn.hutool.core.img.ImgUtil
;
import
cn.hutool.core.io.FileUtil
;
import
com.alibaba.excel.EasyExcel
;
import
com.zq.imgproc.utils.*
;
import
com.zq.imgproc.vo.*
;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.opencv.core.Core
;
import
org.opencv.core.CvType
;
import
org.opencv.core.Mat
;
import
org.opencv.core.MatOfDouble
;
import
org.opencv.imgcodecs.Imgcodecs
;
import
org.opencv.imgproc.Imgproc
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.core.io.InputStreamResource
;
import
org.springframework.core.io.Resource
;
import
org.springframework.http.ContentDisposition
;
import
org.springframework.http.HttpHeaders
;
import
org.springframework.http.MediaType
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.stereotype.Service
;
import
org.springframework.web.multipart.MultipartFile
;
import
javax.imageio.ImageIO
;
import
java.awt.image.BufferedImage
;
import
java.io.BufferedInputStream
;
import
java.io.File
;
import
java.io.IOException
;
import
java.math.BigDecimal
;
import
java.math.RoundingMode
;
import
java.text.SimpleDateFormat
;
import
java.util.*
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
/**
* <p>
* 图片检测服务
* </p>
*
* @author chenhao
* @since 2022/10/24 15:02
*/
@Slf4j
@RequiredArgsConstructor
@Service
public
class
ImgProcService
{
@Value
(
"${imgconfig.deskew}"
)
String
deskewUrl
;
@Value
(
"${imgconfig.deskewpy}"
)
String
deskewpyUrl
;
public
List
<
ImgVO
>
detection
(
List
<
ImgVO
>
pathList
)
throws
Exception
{
List
<
ImgVO
>
res
=
new
ArrayList
<>(
pathList
.
size
());
for
(
ImgVO
one
:
pathList
)
{
DetectionVO
vo
=
new
DetectionVO
();
String
path
=
one
.
getUrl
();
Mat
image
=
Imgcodecs
.
imread
(
path
);
// 检测图片的分辨率
vo
.
setWidthResolution
(
image
.
width
());
vo
.
setHeightResolution
(
image
.
height
());
// 检测图片的DPI
vo
.
setDpi
(
ImageUtil
.
getDpi
(
FileUtil
.
file
(
path
)));
// 检测图片清晰度
BigDecimal
clarity
=
BigDecimal
.
valueOf
(
ImageUtil
.
tenengrad
(
image
));
vo
.
setClarity
(
clarity
.
setScale
(
2
,
RoundingMode
.
HALF_UP
).
doubleValue
());
// 检测图片的亮度
vo
.
setBrightness
(
Convert
.
toInt
(
ImageUtil
.
brightness
(
image
)));
// 检测图片倾斜角度
vo
.
setAngle
(
Deskew
.
getDeskewAngle
(
image
));
// 检测图片的黑边
vo
.
setBlack
(
ImageUtil
.
blackDetection
(
image
));
// 图片弯曲检测
BendResult
bendResult
=
BendUtil
.
getBendResult
(
path
);
vo
.
setBend
(
bendResult
.
getConfidence
());
one
.
setDetectionRes
(
vo
);
res
.
add
(
one
);
}
return
res
;
}
public
DetectionResVo
detection
(
MultipartFile
file
)
throws
Exception
{
DetectionResVo
res
=
new
DetectionResVo
();
Mat
image
=
ImageUtil
.
getMat
(
file
);
// 检测图片的分辨率
res
.
setWidthResolution
(
image
.
width
());
res
.
setHeightResolution
(
image
.
height
());
// 检测图片的DPI
res
.
setDpi
(
ImageUtil
.
getDpi
(
file
));
// 检测图片清晰度
res
.
setClarity
(
clarityDetection
(
image
)
>
10
);
// 检测图片的亮度
res
.
setBrightness
(
brightness
(
brightnessDetection
(
image
)));
// 检测图片倾斜角度
res
.
setAngle
(
Deskew
.
getDeskewAngle
(
image
));
// 检测图片的黑边
res
.
setBlack
(
ImageUtil
.
blackDetection
(
image
));
return
res
;
}
public
DetectionVO
detection2
(
OptimizationReq
req
)
throws
Exception
{
// 1. 临时保存图片
String
ext
=
FileUtil
.
extName
(
req
.
getFilename
());
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
filePath
=
"/data/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"."
+
ext
;
FileUtil
.
writeBytes
(
Base64Decoder
.
decode
(
req
.
getFileContent
()),
filePath
);
if
(
"heic"
.
equals
(
ext
))
{
BufferedImage
bufferedImage
=
ImageIO
.
read
(
new
File
(
filePath
));
ext
=
"jpg"
;
filePath
=
"/data/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"."
+
ext
;
ImageIO
.
write
(
bufferedImage
,
"jpg"
,
new
File
(
filePath
));
}
DetectionVO
res
=
new
DetectionVO
();
Mat
image
=
Imgcodecs
.
imread
(
filePath
);
// 检测图片的分辨率
res
.
setWidthResolution
(
image
.
width
());
res
.
setHeightResolution
(
image
.
height
());
// 检测图片的DPI
res
.
setDpi
(
ImageUtil
.
getDpi
(
FileUtil
.
file
(
filePath
)));
// 检测图片清晰度
res
.
setClarity
(
ImageUtil
.
imageSharpnessDetector
(
image
));
// 检测图片的亮度
res
.
setBrightness
(
ImageUtil
.
brightness
(
image
));
// 检测图片倾斜角度
res
.
setAngle
(
Deskew
.
getDeskewAngle
(
image
));
// 检测图片的黑边
res
.
setBlack
(
ImageUtil
.
blackDetection
(
image
));
// 图片弯曲检测
BendResult
bendResult
=
BendUtil
.
getBendResult
(
filePath
);
res
.
setBend
(
bendResult
.
getConfidence
());
image
.
release
();
FileUtil
.
del
(
filePath
);
return
res
;
}
/**
* 图片亮度检测
* cast为计算出的偏差值,小于1表示比较正常,大于1表示存在亮度异常;当cast异常时,da大于0表示过亮,da小于0表示过暗
*
* @param src 图片矩阵
* @return [cast, da]
*/
private
double
[]
brightnessDetection
(
Mat
src
)
{
// 灰度化,转为灰度图
Mat
gray
=
src
.
clone
();
// 获取原图的列数,RGB图像为3列
if
(
3
==
src
.
channels
())
{
Imgproc
.
cvtColor
(
src
,
gray
,
Imgproc
.
COLOR_BGR2GRAY
);
}
else
{
log
.
error
(
"该图片不是RGB图像,灰度化失败!"
);
return
null
;
}
double
a
=
0
;
int
[]
hist
=
new
int
[
256
];
for
(
int
i
=
0
;
i
<
gray
.
rows
();
i
++)
{
for
(
int
j
=
0
;
j
<
gray
.
cols
();
j
++)
{
a
+=
gray
.
get
(
i
,
j
)[
0
]
-
128
;
int
index
=
(
int
)
gray
.
get
(
i
,
j
)[
0
];
hist
[
index
]++;
}
}
double
da
=
a
/
(
gray
.
rows
()
*
gray
.
cols
());
double
ma
=
0
;
for
(
int
i
=
0
;
i
<
hist
.
length
;
i
++)
{
ma
+=
Math
.
abs
(
i
-
128
-
da
)
*
hist
[
i
];
}
ma
=
ma
/
(
gray
.
rows
()
*
gray
.
cols
());
double
cast
=
Math
.
abs
(
da
)
/
Math
.
abs
(
ma
);
return
new
double
[]{
cast
,
da
};
}
/**
* 用默认标准判断图片亮度情况
*
* @return 图片的亮度情况,1表示亮度正常,2表示过亮,3表示过暗
*/
private
Integer
brightness
(
double
[]
arr
)
{
if
(
arr
==
null
)
{
return
-
1
;
}
double
cast
=
arr
[
0
];
double
da
=
arr
[
1
];
if
(
cast
<
1
)
{
return
1
;
}
else
{
if
(
da
>
0
)
{
return
2
;
}
else
{
return
3
;
}
}
}
/**
* 图像清晰度,求出灰度图的平均灰度和标准差
* 平均值和方差越大,代表图片越清晰
*
* @param src 图片矩阵
* @return [mean, std] 灰度图的平均灰度和标准差
*/
private
double
clarityDetection
(
Mat
src
)
{
Mat
gray
=
src
.
clone
();
Imgproc
.
cvtColor
(
src
,
gray
,
Imgproc
.
COLOR_BGR2GRAY
);
// 拉普拉斯平滑处理
// 阈值太低会导致正常图片被误断为模糊图片,阈值太高会导致模糊图片被误判为正常图片
Mat
laplacImg
=
new
Mat
();
Imgproc
.
Laplacian
(
gray
,
laplacImg
,
CvType
.
CV_64F
);
// 标准差,stddev.get(0,0)[0]
MatOfDouble
stddev
=
new
MatOfDouble
();
// 平均灰度值,mean.get(0,0)[0]
MatOfDouble
mean
=
new
MatOfDouble
();
Core
.
meanStdDev
(
laplacImg
,
mean
,
stddev
);
double
[]
stds
=
stddev
.
get
(
0
,
0
);
return
stds
[
0
];
}
/**
* 灰度化图片后进行canny边缘检测
*
* @param src 图片矩阵
* @return 边缘检测之后的图片矩阵
*/
private
Mat
canny
(
Mat
src
)
{
// 灰度化
Mat
gray
=
src
.
clone
();
if
(
src
.
channels
()
==
4
||
src
.
channels
()
==
3
)
{
Imgproc
.
cvtColor
(
src
,
gray
,
Imgproc
.
COLOR_BGR2GRAY
);
}
else
if
(
src
.
channels
()
==
2
)
{
Imgproc
.
cvtColor
(
src
,
gray
,
Imgproc
.
COLOR_BGR5652GRAY
);
}
else
{
gray
=
src
;
}
Mat
mat
=
gray
.
clone
();
Imgproc
.
Canny
(
gray
,
mat
,
60
,
200
);
return
mat
;
}
/**
* 通过矫正工具获取的倾斜角度
*
* @param imageUrl 图片路径
* @return 图片倾斜角度
*/
private
double
getAngle2
(
String
imageUrl
)
{
// 纠正文件的保存路径
String
ext
=
FileUtil
.
extName
(
imageUrl
);
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/data/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"."
+
ext
;
String
deskewRes
=
ImageCorrectionUtil
.
deskew
(
imageUrl
,
savePath
,
deskewUrl
,
true
);
if
(
deskewRes
.
contains
(
"Error"
))
{
return
0.0d
;
}
else
{
Pattern
pattern
=
Pattern
.
compile
(
"Skew angle found \\[deg\\]: (-?\\d+(?:\\.\\d+)?)"
);
Matcher
matcher
=
pattern
.
matcher
(
deskewRes
);
if
(
matcher
.
find
())
{
return
Double
.
parseDouble
(
matcher
.
group
(
1
));
}
else
{
return
0.0d
;
}
}
}
/**
* 通过矫正工具获取的倾斜角度
*
* @param imageUrl 图片路径
* @return 图片倾斜角度
*/
private
Double
getAngle3
(
String
imageUrl
)
{
// 纠正文件的保存路径
String
ext
=
FileUtil
.
extName
(
imageUrl
);
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/data/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"."
+
ext
;
String
deskewRes
=
ImageCorrectionUtil
.
deskew2
(
imageUrl
,
savePath
,
deskewpyUrl
);
Double
angle
=
Convert
.
toDouble
(
deskewRes
);
return
angle
==
null
?
getAngle2
(
imageUrl
)
:
angle
;
}
/**
* 求数组众数
*
* @param angelList 数组
* @return 数组众数
*/
private
static
int
most
(
List
<
Integer
>
angelList
)
{
if
(
angelList
.
isEmpty
())
{
return
0
;
}
int
res
=
0
;
int
max
=
Integer
.
MIN_VALUE
;
Map
<
Integer
,
Integer
>
map
=
new
HashMap
<>();
for
(
int
i
:
angelList
)
{
map
.
put
(
i
,
map
.
getOrDefault
(
i
,
0
)
+
1
);
}
for
(
Integer
i
:
map
.
keySet
())
{
int
count
=
map
.
get
(
i
);
if
(
count
>
max
)
{
max
=
count
;
res
=
i
;
}
}
return
res
;
}
/**
* 计算直线的倾斜角
*/
private
int
calculateAngle
(
double
x1
,
double
y1
,
double
x2
,
double
y2
)
{
if
(
Math
.
abs
(
x2
-
x1
)
<
1
e
-
4
)
{
return
90
;
}
else
if
(
Math
.
abs
(
y2
-
y1
)
<
1
e
-
4
)
{
return
0
;
}
else
{
double
k
=
-(
y2
-
y1
)
/
(
x2
-
x1
);
double
res
=
Math
.
atan
(
k
)
*
57.29577
;
return
Convert
.
toInt
(
Math
.
round
(
res
));
}
}
public
List
<
ImgVO
>
imageCorrection
(
List
<
ImgVO
>
pathList
)
{
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/data/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"/"
;
int
index
=
1
;
for
(
ImgVO
one
:
pathList
)
{
String
path
=
one
.
getUrl
();
String
ext
=
FileUtil
.
extName
(
one
.
getFileName
());
ImageCorrectionUtil
.
correctImg
(
path
);
Mat
image
=
Imgcodecs
.
imread
(
path
);
double
angle
=
Deskew
.
getDeskewAngle
(
canny
(
image
));
// 通过倾斜角度旋转图片
ImgUtil
.
rotate
(
FileUtil
.
file
(
path
),
Convert
.
toInt
(
angle
)
,
FileUtil
.
file
(
savePath
+
index
+
"."
+
ext
));
one
.
setUrl
(
savePath
+
index
+
"."
+
ext
);
index
++;
}
return
pathList
;
}
public
List
removeBlack
(
List
<
ImgVO
>
pathList
)
{
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/data/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"/"
;
int
index
=
1
;
// 除黑边
for
(
ImgVO
one
:
pathList
)
{
String
ext
=
FileUtil
.
extName
(
one
.
getFileName
());
RemoveBlackUtil2
.
remove
(
one
.
getUrl
(),
savePath
+
index
+
"."
+
ext
);
one
.
setUrl
(
savePath
+
index
+
"."
+
ext
);
index
++;
}
// 修正
return
pathList
;
}
public
List
<
ImgVO
>
deskew
(
List
<
ImgVO
>
pathList
)
{
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/data/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"/"
;
int
index
=
1
;
for
(
ImgVO
one
:
pathList
)
{
String
ext
=
FileUtil
.
extName
(
one
.
getFileName
());
if
(
"heic"
.
equals
(
ext
))
{
Mat
mat
=
Imgcodecs
.
imread
(
one
.
getUrl
());
double
angle
=
Deskew
.
getDeskewAngle
(
mat
);
ImageUtil
.
rotateImage
(
one
.
getUrl
(),
savePath
+
index
+
"."
+
ext
,
angle
);
}
else
{
ImageCorrectionUtil
.
deskew
(
one
.
getUrl
(),
savePath
+
index
+
"."
+
ext
,
deskewUrl
,
false
);
}
one
.
setUrl
(
savePath
+
index
+
"."
+
ext
);
index
++;
}
return
pathList
;
}
public
List
<
ImgVO
>
rotate
(
RotateReq
req
)
{
List
<
ImgVO
>
pathList
=
req
.
getPathList
();
Integer
degree
=
req
.
getDegree
();
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/data/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"/"
;
int
index
=
1
;
for
(
ImgVO
one
:
pathList
)
{
String
ext
=
FileUtil
.
extName
(
one
.
getFileName
());
ImageUtil
.
rotateImage
(
one
.
getUrl
(),
savePath
+
index
+
"."
+
ext
,
degree
);
one
.
setUrl
(
savePath
+
index
+
"."
+
ext
);
index
++;
}
return
pathList
;
}
public
List
<
ImgVO
>
gray
(
List
<
ImgVO
>
pathList
)
{
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/data/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"/"
;
int
index
=
1
;
for
(
ImgVO
one
:
pathList
)
{
String
ext
=
FileUtil
.
extName
(
one
.
getFileName
());
ImageUtil
.
gray
(
one
.
getUrl
(),
savePath
+
index
+
"."
+
ext
);
one
.
setUrl
(
savePath
+
index
+
"."
+
ext
);
index
++;
}
return
pathList
;
}
public
List
<
ImgVO
>
canny
(
List
<
ImgVO
>
pathList
)
{
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/data/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"/"
;
int
index
=
1
;
for
(
ImgVO
one
:
pathList
)
{
String
ext
=
FileUtil
.
extName
(
one
.
getFileName
());
Mat
src
=
Imgcodecs
.
imread
(
one
.
getUrl
());
Mat
cannyImg
=
canny
(
src
);
ImageUtil
.
saveImage
(
cannyImg
,
savePath
+
index
+
"."
+
ext
);
one
.
setUrl
(
savePath
+
index
+
"."
+
ext
);
index
++;
}
return
pathList
;
}
public
OptimizationVO
optimization
(
OptimizationReq
req
)
throws
IOException
{
OptimizationVO
res
=
new
OptimizationVO
();
// 1. 临时保存图片
String
ext
=
FileUtil
.
extName
(
req
.
getFilename
());
String
filename
=
req
.
getFilename
();
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/data/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"/"
;
String
filePath
=
savePath
+
filename
;
FileUtil
.
writeBytes
(
Base64Decoder
.
decode
(
req
.
getFileContent
()),
filePath
);
log
.
info
(
"{}图片进行优化"
,
filePath
);
// 处理heic图片
if
(
"heic"
.
equals
(
ext
))
{
BufferedImage
bufferedImage
=
ImageIO
.
read
(
new
File
(
filePath
));
ext
=
"jpg"
;
filename
=
FileUtil
.
mainName
(
filename
)
+
".jpg"
;
filePath
=
savePath
+
filename
;
ImageIO
.
write
(
bufferedImage
,
"jpg"
,
new
File
(
filePath
));
log
.
info
(
"{}图片从heic转换为jpg"
,
filePath
);
}
String
newPath
=
filePath
;
// 2. 黑边处理
long
start
=
System
.
currentTimeMillis
();
String
blackUrl
=
savePath
+
"(1)."
+
ext
;
Mat
aMat
=
Imgcodecs
.
imread
(
newPath
);
if
(
ImageUtil
.
blackDetection
(
aMat
))
{
res
.
setRemoveBlack
(
true
);
RemoveBlackUtil2
.
remove
(
newPath
,
blackUrl
);
newPath
=
blackUrl
;
}
else
{
res
.
setRemoveBlack
(
false
);
}
aMat
.
release
();
log
.
info
(
"黑边处理消耗的时间【{}】"
,
System
.
currentTimeMillis
()
-
start
);
// 3. 执行纠偏
start
=
System
.
currentTimeMillis
();
String
deskerFile
=
savePath
+
"(2)."
+
ext
;
Mat
bMat
=
Imgcodecs
.
imread
(
newPath
);
double
skewAngle
=
Deskew
.
getDeskewAngle
(
bMat
);
if
(
Double
.
compare
(
skewAngle
,
1
)
<
0
&&
Double
.
compare
(
skewAngle
,
-
1
)
>
0
)
{
res
.
setDeskewAngel
(
0
);
}
else
{
Mat
rotateMat
=
Deskew
.
rotate
(
bMat
,
skewAngle
);
Imgcodecs
.
imwrite
(
deskerFile
,
rotateMat
);
newPath
=
deskerFile
;
res
.
setDeskewAngel
(
skewAngle
);
rotateMat
.
release
();
}
bMat
.
release
();
log
.
info
(
"纠偏所需时间【{}】"
,
System
.
currentTimeMillis
()
-
start
);
// 4。 图片亮度检测
// 计算图像的平均亮度
start
=
System
.
currentTimeMillis
();
Mat
cMat
=
Imgcodecs
.
imread
(
newPath
);
double
brightness
=
ImageUtil
.
brightness
(
cMat
);
res
.
setOriginalBrightnessVal
(
brightness
);
// 亮度异常执行亮度矫正
if
(
brightness
<
100
||
brightness
>
500
)
{
res
.
setBrightness
(
true
);
// 计算亮度调整值
double
alpha
=
300
/
brightness
;
// 执行亮度调整
Mat
grayImage
=
Imgcodecs
.
imread
(
newPath
);
Mat
adjustedImage
=
new
Mat
();
grayImage
.
convertTo
(
adjustedImage
,
-
1
,
alpha
,
0
);
// 保存调整后的图像
Imgcodecs
.
imwrite
(
newPath
,
adjustedImage
);
res
.
setBrightnessVal
(
ImageUtil
.
brightness
(
newPath
));
adjustedImage
.
release
();
grayImage
.
release
();
}
else
{
res
.
setBrightness
(
false
);
res
.
setBrightnessVal
(
0
);
}
cMat
.
release
();
log
.
info
(
"亮度检测所需时间【{}】"
,
System
.
currentTimeMillis
()
-
start
);
// 5. 图片清晰度检测
start
=
System
.
currentTimeMillis
();
Mat
dMat
=
Imgcodecs
.
imread
(
newPath
);
double
laplacianScore
=
ImageUtil
.
imageSharpnessDetector
(
dMat
);
res
.
setOriginalClarityVal
(
laplacianScore
);
if
(
laplacianScore
<
500
)
{
res
.
setClarity
(
true
);
Mat
unsharpMaskingMat
=
ImageUtil
.
unsharpMasking
(
dMat
);
// 保存调整后的图像
Imgcodecs
.
imwrite
(
newPath
,
unsharpMaskingMat
);
res
.
setClarityVal
(
ImageUtil
.
imageSharpnessDetector
(
unsharpMaskingMat
));
unsharpMaskingMat
.
release
();
}
else
{
res
.
setClarity
(
false
);
res
.
setClarityVal
(
0
);
}
dMat
.
release
();
log
.
info
(
"图片清晰度所需时间【{}】"
,
System
.
currentTimeMillis
()
-
start
);
res
.
setFilename
(
filename
);
res
.
setFileContent
(
Base64Encoder
.
encode
(
FileUtil
.
readBytes
(
newPath
)));
res
.
setCorrect
(
false
);
return
res
;
// 6. 图片弯曲矫正
// String correctUrl = savePath + "(3)." + ext;
// HashMap<String, Object> map = new HashMap<>();
// map.put("file", FileUtil.file(newPath));
// String response = HttpUtil.post("http://ddns.gxmailu.com:18888/api/correct", map);
// JSONObject correctRes = JSONUtil.parseObj(response);
// if (ObjectUtil.isNotNull(correctRes) && correctRes.get("success", Boolean.class)) {
// String base64 = correctRes.get("data", String.class);
// res.setCorrect(true);
// res.setFileContent(base64);
// res.setFilename(filename);
// } else {
// res.setCorrect(false);
// res.setFilename(filename);
// res.setFileContent(Base64Encoder.encode(FileUtil.readBytes(newPath)));
// }
// return res;
}
public
Boolean
recognizeRed
(
OptimizationReq
req
)
{
// 暂时保存
String
filename
=
req
.
getFilename
();
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/data/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"/"
;
String
filePath
=
savePath
+
filename
;
FileUtil
.
writeBytes
(
Base64Decoder
.
decode
(
req
.
getFileContent
()),
filePath
);
Mat
mat
=
Imgcodecs
.
imread
(
filePath
);
// 转为HSV空间
Mat
hsv
=
new
Mat
();
Imgproc
.
cvtColor
(
mat
,
hsv
,
Imgproc
.
COLOR_BGR2HSV
);
int
nums
=
0
;
for
(
int
i
=
0
;
i
<
hsv
.
rows
();
i
++)
{
for
(
int
j
=
0
;
j
<
hsv
.
cols
();
j
++)
{
double
[]
clone
=
hsv
.
get
(
i
,
j
).
clone
();
double
h
=
clone
[
0
];
double
s
=
clone
[
1
];
double
v
=
clone
[
2
];
// 红色的hsv范围判断
if
((
h
>
0
&&
h
<
10
)
||
(
h
>
156
&&
h
<
180
))
{
if
(
s
>
43
&&
s
<
255
)
{
if
(
v
<
255
&&
v
>
46
)
{
nums
++;
}
}
}
}
}
FileUtil
.
del
(
FileUtil
.
file
(
filePath
));
return
nums
>
8000
;
}
public
String
removeRed
(
OptimizationReq
req
)
{
// 暂时保存
String
ext
=
FileUtil
.
extName
(
req
.
getFilename
());
String
filename
=
req
.
getFilename
();
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/data/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"/"
;
String
filePath
=
savePath
+
filename
;
FileUtil
.
writeBytes
(
Base64Decoder
.
decode
(
req
.
getFileContent
()),
filePath
);
Mat
mat
=
Imgcodecs
.
imread
(
filePath
);
List
<
Mat
>
mats
=
new
ArrayList
<>();
// 分离图片通道
Core
.
split
(
mat
,
mats
);
// 获取红色通道的矩阵
Mat
red
=
mats
.
get
(
2
);
// 二值化
Mat
threshRed
=
new
Mat
();
Imgproc
.
threshold
(
red
,
threshRed
,
120
,
255
,
Imgproc
.
THRESH_BINARY
);
String
dst
=
savePath
+
"test."
+
ext
;
Imgcodecs
.
imwrite
(
dst
,
threshRed
);
return
Base64Encoder
.
encode
(
FileUtil
.
readBytes
(
dst
));
}
public
ResponseEntity
<
Resource
>
getDetection
(
List
<
ImgVO
>
pathList
)
throws
Exception
{
if
(
pathList
==
null
||
pathList
.
isEmpty
())
{
return
ResponseEntity
.
badRequest
().
build
();
}
pathList
=
detection
(
pathList
);
String
filePath
=
"/data/temp/"
+
UuidUtils
.
uuidNoDash
()
+
".xlsx"
;
EasyExcel
.
write
(
filePath
,
DetectionDTO
.
class
).
sheet
(
"图片检测报告"
).
doWrite
(
getData
(
pathList
));
BufferedInputStream
stream
=
FileUtil
.
getInputStream
(
filePath
);
if
(
stream
==
null
)
{
return
ResponseEntity
.
notFound
().
build
();
}
String
contentDisposition
=
ContentDisposition
.
builder
(
"attachment"
)
.
filename
(
"图片检测报告.xlsx"
)
.
build
().
toString
();
return
ResponseEntity
.
ok
()
.
header
(
HttpHeaders
.
CONTENT_DISPOSITION
,
contentDisposition
)
.
contentType
(
MediaType
.
APPLICATION_OCTET_STREAM
)
.
body
(
new
InputStreamResource
(
stream
));
}
private
List
<
DetectionDTO
>
getData
(
List
<
ImgVO
>
pathList
)
{
List
<
DetectionDTO
>
dtos
=
new
ArrayList
<>(
pathList
.
size
());
for
(
ImgVO
imgVO
:
pathList
)
{
DetectionVO
detectionRes
=
imgVO
.
getDetectionRes
();
DetectionDTO
dto
=
new
DetectionDTO
();
dto
.
setFilename
(
imgVO
.
getFileName
());
dto
.
setResolution
(
detectionRes
.
getWidthResolution
()
+
" x "
+
detectionRes
.
getHeightResolution
());
dto
.
setDpi
(
detectionRes
.
getDpi
());
dto
.
setBrightness
(
detectionRes
.
getBrightness
());
dto
.
setClarity
(
detectionRes
.
getClarity
());
dto
.
setAngle
(
detectionRes
.
getAngle
());
dto
.
setBend
(
detectionRes
.
getBend
());
dto
.
setBlack
(
detectionRes
.
getBlack
()
?
"是"
:
"否"
);
dtos
.
add
(
dto
);
}
return
dtos
;
}
}
imgproc-server/src/main/java/com/zq/imgproc/utils/AssertUtils.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
utils
;
import
cn.hutool.core.util.StrUtil
;
import
com.zq.imgproc.exception.BusinessException
;
import
java.util.Arrays
;
import
java.util.Collection
;
import
java.util.Map
;
/**
* 断言验证帮助类
*
* @author wilmiam
* @since 2018-04-03
*/
@SuppressWarnings
(
"all"
)
public
final
class
AssertUtils
{
private
static
final
String
[]
IMG_EXTS
=
{
"png"
,
"jpg"
,
"jpeg"
};
/**
* Don't let anyone instantiate this class
*/
private
AssertUtils
()
{
}
/**
* 判断给定的文件名后缀是否为图片
*
* @param ext 文件名后缀, 不带点
* @param errMsg 错误信息
*/
public
static
void
isImgExt
(
String
ext
,
String
errMsg
)
{
isImgExt
(
ext
,
400
,
errMsg
);
}
/**
* 判断给定的文件名后缀是否为图片
*
* @param ext 文件名后缀, 不带点
* @param errCode 断言失败的错误代码
* @param errMsg 错误信息
*/
public
static
void
isImgExt
(
String
ext
,
int
errCode
,
String
errMsg
)
{
if
(
StrUtil
.
isBlank
(
ext
)
||
Arrays
.
stream
(
IMG_EXTS
).
noneMatch
(
img
->
img
.
equalsIgnoreCase
(
ext
)))
{
throw
new
BusinessException
(
errCode
,
errMsg
);
}
}
/**
* UnifiedExceptionHandler
* 判断一个布尔表达式, 若表达式为{@code true}则抛出指定错误信息的{@code BusinessException}.
*
* @param expression 布尔表达式
* @param message 断言失败时的错误信息
* @throws BusinessException
*/
public
static
void
notTrue
(
boolean
expression
,
String
message
)
throws
BusinessException
{
notTrue
(
expression
,
400
,
message
);
}
/**
* 判断一个布尔表达式, 若表达式为{@code true}则抛出指定错误信息的{@code BusinessException}.
*
* @param expression 布尔表达式
* @param errCode 断言失败时的错误代码
* @param message 断言失败时的错误信息
* @throws BusinessException
*/
public
static
void
notTrue
(
boolean
expression
,
int
errCode
,
String
message
)
throws
BusinessException
{
if
(
expression
)
{
throw
new
BusinessException
(
errCode
,
message
);
}
}
/**
* 判断一个布尔表达式, 若表达式为{@code false}则抛出指定错误信息的{@code BusinessException}.
*
* @param expression 布尔表达式
* @param message 断言失败时的错误信息
* @throws BusinessException
*/
public
static
void
isTrue
(
boolean
expression
,
String
message
)
throws
BusinessException
{
isTrue
(
expression
,
400
,
message
);
}
/**
* 判断一个布尔表达式, 若表达式为{@code false}则抛出指定错误信息的{@code BusinessException}.
*
* @param expression 布尔表达式
* @param errCode 断言失败时的错误代码
* @param message 断言失败时的错误信息
* @throws BusinessException
*/
public
static
void
isTrue
(
boolean
expression
,
int
errCode
,
String
message
)
throws
BusinessException
{
if
(!
expression
)
{
throw
new
BusinessException
(
errCode
,
message
);
}
}
/**
* 如果对象为{@code null}, 则抛出异常
*
* @param object 要判断的对象
* @throws BusinessException
*/
public
static
void
notNull
(
Object
object
)
throws
BusinessException
{
notNull
(
object
,
"不能处理空对象"
);
}
/**
* 如果对象为{@code null}, 则抛出异常
*
* @param object 要判断的对象
* @param message 断言失败时的错误信息
* @throws BusinessException
*/
public
static
void
notNull
(
Object
object
,
String
message
)
throws
BusinessException
{
notNull
(
object
,
400
,
message
);
}
/**
* 如果对象为{@code null}, 则抛出异常
*
* @param object 要判断的对象
* @param errCode 断言失败时的错误代码
* @param errMsg 断言失败时的错误信息
* @throws BusinessException
*/
public
static
void
notNull
(
Object
object
,
int
errCode
,
String
errMsg
)
throws
BusinessException
{
if
(
object
==
null
)
{
throw
new
BusinessException
(
errCode
,
errMsg
);
}
}
/**
* 如果字符串为{@code null}、空字符串或仅包含空白字符, 则抛出异常
*
* @param text 要进行检查的字符串
* @throws BusinessException
*/
public
static
void
hasText
(
String
text
)
throws
BusinessException
{
hasText
(
text
,
400
,
"参数不能为空字符串"
);
}
/**
* 如果字符串为{@code null}、空字符串或仅包含空白字符, 则抛出异常
*
* @param text 要进行检查的字符串
* @param message 断言失败时的错误信息
* @throws BusinessException
*/
public
static
void
hasText
(
String
text
,
String
message
)
throws
BusinessException
{
hasText
(
text
,
400
,
message
);
}
/**
* 如果字符串为{@code null}、空字符串或仅包含空白字符, 则抛出异常
*
* @param text 要进行检查的字符串
* @param errCode 断言失败时的错误代码
* @param errMsg 错误信息
* @throws BusinessException
*/
public
static
void
hasText
(
String
text
,
int
errCode
,
String
errMsg
)
throws
BusinessException
{
if
(
StrUtil
.
isBlank
(
text
))
{
throw
new
BusinessException
(
errCode
,
errMsg
);
}
}
/**
* 如果数组为{@code null}或长度为0, 则抛出异常
*
* @param array 要进行检查的数组
* @param message 断言失败时的错误信息
* @param <T> 数组的数据类型
* @throws BusinessException
*/
public
static
<
T
>
void
notEmpty
(
T
[]
array
,
String
message
)
throws
BusinessException
{
notEmpty
(
array
,
400
,
message
);
}
/**
* 如果数组为{@code null}或长度为0, 则抛出异常
*
* @param array 要进行检查的数组
* @param errCode 断言失败时的错误代码
* @param errMsg 错误信息
* @param <T> 数组的数据类型
* @throws BusinessException
*/
public
static
<
T
>
void
notEmpty
(
T
[]
array
,
int
errCode
,
String
errMsg
)
throws
BusinessException
{
if
(
array
==
null
||
array
.
length
==
0
)
{
throw
new
BusinessException
(
errCode
,
errMsg
);
}
}
/**
* 如果数组里包含有{@code null}的元素, 则抛出异常. 注意: 若数组本身为{@code null}则不会进行处理, 直接返回
*
* @param array 要进行检查的数组
* @param message 断言失败时的错误信息
* @param <T> 数组的数据类型
* @throws BusinessException
*/
public
static
<
T
>
void
noNullElements
(
T
[]
array
,
String
message
)
throws
BusinessException
{
noNullElements
(
array
,
400
,
message
);
}
/**
* 如果数组里包含有{@code null}的元素, 则抛出异常. 注意: 若数组本身为{@code null}则不会进行处理, 直接返回
*
* @param array 要进行检查的数组
* @param errCode 断言失败时的错误代码
* @param errMsg 错误信息
* @param <T> 数组的数据类型
* @throws BusinessException
*/
public
static
<
T
>
void
noNullElements
(
T
[]
array
,
int
errCode
,
String
errMsg
)
throws
BusinessException
{
if
(
array
!=
null
)
{
for
(
T
element
:
array
)
{
if
(
element
==
null
)
{
throw
new
BusinessException
(
errCode
,
errMsg
);
}
}
}
}
/**
* 如果集合为{@code null},或者不包含任何元素,则抛出异常
*
* @param collection 要进行检查的集合
* @param message 断言失败时的错误信息
* @throws BusinessException
*/
public
static
void
notEmpty
(
Collection
<?>
collection
,
String
message
)
throws
BusinessException
{
notEmpty
(
collection
,
400
,
message
);
}
/**
* 如果集合为{@code null},或者不包含任何元素,则抛出异常
*
* @param collection 要进行检查的集合
* @param errCode 断言失败时的错误代码
* @param errMsg 错误信息
* @throws BusinessException
*/
public
static
void
notEmpty
(
Collection
<?>
collection
,
int
errCode
,
String
errMsg
)
throws
BusinessException
{
if
(
collection
==
null
||
collection
.
isEmpty
())
{
throw
new
BusinessException
(
errCode
,
errMsg
);
}
}
/**
* 如果键值对为{@code null},或者不包含任何键值,则抛出异常
*
* @param map 要进行检查的键值对
* @param message 断言失败时的错误信息
* @throws BusinessException
*/
public
static
void
notEmpty
(
Map
<?,
?>
map
,
String
message
)
throws
BusinessException
{
notEmpty
(
map
,
400
,
message
);
}
/**
* 如果键值对为{@code null},或者不包含任何键值,则抛出异常
*
* @param map 要进行检查的键值对
* @param errCode 断言失败时的错误代码
* @param errMsg 错误信息
* @throws BusinessException
*/
public
static
void
notEmpty
(
Map
<?,
?>
map
,
int
errCode
,
String
errMsg
)
throws
BusinessException
{
if
(
map
==
null
||
map
.
isEmpty
())
{
throw
new
BusinessException
(
errCode
,
errMsg
);
}
}
}
imgproc-server/src/main/java/com/zq/imgproc/utils/BendUtil.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
utils
;
import
cn.hutool.core.convert.Convert
;
import
cn.hutool.core.io.FileUtil
;
import
cn.hutool.http.HttpUtil
;
import
cn.hutool.json.JSONObject
;
import
cn.hutool.json.JSONUtil
;
import
com.zq.imgproc.vo.BendResult
;
import
lombok.extern.slf4j.Slf4j
;
import
java.util.HashMap
;
import
java.util.List
;
/**
* <p>
* 弯曲度检测
* </P>
*
* @author yww
* @since 2023/11/26
*/
@Slf4j
public
class
BendUtil
{
private
final
static
String
URL
=
"http://129.204.37.121:8001/api/det/bending"
;
private
final
static
String
WANPRO_URL
=
"http://172.28.1.223:8030/api/det/bending"
;
private
final
static
String
URL2
=
"http://147.1.3.244:8030/api/det/bending"
;
public
static
void
main
(
String
[]
args
)
{
System
.
out
.
println
(
getBendResult
(
"C:\\Users\\11419\\Desktop\\project\\company\\test\\9.png"
).
toString
());
}
public
static
BendResult
getBendResult
(
String
imgPath
)
{
// 设定参数
HashMap
<
String
,
Object
>
param
=
new
HashMap
<>();
param
.
put
(
"file"
,
FileUtil
.
file
(
imgPath
));
BendResult
res
=
null
;
try
{
String
result
=
HttpUtil
.
post
(
URL2
,
param
,
1000
*
20
);
res
=
parseResult
(
result
);
}
catch
(
Exception
e
)
{
log
.
error
(
"图片弯曲检测失败!"
,
e
);
}
if
(
res
==
null
)
{
res
=
BendResult
.
builder
().
confidence
(
0.0
).
build
();
}
return
res
;
}
/**
* 解析弯曲检测结果
*/
public
static
BendResult
parseResult
(
String
resultJson
)
{
BendResult
result
=
null
;
ResultVo
resultVo
=
JSONUtil
.
toBean
(
resultJson
,
ResultVo
.
class
);
if
(!
resultVo
.
isSuccess
())
{
log
.
warn
(
"弯曲度检测出错: {}"
,
resultVo
.
getErrMsg
());
return
null
;
}
JSONObject
results
=
JSONUtil
.
parseObj
(
Convert
.
toStr
(
resultVo
.
getData
()));
List
<
BendResult
>
bendResults
=
JSONUtil
.
toList
(
results
.
getStr
(
"results"
),
BendResult
.
class
);
if
(
bendResults
==
null
||
bendResults
.
isEmpty
())
{
return
null
;
}
double
max
=
-
1
;
for
(
BendResult
bendResult
:
bendResults
)
{
if
(
bendResult
.
getConfidence
()
==
null
)
{
continue
;
}
if
(
bendResult
.
getConfidence
()
>
max
)
{
result
=
bendResult
;
max
=
bendResult
.
getConfidence
();
}
}
return
result
;
}
}
imgproc-server/src/main/java/com/zq/imgproc/utils/DecompressUtil.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
utils
;
import
cn.hutool.core.io.FileUtil
;
import
cn.hutool.core.util.CharsetUtil
;
import
cn.hutool.core.util.StrUtil
;
import
cn.hutool.core.util.ZipUtil
;
import
cn.hutool.extra.compress.CompressUtil
;
import
cn.hutool.extra.compress.extractor.Extractor
;
import
cn.hutool.http.HttpUtil
;
import
com.zq.imgproc.vo.ImgVO
;
import
org.apache.commons.compress.archivers.ArchiveStreamFactory
;
import
org.apache.commons.io.FileUtils
;
import
org.springframework.web.multipart.MultipartFile
;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.nio.charset.Charset
;
import
java.nio.file.StandardCopyOption
;
import
java.text.SimpleDateFormat
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Date
;
import
java.util.List
;
/**
* <p>
*
* </p>
*
* @author chenhao
* @since 2023/3/7 14:29
*/
public
class
DecompressUtil
{
private
static
final
String
[]
IMG_EXTS
=
{
"png"
,
"jpg"
,
"jpeg"
,
"tif"
,
"bmp"
,
"heic"
};
private
static
final
String
[]
COMPRESS_EXTS
=
{
"zip"
,
"7z"
};
/**
* 压缩包解压
*
* @param file 压缩包文件
* @return 保存的文件夹路径
* @throws IOException IO异常
*/
public
static
String
decompress
(
MultipartFile
file
)
throws
IOException
{
String
ext
=
FileUtil
.
extName
(
file
.
getOriginalFilename
());
if
(
"7z"
.
equals
(
ext
))
{
return
decompress7z
(
file
);
}
else
if
(
"zip"
.
equals
(
ext
))
{
return
decompressZip
(
file
);
}
else
{
return
""
;
}
}
/**
* 7z压缩包解压
* @param file 压缩包文件
* @return 保存的文件夹路径
* @throws IOException IO异常
*/
public
static
String
decompress7z
(
MultipartFile
file
)
throws
IOException
{
// 文件路径要素
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
filePath
=
"/file/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"/"
;
// 解压文件
InputStream
inputStream
=
file
.
getInputStream
();
Extractor
extractor
=
CompressUtil
.
createExtractor
(
CharsetUtil
.
defaultCharset
(),
ArchiveStreamFactory
.
SEVEN_Z
,
inputStream
);
extractor
.
extract
(
FileUtil
.
file
(
filePath
));
extractor
.
close
();
filePath
=
filePath
+
"/"
;
return
filePath
;
}
/**
* zip压缩包解压
* @param file 压缩包文件
* @return 保存的文件夹路径
* @throws IOException IO异常
*/
public
static
String
decompressZip
(
MultipartFile
file
)
throws
IOException
{
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
filePath
=
"/file/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"/"
;
ZipUtil
.
unzip
(
file
.
getInputStream
(),
FileUtil
.
file
(
filePath
),
Charset
.
forName
(
"GBK"
));
filePath
=
filePath
+
"/"
;
return
filePath
;
}
/**
* 文件压缩成zip
*
* @param list 图片文件
* @return zip压缩文件
*/
public
static
String
compress
(
List
<
ImgVO
>
list
)
{
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/file/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"/"
;
for
(
ImgVO
img
:
list
)
{
// 网上的文件需要下载
if
(
img
.
getUrl
().
startsWith
(
"http://ddns.gxmailu.com:18888"
))
{
HttpUtil
.
downloadFile
(
img
.
getUrl
(),
savePath
+
img
.
getFileName
());
}
else
{
FileUtil
.
copyFile
(
img
.
getUrl
(),
savePath
+
img
.
getFileName
(),
StandardCopyOption
.
COPY_ATTRIBUTES
);
}
}
String
filePath
=
"/file/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
".zip"
;
ZipUtil
.
zip
(
savePath
,
filePath
,
false
);
FileUtil
.
del
(
savePath
);
return
filePath
;
}
/**
* 遍历文件夹,返回图片文件
*
* @param path 文件及路径
* @return 图片文件列表
* @throws IOException IO异常
*/
public
static
List
<
ImgVO
>
loopFiles
(
String
path
)
throws
IOException
{
List
<
ImgVO
>
resList
=
new
ArrayList
<>();
List
<
File
>
fileList
=
FileUtil
.
loopFiles
(
path
);
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
String
savePath
=
"/file/temp"
+
yyyyMMdd
+
UuidUtils
.
uuidNoDash
()
+
"/"
;
int
index
=
1
;
for
(
File
file
:
fileList
)
{
String
ext
=
FileUtil
.
extName
(
file
);
if
(
StrUtil
.
isBlank
(
ext
)
||
Arrays
.
stream
(
IMG_EXTS
).
noneMatch
(
img
->
img
.
equalsIgnoreCase
(
ext
)))
{
continue
;
}
// 反转义
String
url
=
savePath
+
index
+
"."
+
ext
;
File
dest
=
new
File
(
url
);
FileUtils
.
writeByteArrayToFile
(
dest
,
FileUtil
.
readBytes
(
file
));
index
++;
resList
.
add
(
ImgVO
.
builder
().
fileName
(
file
.
getName
()).
url
(
url
).
build
());
}
return
resList
;
}
/**
* 文件类型判断
*
* @param file 压缩包文件
* @return 类型正确返回true
*/
public
static
boolean
isCompress
(
MultipartFile
file
)
{
String
ext
=
FileUtil
.
extName
(
file
.
getOriginalFilename
());
return
Arrays
.
stream
(
COMPRESS_EXTS
).
anyMatch
(
suffix
->
suffix
.
equalsIgnoreCase
(
ext
));
}
}
imgproc-server/src/main/java/com/zq/imgproc/utils/DemoUtil.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
utils
;
import
cn.hutool.core.codec.Base64
;
import
cn.hutool.core.io.FileUtil
;
import
cn.hutool.http.HttpRequest
;
import
cn.hutool.http.HttpResponse
;
import
cn.hutool.http.HttpUtil
;
import
cn.hutool.json.JSONObject
;
import
cn.hutool.json.JSONUtil
;
import
com.zq.imgproc.vo.DetectionVO
;
import
com.zq.imgproc.vo.OptimizationReq
;
import
com.zq.imgproc.vo.OptimizationVO
;
public
class
DemoUtil
{
final
static
String
URL
=
"localhost:8900/imgproc/v1/"
;
public
static
void
main
(
String
[]
args
)
{
imageOptimization
();
}
public
static
void
detection
()
{
String
testImg
=
"C:/Users/11419/Desktop/Deskew/TestImages/3.png"
;
String
resImg
=
"C:/Users/11419/Desktop/Deskew/TestImages/res.jpg"
;
String
url
=
URL
+
"detection"
;
OptimizationReq
req
=
new
OptimizationReq
();
req
.
setFilename
(
"3.png"
);
req
.
setFileContent
(
Base64
.
encode
(
FileUtil
.
file
(
testImg
)));
long
start
=
System
.
currentTimeMillis
();
HttpRequest
request
=
HttpUtil
.
createPost
(
url
)
.
body
(
JSONUtil
.
toJsonStr
(
req
));
try
(
HttpResponse
response
=
request
.
execute
())
{
System
.
out
.
println
(
System
.
currentTimeMillis
()
-
start
);
String
res
=
response
.
body
();
JSONObject
object
=
JSONUtil
.
parseObj
(
res
);
DetectionVO
detectionRes
=
object
.
get
(
"data"
,
DetectionVO
.
class
);
System
.
out
.
println
(
JSONUtil
.
toJsonStr
(
detectionRes
));
}
}
public
static
void
imageOptimization
()
{
String
testImg
=
"C:/Users/11419/Desktop/Deskew/TestImages/6.png"
;
String
resImg
=
"C:/Users/11419/Desktop/Deskew/TestImages/6res.png"
;
String
url
=
URL
+
"imageOptimization"
;
OptimizationReq
req
=
new
OptimizationReq
();
req
.
setFilename
(
"4.png"
);
req
.
setFileContent
(
Base64
.
encode
(
FileUtil
.
file
(
testImg
)));
long
start
=
System
.
currentTimeMillis
();
HttpRequest
request
=
HttpUtil
.
createPost
(
url
)
.
body
(
JSONUtil
.
toJsonStr
(
req
));
try
(
HttpResponse
response
=
request
.
execute
())
{
System
.
out
.
println
(
System
.
currentTimeMillis
()-
start
);
String
res
=
response
.
body
();
JSONObject
object
=
JSONUtil
.
parseObj
(
res
);
OptimizationVO
optimizationVO
=
object
.
get
(
"data"
,
OptimizationVO
.
class
);
FileUtil
.
writeBytes
(
Base64
.
decode
(
optimizationVO
.
getFileContent
()),
FileUtil
.
file
(
resImg
));
optimizationVO
.
setFileContent
(
null
);
System
.
out
.
println
(
JSONUtil
.
toJsonStr
(
optimizationVO
));
}
}
}
imgproc-server/src/main/java/com/zq/imgproc/utils/Deskew.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
utils
;
import
cn.hutool.core.convert.Convert
;
import
org.opencv.core.*
;
import
org.opencv.imgcodecs.Imgcodecs
;
import
org.opencv.imgproc.Imgproc
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
static
org
.
opencv
.
imgproc
.
Imgproc
.
LINE_AA
;
public
class
Deskew
{
public
static
void
main
(
String
[]
args
)
{
System
.
load
(
"D:/project/imgproc/lib/opencv_java460.dll"
);
Mat
image
=
Imgcodecs
.
imread
(
"C:/Users/11419/Desktop/Deskew/TestImages/4.png"
);
int
angle
=
getDeskewAngle
(
image
);
System
.
out
.
println
(
angle
);
Mat
roateMat
=
rotate
(
image
,
angle
);
Imgcodecs
.
imwrite
(
"C:/Users/11419/Desktop/Deskew/TestImages/4res.png"
,
roateMat
);
}
public
static
Integer
getDeskewAngle
(
Mat
src
)
{
// 图片灰度化
Mat
gray
=
src
.
clone
();
Imgproc
.
cvtColor
(
src
,
gray
,
Imgproc
.
COLOR_BGR2GRAY
);
// // 高斯模糊(?)
// @Cleanup
// UMat blur = gray.clone();
// GaussianBlur(gray, blur, new Size(9, 9), 0);
// // 二值化(?)
// @Cleanup
// UMat thresh = blur.clone();
// threshold(blur, thresh, 0, 255, THRESH_BINARY_INV + THRESH_OTSU);
Mat
kernel
=
Imgproc
.
getStructuringElement
(
Imgproc
.
MORPH_RECT
,
new
Size
(
5
,
5
));
// 图片膨胀
Mat
erode
=
gray
.
clone
();
Imgproc
.
erode
(
gray
,
erode
,
kernel
);
// 图片腐蚀
Mat
dilate
=
erode
.
clone
();
Imgproc
.
dilate
(
erode
,
dilate
,
kernel
);
// 边缘检测
Mat
canny
=
dilate
.
clone
();
Imgproc
.
Canny
(
dilate
,
canny
,
50
,
150
);
// 霍夫变换得到线条
Mat
lines
=
new
Mat
();
//累加器阈值参数,小于设置值不返回
int
threshold
=
90
;
//最低线段长度,低于设置值则不返回
double
minLineLength
=
100
;
//间距小于该值的线当成同一条线
double
maxLineGap
=
10
;
// 霍夫变换,通过步长为1,角度为PI/180来搜索可能的直线
Imgproc
.
HoughLinesP
(
canny
,
lines
,
1
,
Math
.
PI
/
180
,
threshold
,
minLineLength
,
maxLineGap
);
// Mat mat = canny.clone();
// Scalar color = new Scalar(0, 0, 255);
// for (int i = 0; i < lines.rows(); i++) {
// double[] line = lines.get(i, 0);
// Imgproc.line(mat, new Point(line[0], line[1]), new Point(line[2], line[3]), color,3, LINE_AA);
// }
// Imgcodecs.imwrite("C:\\Users\\11419\\Desktop\\test\\bend2res.jpg", mat);
// 计算倾斜角度
List
<
Integer
>
angelList
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
lines
.
rows
();
i
++)
{
double
[]
line
=
lines
.
get
(
i
,
0
);
int
k
=
calculateAngle
(
line
[
0
],
line
[
1
],
line
[
2
],
line
[
3
]);
angelList
.
add
(
k
);
}
if
(
angelList
.
isEmpty
())
{
return
0
;
}
gray
.
release
();
kernel
.
release
();
erode
.
release
();
dilate
.
release
();
canny
.
release
();
lines
.
release
();
// 可能还得需要考虑方差来决定选择平均数还是众数
return
most
(
angelList
);
}
/**
* 求数组众数
*
* @param angelList 数组
* @return 数组众数
*/
private
static
int
most
(
List
<
Integer
>
angelList
)
{
if
(
angelList
.
isEmpty
())
{
return
0
;
}
int
res
=
0
;
int
max
=
Integer
.
MIN_VALUE
;
Map
<
Integer
,
Integer
>
map
=
new
HashMap
<>();
for
(
int
i
:
angelList
)
{
map
.
put
(
i
,
map
.
getOrDefault
(
i
,
0
)
+
1
);
}
for
(
Integer
i
:
map
.
keySet
())
{
int
count
=
map
.
get
(
i
);
if
(
count
>
max
)
{
max
=
count
;
res
=
i
;
}
}
return
res
;
}
/**
* 计算直线的倾斜角
*/
private
static
int
calculateAngle
(
double
x1
,
double
y1
,
double
x2
,
double
y2
)
{
double
dx
=
x2
-
x1
;
double
dy
=
y2
-
y1
;
if
(
Math
.
abs
(
dx
)
<
1
e
-
4
)
{
return
90
;
}
else
if
(
Math
.
abs
(
dy
)
<
1
e
-
4
)
{
return
0
;
}
else
{
double
radians
=
Math
.
atan2
(
dy
,
dx
);
double
degrees
=
Math
.
toDegrees
(
radians
);
return
Convert
.
toInt
(
Math
.
round
(
degrees
));
}
}
/**
* 图片旋转
*
* @param image 输入图片
* @param angle 旋转角度
* @return 输出图片
*/
public
static
Mat
rotate
(
Mat
image
,
double
angle
)
{
int
w
=
image
.
cols
();
int
h
=
image
.
rows
();
if
(
angle
<
0
)
{
angle
=
360
+
angle
;
}
Point
center
=
new
Point
((
double
)
w
/
2
,
(
double
)
h
/
2
);
double
scale
=
1.0
;
Mat
rotationMatrix
=
Imgproc
.
getRotationMatrix2D
(
center
,
angle
,
scale
);
// 设置填充颜色为白色
Scalar
backgroundColor
=
new
Scalar
(
255
,
255
,
255
);
Mat
rotatedImage
=
new
Mat
();
Imgproc
.
warpAffine
(
image
,
rotatedImage
,
rotationMatrix
,
new
Size
(
w
,
h
),
Imgproc
.
INTER_LINEAR
,
Core
.
BORDER_CONSTANT
,
backgroundColor
);
rotationMatrix
.
release
();
return
rotatedImage
;
}
}
imgproc-server/src/main/java/com/zq/imgproc/utils/ImageCorrectionUtil.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
utils
;
import
cn.hutool.core.util.RuntimeUtil
;
import
com.drew.imaging.ImageMetadataReader
;
import
com.drew.metadata.Directory
;
import
com.drew.metadata.Metadata
;
import
com.drew.metadata.Tag
;
import
javax.imageio.ImageIO
;
import
java.awt.*
;
import
java.awt.image.BufferedImage
;
import
java.io.File
;
import
java.io.FileOutputStream
;
/**
* <p>
* 纠偏
* </p>
*
* @author chenhao
* @since 2023/3/8 10:23
*/
public
class
ImageCorrectionUtil
{
/**
* 纠正图片旋转
*
* @param srcImgPath 图片地址
*/
public
static
void
correctImg
(
String
srcImgPath
)
{
FileOutputStream
fos
=
null
;
try
{
// 原始图片
File
srcFile
=
new
File
(
srcImgPath
);
// 获取偏转角度
int
angle
=
getAngle
(
srcFile
);
if
(
angle
!=
90
&&
angle
!=
270
)
{
return
;
}
// 原始图片缓存
BufferedImage
srcImg
=
ImageIO
.
read
(
srcFile
);
// 宽高互换
// 原始宽度
int
imgWidth
=
srcImg
.
getHeight
();
// 原始高度
int
imgHeight
=
srcImg
.
getWidth
();
// 中心点位置
double
centerWidth
=
((
double
)
imgWidth
)
/
2
;
double
centerHeight
=
((
double
)
imgHeight
)
/
2
;
// 图片缓存
BufferedImage
targetImg
=
new
BufferedImage
(
imgWidth
,
imgHeight
,
BufferedImage
.
TYPE_INT_RGB
);
// 旋转对应角度
Graphics2D
g
=
targetImg
.
createGraphics
();
g
.
rotate
(
Math
.
toRadians
(
angle
),
centerWidth
,
centerHeight
);
g
.
drawImage
(
srcImg
,
(
imgWidth
-
srcImg
.
getWidth
())
/
2
,
(
imgHeight
-
srcImg
.
getHeight
())
/
2
,
null
);
g
.
rotate
(
Math
.
toRadians
(-
angle
),
centerWidth
,
centerHeight
);
g
.
dispose
();
// 输出图片
fos
=
new
FileOutputStream
(
srcFile
);
ImageIO
.
write
(
targetImg
,
"jpg"
,
fos
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
finally
{
if
(
fos
!=
null
)
{
try
{
fos
.
flush
();
fos
.
close
();
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
}
}
/**
* 获取图片旋转角度
*
* @param file 上传图片
* @return 图片旋转角度
*/
private
static
int
getAngle
(
File
file
)
throws
Exception
{
Metadata
metadata
=
ImageMetadataReader
.
readMetadata
(
file
);
for
(
Directory
directory
:
metadata
.
getDirectories
())
{
for
(
Tag
tag
:
directory
.
getTags
())
{
if
(
"Orientation"
.
equals
(
tag
.
getTagName
()))
{
String
orientation
=
tag
.
getDescription
();
if
(
orientation
.
contains
(
"90"
))
{
return
90
;
}
else
if
(
orientation
.
contains
(
"180"
))
{
return
180
;
}
else
if
(
orientation
.
contains
(
"270"
))
{
return
270
;
}
}
}
}
return
0
;
}
/**
* 调用deskew工具
* Usage:
* deskew [-o output] [-a angle] [-b color] [..] input
* input: Input image file
* Options:
* -o output: 输出图像文件路径(默认:out.png)
* -a angle: 最大期望倾斜角度(两个方向)(默认:10度)
* -b color: 背景颜色十六进制格式RRGGBB|LL|AARRGGBB(默认为黑色)
* Ext. options:
* -q filter: 用于旋转的重采样过滤器 (default: linear,
* values: nearest|linear|cubic|lanczos)
* -t a|treshold: 自动阈值或0..255的值 (default: a)
* -r rect: 仅在内容矩形中进行倾斜检测 (pixels):
* left,top,right,bottom (default: whole page)
* -f format: 输出像素格式 (values: b1|g8|rgb24|rgba32)
* -l angle: 如果倾斜角度较小,则跳过倾斜步骤 (default: 0.01)
* -g flags: Operational flags (any combination of):
* c - auto crop, d - detect only (no output to file)
* -s info: 信息转储 (any combination of):
* s - skew detection stats, p - program parameters, t - timings
* -c specs: 某些文件格式的输出压缩规格。可以定义几个规格-用逗号分隔。支持规格:
* jXX - JPEG compression quality, XX is in range [1,100(best)]
* tSCHEME - TIFF compression scheme: none|lzw|rle|deflate|jpeg|g4
* Supported file formats
* Input: BMP, JPG, PNG, JNG, GIF, DDS, TGA, PBM, PGM, PPM, PAM, PFM, TIF, PSD
* Output: BMP, JPG, PNG, JNG, GIF, DDS, TGA, PGM, PPM, PAM, PFM, TIF, PSD
*
* @return 调用结果
*/
public
static
String
deskew
(
String
src
,
String
dst
,
String
deskewUrl
,
boolean
deskewAngle
)
{
StringBuilder
str
=
new
StringBuilder
();
// 命令行位置
str
.
append
(
deskewUrl
).
append
(
" "
);
// 背景颜色
str
.
append
(
"-b"
).
append
(
" "
).
append
(
"FFFFFF"
).
append
(
" "
);
if
(
deskewAngle
)
{
// 图片旋转耗时很多,跳过图片旋转
str
.
append
(
"-l"
).
append
(
" "
).
append
(
"80"
).
append
(
" "
);
}
// 输出路径
str
.
append
(
"-o"
).
append
(
" "
).
append
(
dst
).
append
(
" "
);
// 输入路径
str
.
append
(
src
);
return
RuntimeUtil
.
execForStr
(
str
.
toString
());
}
public
static
String
deskew2
(
String
src
,
String
dst
,
String
deskewpyUrl
)
{
StringBuilder
str
=
new
StringBuilder
();
// python启动
str
.
append
(
"python"
).
append
(
" "
);
// python文件位置
str
.
append
(
deskewpyUrl
).
append
(
" "
);
// 输入文件
str
.
append
(
"-i"
).
append
(
" "
).
append
(
src
).
append
(
" "
);
// 输出文件
str
.
append
(
"-o"
).
append
(
" "
).
append
(
dst
).
append
(
" "
);
return
RuntimeUtil
.
execForStr
(
str
.
toString
());
}
public
static
void
main
(
String
[]
args
)
{
String
path1
=
"C:\\Users\\11419\\Desktop\\Deskew\\TestImages\\3.png"
;
String
path2
=
"C:\\Users\\11419\\Desktop\\Deskew\\TestImages\\res3.png"
;
String
du
=
"C:\\Users\\11419\\Desktop\\lib\\correct.py"
;
String
res
=
deskew2
(
path1
,
path2
,
du
);
System
.
out
.
println
(
res
);
}
}
imgproc-server/src/main/java/com/zq/imgproc/utils/ImageUtil.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
utils
;
import
cn.hutool.core.convert.Convert
;
import
cn.hutool.core.img.ImgUtil
;
import
cn.hutool.core.io.FileUtil
;
import
cn.hutool.http.HttpUtil
;
import
cn.hutool.json.JSONObject
;
import
cn.hutool.json.JSONUtil
;
import
com.drew.imaging.ImageMetadataReader
;
import
com.drew.metadata.Directory
;
import
com.drew.metadata.Metadata
;
import
com.drew.metadata.Tag
;
import
com.zq.imgproc.exception.BusinessException
;
import
com.zq.imgproc.vo.ImgVO
;
import
org.apache.commons.imaging.ImageInfo
;
import
org.apache.commons.imaging.ImageReadException
;
import
org.apache.commons.imaging.Imaging
;
import
org.apache.commons.io.FileUtils
;
import
org.opencv.core.*
;
import
org.opencv.imgcodecs.Imgcodecs
;
import
org.opencv.imgproc.Imgproc
;
import
org.springframework.web.multipart.MultipartFile
;
import
java.awt.*
;
import
java.awt.image.BufferedImage
;
import
java.awt.image.DataBuffer
;
import
java.awt.image.DataBufferByte
;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.text.SimpleDateFormat
;
import
java.util.ArrayList
;
import
java.util.Date
;
import
java.util.List
;
import
java.util.Objects
;
/**
* <p>
* 图像处理工具类
* </p>
*
* @author chenhao
* @since 2023/3/10 15:06
*/
public
class
ImageUtil
{
/**
* 去黑边"全黑"阈值
*/
private
static
final
Integer
BLACK_VALUE
=
40
;
/**
* 使用Graphics2D进行旋转图片
*
* @param image 需要旋转的图片
* @param degree 旋转的角度
* @return 旋转后的图片
*/
public
static
BufferedImage
rotateImage
(
BufferedImage
image
,
double
degree
)
{
int
width
=
image
.
getWidth
();
int
height
=
image
.
getHeight
();
int
type
=
image
.
getType
();
BufferedImage
res
=
new
BufferedImage
(
width
,
height
,
type
);
Graphics2D
graphics
=
res
.
createGraphics
();
graphics
.
setRenderingHint
(
RenderingHints
.
KEY_INTERPOLATION
,
RenderingHints
.
VALUE_INTERPOLATION_BILINEAR
);
// 设置图片底色
graphics
.
setBackground
(
Color
.
BLACK
);
// 填充底图
graphics
.
fillRect
(
0
,
0
,
width
,
height
);
// 按中心点旋转图片
graphics
.
rotate
(
Math
.
toRadians
(
degree
),
width
>>
1
,
height
>>
1
);
graphics
.
drawImage
(
image
,
0
,
0
,
null
);
// 关闭Graphics2D
graphics
.
dispose
();
return
res
;
}
/**
* 使用Graphics2D进行旋转图片
*
* @param src 输入路径
* @param dst 输出路径
* @param degree 旋转角度
*/
public
static
void
rotateImage
(
String
src
,
String
dst
,
double
degree
)
{
BufferedImage
image
=
ImgUtil
.
read
(
src
);
ImgUtil
.
write
(
rotateImage
(
image
,
degree
),
FileUtil
.
file
(
dst
));
}
/**
* 将图片转换为BufferedImage.TYPE_3BYTE_BGR
*
* @param image 图片流
* @return 图片流
*/
public
static
BufferedImage
convert
(
BufferedImage
image
)
{
BufferedImage
convertedImage
=
new
BufferedImage
(
image
.
getWidth
(),
image
.
getHeight
(),
BufferedImage
.
TYPE_3BYTE_BGR
);
convertedImage
.
getGraphics
().
drawImage
(
image
,
0
,
0
,
null
);
return
convertedImage
;
}
/**
* 根据文件转换为mat对象
*
* @param file 文件
* @return Mat
*/
public
static
Mat
getMat
(
MultipartFile
file
)
throws
IOException
,
ImageReadException
{
ImageInfo
imageInfo
=
Imaging
.
getImageInfo
(
file
.
getInputStream
(),
file
.
getOriginalFilename
());
InputStream
inputStream
=
file
.
getInputStream
();
BufferedImage
image
=
ImgUtil
.
read
(
inputStream
);
image
=
ImageUtil
.
convert
(
image
);
DataBuffer
data
=
image
.
getRaster
().
getDataBuffer
();
byte
[]
bytes
=
((
DataBufferByte
)
data
).
getData
();
Mat
mat
=
new
Mat
(
imageInfo
.
getHeight
(),
imageInfo
.
getWidth
(),
CvType
.
CV_8UC3
);
mat
.
put
(
0
,
0
,
bytes
);
return
mat
;
}
/**
* 保存图片到指定位置
*
* @param mat 图片文件
* @param filePath 文件路径
*/
public
static
void
saveImage
(
Mat
mat
,
String
filePath
)
{
MatOfByte
matOfByte
=
new
MatOfByte
();
Imgcodecs
.
imencode
(
".png"
,
mat
,
matOfByte
);
byte
[]
byteArray
=
matOfByte
.
toArray
();
FileUtil
.
writeBytes
(
byteArray
,
filePath
);
}
/**
* 像素求和
*
* @param mat mat
* @return sum
*/
public
static
int
sum
(
Mat
mat
)
{
int
sum
=
0
;
for
(
int
row
=
0
;
row
<
mat
.
height
();
row
++)
{
for
(
int
col
=
0
;
col
<
mat
.
width
();
col
++)
{
sum
+=
mat
.
get
(
row
,
col
)[
0
];
}
}
return
sum
;
}
/**
* 图片灰度化
*
* @param src 图片地址
* @param dst 灰度化图片保存地址
*/
public
static
void
gray
(
String
src
,
String
dst
)
{
// 灰度化
Mat
mat
=
Imgcodecs
.
imread
(
src
);
Mat
gray
=
mat
.
clone
();
// if (mat.channels() == 4 || mat.channels() == 3) {
// Imgproc.cvtColor(mat, gray, Imgproc.COLOR_BGR2GRAY);
// } else if (mat.channels() == 2) {
// Imgproc.cvtColor(mat, gray, Imgproc.COLOR_BGR5652GRAY);
// } else {
// gray = mat;
// }
Imgproc
.
cvtColor
(
mat
,
gray
,
Imgproc
.
COLOR_BGR2GRAY
);
saveImage
(
gray
,
dst
);
gray
.
release
();
mat
.
release
();
}
/**
* 边缘检测
*
* @param src 图片地址
* @param dst 边缘检测图片保存地址
*/
public
static
void
canny
(
String
src
,
String
dst
)
{
Mat
mat
=
Imgcodecs
.
imread
(
src
);
Mat
cannyImg
=
canny
(
mat
);
ImageUtil
.
saveImage
(
cannyImg
,
dst
);
}
/**
* 灰度化图片后进行canny边缘检测
*
* @param src 图片矩阵
* @return 边缘检测之后的图片矩阵
*/
private
static
Mat
canny
(
Mat
src
)
{
// 灰度化
Mat
gray
=
src
.
clone
();
if
(
src
.
channels
()
==
4
||
src
.
channels
()
==
3
)
{
Imgproc
.
cvtColor
(
src
,
gray
,
Imgproc
.
COLOR_BGR2GRAY
);
}
else
if
(
src
.
channels
()
==
2
)
{
Imgproc
.
cvtColor
(
src
,
gray
,
Imgproc
.
COLOR_BGR5652GRAY
);
}
else
{
gray
=
src
;
}
Mat
mat
=
gray
.
clone
();
Imgproc
.
Canny
(
gray
,
mat
,
60
,
200
);
return
mat
;
}
/**
* 灰度化图片后进行canny边缘检测
*/
public
static
Mat
canny
(
Mat
src
,
int
threshold1
,
int
threshold2
,
int
apertureSize
)
{
// 灰度化
Mat
gray
=
new
Mat
();
if
(
src
.
channels
()
==
4
||
src
.
channels
()
==
3
)
{
Imgproc
.
cvtColor
(
src
,
gray
,
Imgproc
.
COLOR_BGR2GRAY
);
}
else
if
(
src
.
channels
()
==
2
)
{
Imgproc
.
cvtColor
(
src
,
gray
,
Imgproc
.
COLOR_BGR5652GRAY
);
}
else
{
gray
=
src
;
}
Mat
mat
=
new
Mat
();
Imgproc
.
Canny
(
gray
,
mat
,
threshold1
,
threshold2
,
apertureSize
);
gray
.
release
();
return
mat
;
}
/**
* 保存临时图片
*
* @param file 图片文件
* @return 图片保存地址
*/
public
static
String
saveTempFile
(
MultipartFile
file
)
{
try
{
String
originalFilename
=
file
.
getOriginalFilename
();
//取文件扩展名
String
ext
=
Objects
.
requireNonNull
(
originalFilename
).
substring
(
originalFilename
.
lastIndexOf
(
"."
)
+
1
);
//生成新文件名
String
name
=
UuidUtils
.
uuidNoDash
()
+
"."
+
ext
;
String
yyyyMMdd
=
new
SimpleDateFormat
(
"/yyyyMM/dd/"
).
format
(
new
Date
());
File
dest
=
new
File
(
"/data/temp/"
+
yyyyMMdd
+
name
);
FileUtils
.
writeByteArrayToFile
(
dest
,
file
.
getBytes
());
return
"/data/temp"
+
yyyyMMdd
+
name
;
}
catch
(
IOException
e
)
{
throw
new
BusinessException
(
"文件保存失败"
);
}
}
/**
* 图片弯曲矫正
*
* @param pathList 图片列表
* @return 弯曲矫正之后的图片列表
*/
public
static
List
correct
(
List
<
ImgVO
>
pathList
)
{
String
url
=
"http://ddns.gxmailu.com:18888/imgproc/correct"
;
String
body
=
JSONUtil
.
toJsonStr
(
pathList
);
String
res
=
HttpUtil
.
post
(
url
,
body
);
JSONObject
jsonObject
=
JSONUtil
.
parseObj
(
res
);
if
(
jsonObject
.
getByPath
(
"success"
,
Boolean
.
class
))
{
return
jsonObject
.
getByPath
(
"data"
,
ArrayList
.
class
);
}
else
{
return
pathList
;
}
}
/**
* 计算图片清晰度
*
* @param path 图片路径
* @return 图片清晰度
*/
public
static
double
imageSharpnessDetector
(
String
path
)
{
Mat
image
=
Imgcodecs
.
imread
(
path
);
Mat
grayImage
=
new
Mat
();
Imgproc
.
cvtColor
(
image
,
grayImage
,
Imgproc
.
COLOR_BGR2GRAY
);
// 计算拉普拉斯
Mat
laplacian
=
new
Mat
();
Imgproc
.
Laplacian
(
grayImage
,
laplacian
,
CvType
.
CV_64F
);
// 计算清晰度
MatOfDouble
mean
=
new
MatOfDouble
();
MatOfDouble
stdDev
=
new
MatOfDouble
();
Core
.
meanStdDev
(
laplacian
,
mean
,
stdDev
);
laplacian
.
release
();
// 计算拉普拉斯分数
return
Math
.
pow
(
stdDev
.
get
(
0
,
0
)[
0
],
2.0
);
}
/**
* Tenengrad梯度方法计算清晰度
* Tenengrad梯度方法利用Sobel算子分别计算水平和垂直方向的梯度,同一场景下梯度值越高,图像越清晰。
*
* @param image 图片矩阵
* @return 图片清晰度
*/
public
static
double
tenengrad
(
Mat
image
)
{
// 图片灰度化
Mat
grayImage
=
image
.
clone
();
Imgproc
.
cvtColor
(
image
,
grayImage
,
Imgproc
.
COLOR_BGR2GRAY
);
// Sobel算子
Mat
sobelImage
=
new
Mat
();
Imgproc
.
Sobel
(
grayImage
,
sobelImage
,
CvType
.
CV_16U
,
1
,
1
);
Scalar
mean
=
Core
.
mean
(
sobelImage
);
double
meanValue
=
mean
.
val
[
0
];
// 释放内存
sobelImage
.
release
();
grayImage
.
release
();
return
meanValue
;
}
/**
* Laplacian方法计算清晰度
*
* @param image 图片矩阵
* @return 图片清晰度
*/
public
static
double
laplacian
(
Mat
image
)
{
// 图片灰度化
Mat
grayImage
=
image
.
clone
();
Imgproc
.
cvtColor
(
image
,
grayImage
,
Imgproc
.
COLOR_BGR2GRAY
);
// Laplacian算子
Mat
laplacian
=
new
Mat
();
Imgproc
.
Laplacian
(
grayImage
,
laplacian
,
CvType
.
CV_16U
);
Scalar
mean
=
Core
.
mean
(
laplacian
);
double
meanValue
=
mean
.
val
[
0
];
// 释放内存
laplacian
.
release
();
grayImage
.
release
();
return
meanValue
;
}
/**
* 通过灰度方差获取图片清晰度
* 对焦清晰的图像相比对焦模糊的图像,它的数据之间的灰度差异应该更大,即它的方差应该较大,可以通过图像灰度数据的方差来衡量图像的清晰度,方差越大,表示清晰度越好。
*
* @param image 图片矩阵
* @return 图片清晰度
*/
public
static
double
variance
(
Mat
image
)
{
// 图片灰度化
Mat
grayImage
=
image
.
clone
();
Imgproc
.
cvtColor
(
image
,
grayImage
,
Imgproc
.
COLOR_BGR2GRAY
);
// 计算灰度图像的标准差
MatOfDouble
mean
=
new
MatOfDouble
();
MatOfDouble
stdDev
=
new
MatOfDouble
();
Core
.
meanStdDev
(
grayImage
,
mean
,
stdDev
);
double
meanValue
=
stdDev
.
get
(
0
,
0
)[
0
];
// 释放内存
mean
.
release
();
stdDev
.
release
();
grayImage
.
release
();
return
meanValue
;
}
/**
* 计算图片清晰度
*
* @param image 图片矩阵
* @return 图片清晰度
*/
public
static
double
imageSharpnessDetector
(
Mat
image
)
{
// 图片灰度化
Mat
grayImage
=
image
.
clone
();
Imgproc
.
cvtColor
(
image
,
grayImage
,
Imgproc
.
COLOR_BGR2GRAY
);
// 拉普拉斯计算
Mat
laplacian
=
new
Mat
();
Imgproc
.
Laplacian
(
grayImage
,
laplacian
,
CvType
.
CV_64F
);
// 计算清晰度
MatOfDouble
mean
=
new
MatOfDouble
();
MatOfDouble
stdDev
=
new
MatOfDouble
();
Core
.
meanStdDev
(
laplacian
,
mean
,
stdDev
);
// 释放内存
laplacian
.
release
();
grayImage
.
release
();
// 计算拉普拉斯分数
return
Math
.
pow
(
stdDev
.
get
(
0
,
0
)[
0
],
2.0
);
}
/**
* 计算图片平均亮度,范围是0到255
*
* @param path 图片路径
* @return 图片平均亮度
*/
public
static
double
brightness
(
String
path
)
{
Mat
grayImage
=
Imgcodecs
.
imread
(
path
,
Imgcodecs
.
IMREAD_GRAYSCALE
);
// 计算图像的平均亮度
Scalar
mean
=
Core
.
mean
(
grayImage
);
grayImage
.
release
();
return
mean
.
val
[
0
];
}
/**
* 计算图片平均亮度
*
* @param image 图片路径
* @return 图片平均亮度
*/
public
static
double
brightness
(
Mat
image
)
{
// 图片灰度化
Mat
grayImage
=
image
.
clone
();
Imgproc
.
cvtColor
(
image
,
grayImage
,
Imgproc
.
COLOR_BGR2GRAY
);
// 计算图像的平均亮度
Scalar
mean
=
Core
.
mean
(
grayImage
);
// 释放内存
grayImage
.
release
();
return
mean
.
val
[
0
];
}
/**
* 执行高斯模糊, UnsharpMasking
*
* @param path 图片路径
* @return 执行后的结果
*/
public
static
Mat
unsharpMasking
(
String
path
)
{
Mat
image
=
Imgcodecs
.
imread
(
path
);
// 高斯模糊
Mat
blurred
=
new
Mat
();
Imgproc
.
GaussianBlur
(
image
,
blurred
,
new
Size
(
3
,
3
),
0
);
// UnsharpMasking
Mat
unsharpMasked
=
new
Mat
();
Core
.
addWeighted
(
image
,
1.5
,
blurred
,
-
0.5
,
0
,
unsharpMasked
);
image
.
release
();
blurred
.
release
();
return
unsharpMasked
;
}
/**
* 执行高斯模糊, UnsharpMasking
*
* @param image 图片路径
* @return 执行后的结果
*/
public
static
Mat
unsharpMasking
(
Mat
image
)
{
// 高斯模糊
Mat
blurred
=
new
Mat
();
Imgproc
.
GaussianBlur
(
image
,
blurred
,
new
Size
(
3
,
3
),
0
);
// UnsharpMasking
Mat
unsharpMasked
=
new
Mat
();
Core
.
addWeighted
(
image
,
1.5
,
blurred
,
-
0.5
,
0
,
unsharpMasked
);
blurred
.
release
();
return
unsharpMasked
;
}
/**
* 黑边检测
*
* @param src 图片矩阵
* @return true表示可能存在黑边
*/
public
static
boolean
blackDetection
(
Mat
src
)
{
int
width
=
src
.
width
();
int
height
=
src
.
height
();
// 定义初始边界
int
top
=
0
;
int
left
=
0
;
int
right
=
width
-
1
;
int
bottom
=
height
-
1
;
// 上方黑边判断
for
(
int
row
=
0
;
row
<
height
;
row
++)
{
if
(
ImageUtil
.
sum
(
src
.
row
(
row
))
/
width
<
BLACK_VALUE
)
{
top
=
row
;
}
else
{
break
;
}
}
// 左边黑边判断
for
(
int
col
=
0
;
col
<
width
;
col
++)
{
if
(
ImageUtil
.
sum
(
src
.
col
(
col
))
/
height
<
BLACK_VALUE
)
{
left
=
col
;
}
else
{
break
;
}
}
// 右边黑边判断
for
(
int
col
=
width
-
1
;
col
>
0
;
col
--)
{
if
(
ImageUtil
.
sum
(
src
.
col
(
col
))
/
height
<
BLACK_VALUE
)
{
right
=
col
;
}
else
{
break
;
}
}
// 下方黑边判断
for
(
int
row
=
height
-
1
;
row
>
0
;
row
--)
{
if
(
ImageUtil
.
sum
(
src
.
row
(
row
))
/
width
<
BLACK_VALUE
)
{
bottom
=
row
;
}
else
{
break
;
}
}
// 若是边界没有被更新,认为不存在黑边
return
top
!=
0
||
left
!=
0
||
right
!=
width
-
1
||
bottom
!=
height
-
1
;
}
/**
* 获取图片DPI
*
* @param file 图片文件
* @return [水平DPI,垂直DPI]
*/
public
static
Integer
getDpi
(
MultipartFile
file
)
throws
Exception
{
int
res
;
ImageInfo
imageInfo
=
Imaging
.
getImageInfo
(
file
.
getInputStream
(),
file
.
getOriginalFilename
());
res
=
imageInfo
.
getPhysicalWidthDpi
();
if
(
res
==
-
1
)
{
Metadata
metadata
=
ImageMetadataReader
.
readMetadata
(
file
.
getInputStream
());
for
(
Directory
directory
:
metadata
.
getDirectories
())
{
for
(
Tag
tag
:
directory
.
getTags
())
{
if
(
"X Resolution"
.
equals
(
tag
.
getTagName
()))
{
res
=
Convert
.
toInt
(
tag
.
getDescription
());
}
}
}
}
return
res
;
}
/**
* 获取图片DPI
*
* @param file 图片文件
* @return [水平DPI,垂直DPI]
*/
public
static
Integer
getDpi
(
File
file
)
throws
Exception
{
int
res
;
ImageInfo
imageInfo
=
Imaging
.
getImageInfo
(
file
);
res
=
imageInfo
.
getPhysicalWidthDpi
();
if
(
res
==
-
1
)
{
Metadata
metadata
=
ImageMetadataReader
.
readMetadata
(
file
);
for
(
Directory
directory
:
metadata
.
getDirectories
())
{
for
(
Tag
tag
:
directory
.
getTags
())
{
if
(
"X Resolution"
.
equals
(
tag
.
getTagName
()))
{
res
=
Convert
.
toInt
(
tag
.
getDescription
());
}
}
}
}
return
res
;
}
}
imgproc-server/src/main/java/com/zq/imgproc/utils/RemoveBlackUtil.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
utils
;
import
org.opencv.core.*
;
import
org.opencv.imgcodecs.Imgcodecs
;
import
org.opencv.imgproc.Imgproc
;
import
org.opencv.utils.Converters
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Random
;
/**
* <p>
* 去黑边
* </p>
*
* @author chenhao
* @since 2023/3/8 9:31
*/
public
class
RemoveBlackUtil
{
public
static
void
main
(
String
[]
args
)
{
String
testImg
=
"C:\\Users\\11419\\Desktop\\test\\a.jpg"
;
String
resImg
=
"C:\\Users\\11419\\Desktop\\test\\8.jpg"
;
System
.
load
(
"D:\\project\\imgproc\\lib\\opencv_java460.dll"
);
remove
(
testImg
,
resImg
);
}
public
static
void
remove
(
String
src
,
String
dst
)
{
Mat
img
=
Imgcodecs
.
imread
(
src
);
if
(
img
.
empty
()){
return
;
}
Mat
greyImg
=
img
.
clone
();
//1.彩色转灰色
Imgproc
.
cvtColor
(
img
,
greyImg
,
Imgproc
.
COLOR_BGR2GRAY
);
ImageUtil
.
saveImage
(
greyImg
,
"C:\\Users\\11419\\Desktop\\test\\1.jpg"
);
Mat
gaussianBlurImg
=
greyImg
.
clone
();
// 2.高斯滤波,降噪
Imgproc
.
GaussianBlur
(
greyImg
,
gaussianBlurImg
,
new
Size
(
3
,
3
),
0
);
ImageUtil
.
saveImage
(
greyImg
,
"C:\\Users\\11419\\Desktop\\test\\2.jpg"
);
// 3.Canny边缘检测
Mat
cannyImg
=
gaussianBlurImg
.
clone
();
Imgproc
.
Canny
(
gaussianBlurImg
,
cannyImg
,
50
,
200
);
ImageUtil
.
saveImage
(
cannyImg
,
"C:\\Users\\11419\\Desktop\\test\\3.jpg"
);
// 4.膨胀,连接边缘
Mat
dilateImg
=
cannyImg
.
clone
();
Imgproc
.
dilate
(
cannyImg
,
dilateImg
,
new
Mat
(),
new
Point
(-
1
,
-
1
),
3
,
1
,
new
Scalar
(
1
));
ImageUtil
.
saveImage
(
dilateImg
,
"C:\\Users\\11419\\Desktop\\test\\4.jpg"
);
//5.对边缘检测的结果图再进行轮廓提取
List
<
MatOfPoint
>
contours
=
new
ArrayList
<>();
List
<
MatOfPoint
>
drawContours
=
new
ArrayList
<>();
Mat
hierarchy
=
new
Mat
();
Imgproc
.
findContours
(
dilateImg
,
contours
,
hierarchy
,
Imgproc
.
RETR_EXTERNAL
,
Imgproc
.
CHAIN_APPROX_SIMPLE
);
Mat
linePic
=
Mat
.
zeros
(
dilateImg
.
rows
(),
dilateImg
.
cols
(),
CvType
.
CV_8UC3
);
//6.找出轮廓对应凸包的四边形拟合
List
<
MatOfPoint
>
squares
=
new
ArrayList
<>();
List
<
MatOfPoint
>
hulls
=
new
ArrayList
<>();
MatOfInt
hull
=
new
MatOfInt
();
MatOfPoint2f
approx
=
new
MatOfPoint2f
();
approx
.
convertTo
(
approx
,
CvType
.
CV_32F
);
for
(
MatOfPoint
contour
:
contours
)
{
// 边框的凸包
Imgproc
.
convexHull
(
contour
,
hull
);
// 用凸包计算出新的轮廓点
Point
[]
contourPoints
=
contour
.
toArray
();
int
[]
indices
=
hull
.
toArray
();
List
<
Point
>
newPoints
=
new
ArrayList
<>();
for
(
int
index
:
indices
)
{
newPoints
.
add
(
contourPoints
[
index
]);
}
MatOfPoint2f
contourHull
=
new
MatOfPoint2f
();
contourHull
.
fromList
(
newPoints
);
// 多边形拟合凸包边框(此时的拟合的精度较低)
Imgproc
.
approxPolyDP
(
contourHull
,
approx
,
Imgproc
.
arcLength
(
contourHull
,
true
)
*
0.02
,
true
);
MatOfPoint
mat
=
new
MatOfPoint
();
mat
.
fromArray
(
approx
.
toArray
());
drawContours
.
add
(
mat
);
// 筛选出面积大于某一阈值的,且四边形的各个角度都接近直角的凸四边形
MatOfPoint
approxf1
=
new
MatOfPoint
();
approx
.
convertTo
(
approxf1
,
CvType
.
CV_32S
);
if
(
approx
.
rows
()
==
4
&&
Math
.
abs
(
Imgproc
.
contourArea
(
approx
))
>
40000
&&
Imgproc
.
isContourConvex
(
approxf1
))
{
double
maxCosine
=
0
;
for
(
int
j
=
2
;
j
<
5
;
j
++)
{
double
cosine
=
Math
.
abs
(
getAngle
(
approxf1
.
toArray
()[
j
%
4
],
approxf1
.
toArray
()[
j
-
2
],
approxf1
.
toArray
()[
j
-
1
]));
maxCosine
=
Math
.
max
(
maxCosine
,
cosine
);
}
// 角度大概72度
if
(
maxCosine
<
0.3
)
{
MatOfPoint
tmp
=
new
MatOfPoint
();
contourHull
.
convertTo
(
tmp
,
CvType
.
CV_32S
);
squares
.
add
(
approxf1
);
hulls
.
add
(
tmp
);
}
}
}
//这里是把提取出来的轮廓通过不同颜色的线描述出来,具体效果可以自己去看
Random
r
=
new
Random
();
for
(
int
i
=
0
;
i
<
drawContours
.
size
();
i
++)
{
Imgproc
.
drawContours
(
linePic
,
drawContours
,
i
,
new
Scalar
(
r
.
nextInt
(
255
),
r
.
nextInt
(
255
),
r
.
nextInt
(
255
)));
}
ImageUtil
.
saveImage
(
linePic
,
"C:\\Users\\11419\\Desktop\\test\\5.jpg"
);
//7.找出最大的矩形
int
index
=
findLargestSquare
(
squares
);
MatOfPoint
largest_square
;
if
(
squares
.
size
()
>
0
){
largest_square
=
squares
.
get
(
index
);
}
else
{
System
.
out
.
println
(
"图片无法识别"
);
return
;
}
Mat
polyPic
=
Mat
.
zeros
(
img
.
size
(),
CvType
.
CV_8UC3
);
Imgproc
.
drawContours
(
polyPic
,
squares
,
index
,
new
Scalar
(
0
,
0
,
255
),
2
);
ImageUtil
.
saveImage
(
polyPic
,
"C:\\Users\\11419\\Desktop\\test\\6.jpg"
);
//存储矩形的四个凸点
hull
=
new
MatOfInt
();
Imgproc
.
convexHull
(
largest_square
,
hull
,
false
);
List
<
Integer
>
hullList
=
hull
.
toList
();
List
<
Point
>
polyContoursList
=
largest_square
.
toList
();
List
<
Point
>
hullPointList
=
new
ArrayList
<>();
for
(
Integer
integer
:
hullList
)
{
Imgproc
.
circle
(
polyPic
,
polyContoursList
.
get
(
integer
),
10
,
new
Scalar
(
r
.
nextInt
(
255
),
r
.
nextInt
(
255
),
r
.
nextInt
(
255
),
3
));
hullPointList
.
add
(
polyContoursList
.
get
(
integer
));
}
Core
.
addWeighted
(
polyPic
,
1
,
img
,
1
,
0
,
img
);
ImageUtil
.
saveImage
(
img
,
"C:\\Users\\11419\\Desktop\\test\\7.jpg"
);
List
<
Point
>
lastHullPointList
=
new
ArrayList
<>(
hullPointList
);
//dstPoints储存的是变换后各点的坐标,依次为左上,右上,右下, 左下
//srcPoints储存的是上面得到的四个角的坐标
Point
[]
dstPoints
=
{
new
Point
(
0
,
0
),
new
Point
(
img
.
cols
(),
0
),
new
Point
(
img
.
cols
(),
img
.
rows
()),
new
Point
(
0
,
img
.
rows
())};
Point
[]
srcPoints
=
new
Point
[
4
];
boolean
sorted
=
false
;
int
n
=
4
;
//对四个点进行排序 分出左上 右上 右下 左下
while
(!
sorted
&&
n
>
0
){
for
(
int
i
=
1
;
i
<
n
;
i
++){
sorted
=
true
;
if
(
lastHullPointList
.
get
(
i
-
1
).
x
>
lastHullPointList
.
get
(
i
).
x
){
Point
tempp1
=
lastHullPointList
.
get
(
i
);
Point
tempp2
=
lastHullPointList
.
get
(
i
-
1
);
lastHullPointList
.
set
(
i
,
tempp2
);
lastHullPointList
.
set
(
i
-
1
,
tempp1
);
sorted
=
false
;
}
}
n
--;
}
//即先对四个点的x坐标进行冒泡排序分出左右,再根据两对坐标的y值比较分出上下
if
(
lastHullPointList
.
get
(
0
).
y
<
lastHullPointList
.
get
(
1
).
y
){
srcPoints
[
0
]
=
lastHullPointList
.
get
(
0
);
srcPoints
[
3
]
=
lastHullPointList
.
get
(
1
);
}
else
{
srcPoints
[
0
]
=
lastHullPointList
.
get
(
1
);
srcPoints
[
3
]
=
lastHullPointList
.
get
(
0
);
}
if
(
lastHullPointList
.
get
(
2
).
y
<
lastHullPointList
.
get
(
3
).
y
){
srcPoints
[
1
]
=
lastHullPointList
.
get
(
2
);
srcPoints
[
2
]
=
lastHullPointList
.
get
(
3
);
}
else
{
srcPoints
[
1
]
=
lastHullPointList
.
get
(
3
);
srcPoints
[
2
]
=
lastHullPointList
.
get
(
2
);
}
List
<
Point
>
listSrcs
=
java
.
util
.
Arrays
.
asList
(
srcPoints
[
0
],
srcPoints
[
1
],
srcPoints
[
2
],
srcPoints
[
3
]);
Mat
srcPointsMat
=
Converters
.
vector_Point_to_Mat
(
listSrcs
,
CvType
.
CV_32F
);
List
<
Point
>
dstSrcs
=
java
.
util
.
Arrays
.
asList
(
dstPoints
[
0
],
dstPoints
[
1
],
dstPoints
[
2
],
dstPoints
[
3
]);
Mat
dstPointsMat
=
Converters
.
vector_Point_to_Mat
(
dstSrcs
,
CvType
.
CV_32F
);
//参数分别为输入输出图像、变换矩阵、大小。
//坐标变换后就得到了我们要的最终图像。
Mat
transMat
=
Imgproc
.
getPerspectiveTransform
(
srcPointsMat
,
dstPointsMat
);
//得到变换矩阵
Mat
outPic
=
new
Mat
();
Imgproc
.
warpPerspective
(
img
,
outPic
,
transMat
,
img
.
size
());
ImageUtil
.
saveImage
(
outPic
,
dst
);
}
// 根据三个点计算中间那个点的夹角 pt1 pt0 pt2
private
static
double
getAngle
(
Point
pt1
,
Point
pt2
,
Point
pt0
)
{
double
dx1
=
pt1
.
x
-
pt0
.
x
;
double
dy1
=
pt1
.
y
-
pt0
.
y
;
double
dx2
=
pt2
.
x
-
pt0
.
x
;
double
dy2
=
pt2
.
y
-
pt0
.
y
;
return
(
dx1
*
dx2
+
dy1
*
dy2
)/
Math
.
sqrt
((
dx1
*
dx1
+
dy1
*
dy1
)*(
dx2
*
dx2
+
dy2
*
dy2
)
+
1
e
-
10
);
}
// 找到最大的正方形轮廓
private
static
int
findLargestSquare
(
List
<
MatOfPoint
>
squares
)
{
if
(
squares
.
size
()
==
0
)
{
return
-
1
;
}
int
max_width
=
0
;
int
max_height
=
0
;
int
max_square_idx
=
0
;
int
currentIndex
=
0
;
for
(
MatOfPoint
square
:
squares
)
{
Rect
rectangle
=
Imgproc
.
boundingRect
(
square
);
if
(
rectangle
.
width
>=
max_width
&&
rectangle
.
height
>=
max_height
)
{
max_width
=
rectangle
.
width
;
max_height
=
rectangle
.
height
;
max_square_idx
=
currentIndex
;
}
currentIndex
++;
}
return
max_square_idx
;
}
}
imgproc-server/src/main/java/com/zq/imgproc/utils/RemoveBlackUtil2.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
utils
;
import
org.opencv.core.Mat
;
import
org.opencv.core.Rect
;
import
org.opencv.imgcodecs.Imgcodecs
;
import
org.opencv.imgproc.Imgproc
;
/**
* <p>
* 去黑边
* </p>
*
* @author chenhao
* @since 2023/3/10 15:01
*/
public
class
RemoveBlackUtil2
{
/**
* 去黑边"全黑"阈值
*/
private
static
final
Integer
BLACK_VALUE
=
100
;
public
static
void
remove
(
String
src
,
String
dst
)
{
Mat
mat
=
Imgcodecs
.
imread
(
src
);
Mat
res
=
removeBlackEdge
(
mat
);
ImageUtil
.
saveImage
(
res
,
dst
);
mat
.
release
();
res
.
release
();
}
/**
* 去除图片黑边,若无黑边,则原图返回。默认“全黑”阈值为 {@code BLACK_VALUE}
*
* @param srcMat 预去除黑边的Mat
* @return 去除黑边之后的Mat
*/
private
static
Mat
removeBlackEdge
(
Mat
srcMat
)
{
return
removeBlackEdge
(
srcMat
,
BLACK_VALUE
);
}
/**
* 去除图片黑边,若无黑边,则原图返回。
*
* @param blackValue 一般低于5的已经是很黑的颜色了
* @param srcMat 源Mat对象
* @return Mat对象
*/
private
static
Mat
removeBlackEdge
(
Mat
srcMat
,
int
blackValue
)
{
// 灰度化
Mat
grayMat
=
gray
(
srcMat
);
// 定义边界
int
topRow
=
0
;
int
leftCol
=
0
;
int
rightCol
=
grayMat
.
width
()
-
1
;
int
bottomRow
=
grayMat
.
height
()
-
1
;
// 上方黑边判断
for
(
int
row
=
0
;
row
<
grayMat
.
height
();
row
++)
{
if
(
ImageUtil
.
sum
(
grayMat
.
row
(
row
))
/
grayMat
.
width
()
<
blackValue
)
{
topRow
=
row
;
}
else
{
break
;
}
}
// 左边黑边判断
for
(
int
col
=
0
;
col
<
grayMat
.
width
();
col
++)
{
if
(
ImageUtil
.
sum
(
grayMat
.
col
(
col
))
/
grayMat
.
height
()
<
blackValue
)
{
leftCol
=
col
;
}
else
{
break
;
}
}
// 右边黑边判断
for
(
int
col
=
grayMat
.
width
()
-
1
;
col
>
0
;
col
--)
{
if
(
ImageUtil
.
sum
(
grayMat
.
col
(
col
))
/
grayMat
.
height
()
<
blackValue
)
{
rightCol
=
col
;
}
else
{
break
;
}
}
// 下方黑边判断
for
(
int
row
=
grayMat
.
height
()
-
1
;
row
>
0
;
row
--)
{
if
(
ImageUtil
.
sum
(
grayMat
.
row
(
row
))
/
grayMat
.
width
()
<
blackValue
)
{
bottomRow
=
row
;
}
else
{
break
;
}
}
int
x
=
leftCol
;
int
y
=
topRow
;
int
width
=
rightCol
-
leftCol
;
int
height
=
bottomRow
-
topRow
;
grayMat
.
release
();
if
(
leftCol
==
0
&&
rightCol
==
grayMat
.
width
()
-
1
&&
topRow
==
0
&&
bottomRow
==
grayMat
.
height
()
-
1
)
{
return
srcMat
;
}
return
cut
(
srcMat
,
x
,
y
,
width
,
height
);
}
/**
* 灰度处理 BGR灰度处理
*
* @param src 原图Mat
* @return Mat 灰度后的Mat
*/
private
static
Mat
gray
(
Mat
src
)
{
Mat
gray
=
new
Mat
();
Imgproc
.
cvtColor
(
src
,
gray
,
Imgproc
.
COLOR_BGR2GRAY
);
return
gray
;
}
/**
* 按照指定的尺寸截取Mat,坐标原点为左上角
*
* @param src 源Mat
* @param x x
* @param y y
* @param width width
* @param height height
* @return 截取后的Mat
*/
private
static
Mat
cut
(
Mat
src
,
int
x
,
int
y
,
int
width
,
int
height
)
{
if
(
x
<
0
)
{
x
=
0
;
}
if
(
y
<
0
)
{
y
=
0
;
}
if
(
width
>
src
.
width
())
{
width
=
src
.
width
();
}
if
(
height
>
src
.
height
())
{
height
=
src
.
height
();
}
// 截取尺寸
Rect
rect
=
new
Rect
(
x
,
y
,
width
,
height
);
return
new
Mat
(
src
,
rect
);
}
}
imgproc-server/src/main/java/com/zq/imgproc/utils/ResultVo.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
utils
;
import
com.fasterxml.jackson.annotation.JsonIgnoreProperties
;
import
com.fasterxml.jackson.annotation.JsonInclude
;
import
io.swagger.annotations.ApiModel
;
import
io.swagger.annotations.ApiModelProperty
;
import
java.io.Serializable
;
/**
* 统一的接口响应信息
*
* @author wilmiam
* @since 2021-07-09 17:45
*/
@ApiModel
(
"API响应消息"
)
@JsonInclude
(
JsonInclude
.
Include
.
NON_NULL
)
@JsonIgnoreProperties
(
ignoreUnknown
=
true
)
public
class
ResultVo
<
T
>
implements
Serializable
{
@ApiModelProperty
(
value
=
"成功标记"
,
example
=
"true"
)
private
boolean
success
;
@ApiModelProperty
(
"错误码"
)
@JsonInclude
(
JsonInclude
.
Include
.
NON_DEFAULT
)
private
int
errCode
;
@ApiModelProperty
(
"错误信息"
)
private
String
errMsg
;
@ApiModelProperty
(
"响应的数据"
)
private
T
data
;
public
static
ResultVo
success
()
{
return
success
(
null
);
}
public
static
<
E
>
ResultVo
<
E
>
success
(
E
data
)
{
ResultVo
<
E
>
result
=
new
ResultVo
<>();
result
.
setSuccess
(
true
);
result
.
setData
(
data
);
return
result
;
}
public
static
ResultVo
fail
(
String
errMsg
)
{
return
fail
(
500
,
errMsg
);
}
public
static
ResultVo
fail
(
int
errCode
,
String
errMsg
)
{
ResultVo
result
=
new
ResultVo
<>();
result
.
setSuccess
(
false
);
result
.
setErrCode
(
errCode
);
result
.
setErrMsg
(
errMsg
);
return
result
;
}
public
boolean
isSuccess
()
{
return
success
;
}
public
void
setSuccess
(
boolean
success
)
{
this
.
success
=
success
;
}
public
int
getErrCode
()
{
return
errCode
;
}
public
void
setErrCode
(
int
errCode
)
{
this
.
errCode
=
errCode
;
}
public
String
getErrMsg
()
{
return
errMsg
;
}
public
void
setErrMsg
(
String
errMsg
)
{
this
.
errMsg
=
errMsg
;
}
public
T
getData
()
{
return
data
;
}
public
void
setData
(
T
data
)
{
this
.
data
=
data
;
}
}
imgproc-server/src/main/java/com/zq/imgproc/utils/Test2.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
utils
;
import
cn.hutool.core.convert.Convert
;
import
cn.hutool.core.io.FileUtil
;
import
cn.hutool.json.JSONUtil
;
import
com.zq.imgproc.vo.DetectionVO
;
import
lombok.extern.slf4j.Slf4j
;
import
org.opencv.core.Core
;
import
org.opencv.core.Mat
;
import
org.opencv.core.Scalar
;
import
org.opencv.imgcodecs.Imgcodecs
;
import
java.math.BigDecimal
;
import
java.math.RoundingMode
;
/**
* <p>
*
* </p>
*
* @author chenhao
* @since 2023/11/20
*/
@Slf4j
public
class
Test2
{
public
static
void
main
(
String
[]
args
)
throws
Exception
{
System
.
load
(
"D:\\project\\imgproc\\lib\\opencv_java460.dll"
);
String
src
=
"C:\\Users\\11419\\Desktop\\test\\black1.png"
;
String
dst
=
"C:\\Users\\11419\\Desktop\\Deskew\\TestImages\\6res.png"
;
long
start
=
System
.
currentTimeMillis
();
DetectionVO
vo
=
new
DetectionVO
();
Mat
image
=
Imgcodecs
.
imread
(
src
);
log
.
info
(
"加载图片耗时为:--------------{}"
,
System
.
currentTimeMillis
()
-
start
);
start
=
System
.
currentTimeMillis
();
// 检测图片的分辨率
vo
.
setWidthResolution
(
image
.
width
());
vo
.
setHeightResolution
(
image
.
height
());
log
.
info
(
"检测图片分辨率耗时为:--------------{}"
,
System
.
currentTimeMillis
()
-
start
);
start
=
System
.
currentTimeMillis
();
// 检测图片的DPI
vo
.
setDpi
(
ImageUtil
.
getDpi
(
FileUtil
.
file
(
src
)));
log
.
info
(
"检测图片DPI耗时为:--------------{}"
,
System
.
currentTimeMillis
()
-
start
);
start
=
System
.
currentTimeMillis
();
// 检测图片清晰度
BigDecimal
clarity
=
BigDecimal
.
valueOf
(
ImageUtil
.
tenengrad
(
image
));
vo
.
setClarity
(
clarity
.
setScale
(
2
,
RoundingMode
.
HALF_UP
).
doubleValue
());
log
.
info
(
"检测图片清晰度耗时为:--------------{}"
,
System
.
currentTimeMillis
()
-
start
);
start
=
System
.
currentTimeMillis
();
// 检测图片的亮度
vo
.
setBrightness
(
Convert
.
toInt
(
ImageUtil
.
brightness
(
image
)));
log
.
info
(
"检测图片亮度耗时为:--------------{}"
,
System
.
currentTimeMillis
()
-
start
);
start
=
System
.
currentTimeMillis
();
// 检测图片倾斜角度
vo
.
setAngle
(
Deskew
.
getDeskewAngle
(
image
));
log
.
info
(
"检测图片倾斜角度耗时为:--------------{}"
,
System
.
currentTimeMillis
()
-
start
);
start
=
System
.
currentTimeMillis
();
// 检测图片的黑边
blackDetection2
(
image
);
blackDetection3
(
image
);
System
.
out
.
println
(
JSONUtil
.
toJsonStr
(
vo
));
}
/**
* 黑边检测
*
* @param src 图片矩阵
* @return true表示可能存在黑边
*/
public
static
boolean
blackDetection
(
Mat
src
)
{
int
BLACK_VALUE
=
50
;
int
width
=
src
.
width
();
int
height
=
src
.
height
();
// 定义初始边界
int
top
=
0
;
int
left
=
0
;
int
right
=
width
-
1
;
int
bottom
=
height
-
1
;
// 上方黑边判断
for
(
int
row
=
0
;
row
<
height
;
row
++)
{
if
(
ImageUtil
.
sum
(
src
.
row
(
row
))
/
width
<
BLACK_VALUE
)
{
top
=
row
;
}
else
{
break
;
}
}
// 左边黑边判断
for
(
int
col
=
0
;
col
<
width
;
col
++)
{
if
(
ImageUtil
.
sum
(
src
.
col
(
col
))
/
height
<
BLACK_VALUE
)
{
left
=
col
;
}
else
{
break
;
}
}
// 右边黑边判断
for
(
int
col
=
width
-
1
;
col
>
0
;
col
--)
{
if
(
ImageUtil
.
sum
(
src
.
col
(
col
))
/
height
<
BLACK_VALUE
)
{
right
=
col
;
}
else
{
break
;
}
}
// 下方黑边判断
for
(
int
row
=
height
-
1
;
row
>
0
;
row
--)
{
if
(
ImageUtil
.
sum
(
src
.
row
(
row
))
/
width
<
BLACK_VALUE
)
{
bottom
=
row
;
}
else
{
break
;
}
}
// 若是边界没有被更新,认为不存在黑边
return
top
!=
0
||
left
!=
0
||
right
!=
width
-
1
||
bottom
!=
height
-
1
;
}
public
static
void
blackDetection2
(
Mat
src
)
{
long
start
=
System
.
currentTimeMillis
();
int
BLACK_VALUE
=
30
;
int
width
=
src
.
width
();
int
height
=
src
.
height
();
int
rows
=
0
;
int
cols
=
0
;
// 上方黑边判断
for
(
int
row
=
0
;
row
<
height
;
row
++)
{
if
(
ImageUtil
.
sum
(
src
.
row
(
row
))
/
width
<
BLACK_VALUE
)
{
rows
++;
}
}
// 左边黑边判断
for
(
int
col
=
0
;
col
<
width
;
col
++)
{
if
(
ImageUtil
.
sum
(
src
.
col
(
col
))
/
height
<
BLACK_VALUE
)
{
cols
++;
}
}
log
.
info
(
"{}, {}, {}, {}"
,
"22222"
,
rows
,
cols
,
System
.
currentTimeMillis
()
-
start
);
}
public
static
void
blackDetection3
(
Mat
src
)
{
long
start
=
System
.
currentTimeMillis
();
int
BLACK_VALUE
=
30
;
int
width
=
src
.
width
();
int
height
=
src
.
height
();
int
rows
=
0
;
int
cols
=
0
;
int
limitRows
=
500
;
int
limitCols
=
500
;
for
(
int
row
=
0
;
row
<
height
;
row
++)
{
Scalar
mean
=
Core
.
mean
(
src
.
row
(
row
));
if
(
Convert
.
toInt
(
mean
.
val
[
0
])
<
BLACK_VALUE
)
{
rows
++;
}
}
for
(
int
col
=
0
;
col
<
width
;
col
++)
{
Scalar
mean
=
Core
.
mean
(
src
.
col
(
col
));
if
(
Convert
.
toInt
(
mean
.
val
[
0
])
<
BLACK_VALUE
)
{
cols
++;
}
}
log
.
info
(
"{}, {}, {}, {}"
,
"3333333"
,
rows
,
cols
,
System
.
currentTimeMillis
()
-
start
);
}
}
imgproc-server/src/main/java/com/zq/imgproc/utils/TestUtil.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
utils
;
import
cn.hutool.core.convert.Convert
;
import
cn.hutool.core.io.FileUtil
;
import
com.drew.imaging.ImageMetadataReader
;
import
com.drew.metadata.Directory
;
import
com.drew.metadata.Metadata
;
import
com.drew.metadata.Tag
;
import
com.zq.imgproc.vo.DetectionVO
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.imaging.ImageInfo
;
import
org.apache.commons.imaging.Imaging
;
import
org.opencv.core.Mat
;
import
org.opencv.imgcodecs.Imgcodecs
;
import
java.io.File
;
@Slf4j
public
class
TestUtil
{
public
static
void
main
(
String
[]
args
)
throws
Exception
{
System
.
load
(
"D:/project/imgproc/lib/opencv_java460.dll"
);
String
src
=
"C:\\Users\\11419\\Desktop\\4.png"
;
String
dst
=
"C:\\Users\\11419\\Desktop\\res.png"
;
// deskew(src, dst);
RemoveBlackUtil2
.
remove
(
src
,
dst
);
// bright(src, dst);
// im(src, dst);
}
public
static
void
deskew
(
String
src
,
String
dst
)
{
Mat
mat
=
Imgcodecs
.
imread
(
src
);
double
skewAngle
=
Deskew
.
getDeskewAngle
(
mat
);
Mat
rotateMat
=
Deskew
.
rotate
(
mat
,
skewAngle
);
Imgcodecs
.
imwrite
(
dst
,
rotateMat
);
}
public
static
void
removeBlack
(
String
src
,
String
dst
)
{
RemoveBlackUtil2
.
remove
(
src
,
dst
);
}
public
static
void
bright
(
String
src
,
String
dst
)
{
double
brightness
=
ImageUtil
.
brightness
(
src
);
System
.
out
.
println
(
brightness
);
// 亮度异常执行亮度矫正
if
(
brightness
<
100
||
brightness
>
500
)
{
// 计算亮度调整值
double
alpha
=
300
/
brightness
;
// 执行亮度调整
Mat
grayImage
=
Imgcodecs
.
imread
(
src
);
Mat
adjustedImage
=
new
Mat
();
grayImage
.
convertTo
(
adjustedImage
,
-
1
,
alpha
,
0
);
// 保存调整后的图像
Imgcodecs
.
imwrite
(
dst
,
adjustedImage
);
}
}
public
static
void
im
(
String
src
,
String
dst
)
{
double
laplacianScore
=
ImageUtil
.
imageSharpnessDetector
(
src
);
System
.
out
.
println
(
laplacianScore
);
if
(
laplacianScore
<
500
)
{
Mat
unsharpMaskingMat
=
ImageUtil
.
unsharpMasking
(
src
);
// 保存调整后的图像
Imgcodecs
.
imwrite
(
dst
,
unsharpMaskingMat
);
}
}
public
static
DetectionVO
detection
(
String
src
)
throws
Exception
{
DetectionVO
res
=
new
DetectionVO
();
Mat
image
=
Imgcodecs
.
imread
(
src
);
// 检测图片的分辨率
res
.
setWidthResolution
(
image
.
width
());
res
.
setHeightResolution
(
image
.
height
());
// 检测图片的DPI
res
.
setDpi
(
getDpi
(
FileUtil
.
file
(
src
)));
// 检测图片清晰度
res
.
setClarity
(
ImageUtil
.
imageSharpnessDetector
(
image
));
// 检测图片的亮度
res
.
setBrightness
(
ImageUtil
.
brightness
(
image
));
// 检测图片倾斜角度
res
.
setAngle
(
Deskew
.
getDeskewAngle
(
image
));
// 检测图片的黑边
res
.
setBlack
(
blackDetection
(
image
));
return
res
;
}
/**
* 获取图片DPI
*
* @param file 图片文件
* @return [水平DPI,垂直DPI]
*/
private
static
Integer
getDpi
(
File
file
)
throws
Exception
{
int
res
;
ImageInfo
imageInfo
=
Imaging
.
getImageInfo
(
file
);
res
=
imageInfo
.
getPhysicalWidthDpi
();
if
(
res
==
-
1
)
{
Metadata
metadata
=
ImageMetadataReader
.
readMetadata
(
file
);
for
(
Directory
directory
:
metadata
.
getDirectories
())
{
for
(
Tag
tag
:
directory
.
getTags
())
{
if
(
"X Resolution"
.
equals
(
tag
.
getTagName
()))
{
res
=
Convert
.
toInt
(
tag
.
getDescription
());
}
}
}
}
return
res
;
}
/**
* 黑边像素阈值
*/
private
static
final
Integer
THRESHOLD
=
30
;
/**
* 黑边检测
*
* @param src 图片矩阵
* @return true表示可能存在黑边
*/
public
static
boolean
blackDetection
(
Mat
src
)
{
int
width
=
src
.
width
();
int
height
=
src
.
height
();
// 定义初始边界
int
top
=
0
;
int
left
=
0
;
int
right
=
width
-
1
;
int
bottom
=
height
-
1
;
// 上方黑边判断
for
(
int
row
=
0
;
row
<
height
;
row
++)
{
if
(
ImageUtil
.
sum
(
src
.
row
(
row
))
/
width
<
THRESHOLD
)
{
top
=
row
;
}
else
{
break
;
}
}
// 左边黑边判断
for
(
int
col
=
0
;
col
<
width
;
col
++)
{
if
(
ImageUtil
.
sum
(
src
.
col
(
col
))
/
height
<
THRESHOLD
)
{
left
=
col
;
}
else
{
break
;
}
}
// 右边黑边判断
for
(
int
col
=
width
-
1
;
col
>
0
;
col
--)
{
if
(
ImageUtil
.
sum
(
src
.
col
(
col
))
/
height
<
THRESHOLD
)
{
right
=
col
;
}
else
{
break
;
}
}
// 下方黑边判断
for
(
int
row
=
height
-
1
;
row
>
0
;
row
--)
{
if
(
ImageUtil
.
sum
(
src
.
row
(
row
))
/
width
<
THRESHOLD
)
{
bottom
=
row
;
}
else
{
break
;
}
}
// 若是边界没有被更新,认为不存在黑边
return
top
!=
0
||
left
!=
0
||
right
!=
width
-
1
||
bottom
!=
height
-
1
;
}
}
imgproc-server/src/main/java/com/zq/imgproc/utils/UploadUtils.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
utils
;
import
cn.hutool.core.io.FileUtil
;
import
com.zq.imgproc.exception.BusinessException
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.web.multipart.MultipartFile
;
import
java.io.File
;
import
java.io.IOException
;
import
java.text.SimpleDateFormat
;
import
java.util.Date
;
import
java.util.Objects
;
/**
* @author wilmiam
* @since 2021-07-09 18:05
*/
@Slf4j
public
class
UploadUtils
{
private
final
static
String
DATE_FORMAT
=
"/yyyyMM/dd/"
;
/**
* 保存临时文件
*
* @return 保存地址
*/
public
static
String
saveTempFile
(
MultipartFile
file
,
String
systemName
)
{
try
{
String
originalFilename
=
file
.
getOriginalFilename
();
//取文件扩展名
String
ext
=
Objects
.
requireNonNull
(
originalFilename
).
substring
(
originalFilename
.
lastIndexOf
(
"."
)
+
1
);
//生成新文件名
String
name
=
UuidUtils
.
uuidNoDash
()
+
"."
+
ext
;
String
yyyyMMdd
=
new
SimpleDateFormat
(
DATE_FORMAT
).
format
(
new
Date
());
File
dest
=
new
File
(
"/data/temp/"
+
systemName
+
yyyyMMdd
+
name
);
FileUtil
.
writeBytes
(
file
.
getBytes
(),
dest
);
return
"/data/temp/"
+
systemName
+
yyyyMMdd
+
name
;
}
catch
(
IOException
e
)
{
log
.
error
(
"文件保存失败:{}"
,
e
.
getMessage
());
throw
new
BusinessException
(
"文件保存失败"
);
}
}
}
imgproc-server/src/main/java/com/zq/imgproc/utils/UuidUtils.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
utils
;
import
java.util.UUID
;
/**
* Utility class that handles the uuid stuff.
*
* @author wilmiam
* @since 2021-07-09 18:05
*/
public
class
UuidUtils
{
private
static
final
String
URN_UUID_REGEX
=
"^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$"
;
private
static
final
String
NODASH_UUID_REGEX
=
"^[a-f0-9]{32}$"
;
/**
* Don't let anyone instantiate this class
*/
private
UuidUtils
()
{
}
/**
* 返回一个随机的带有分隔符"-"的36位UUID字符串
*
* @return
*/
public
static
String
uuid
()
{
return
UUID
.
randomUUID
().
toString
();
}
/**
* 返回一个随机的没有分隔符"-"的32位UUID字符串
*
* @return
*/
public
static
String
uuidNoDash
()
{
return
uuid
().
replaceAll
(
"-"
,
""
);
}
/**
* 判断一个字符串是否是一个UUID字符串
*
* @param str 要进行判断的字符串
* @return
*/
public
static
boolean
isUuid
(
String
str
)
{
return
ValidateUtil
.
isNotBlank
(
str
)
&&
str
.
matches
(
URN_UUID_REGEX
);
}
/**
* 判断一个字符串是否是一个没有分隔符的uuid字符串
*
* @param str 要进行判断的字符串
* @return
*/
public
static
boolean
isNoDashUuid
(
String
str
)
{
return
ValidateUtil
.
isNotBlank
(
str
)
&&
str
.
matches
(
NODASH_UUID_REGEX
);
}
}
imgproc-server/src/main/java/com/zq/imgproc/utils/ValidateUtil.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
utils
;
import
java.math.BigDecimal
;
import
java.math.BigInteger
;
import
java.util.Arrays
;
import
java.util.Collection
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
/**
* 验证工具类
*
* @author wilmiam
* @since 2021-07-09 18:05
*/
public
class
ValidateUtil
{
/**
* URL验证正则表达式
*/
private
static
final
String
URL_REGEX
=
"^(http|https)\\://([a-zA-Z0-9\\.\\-]+(\\:[a-zA-Z0-9\\.&%\\$\\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\\-]+\\.)*[a-zA-Z0-9\\-]+\\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{1,10}))(\\:[0-9]+)*(/($|[a-zA-Z0-9\\.\\,\\?\\'\\\\\\+&%\\$#\\=~_\\-]+))*$"
;
/**
* 安全SQL验证正则表达式
*/
private
static
final
String
SAFE_SQL_REGEX
=
"[-|;|,|\\/|\\(|\\)|\\[|\\]|\\}|\\{|%|@|\\*|!|\\']"
;
/**
* 身份证验证加权因子数组,将前17位加权因子保存在数组里
*/
private
static
final
int
[]
IDCARD_WI
=
new
int
[]{
7
,
9
,
10
,
5
,
8
,
4
,
2
,
1
,
6
,
3
,
7
,
9
,
10
,
5
,
8
,
4
,
2
};
/**
* 身份证验证校验码数组,这是除以11后,可能产生的11位余数、验证码,也保存
*/
private
static
final
int
[]
IDCARD_Y
=
new
int
[]{
1
,
0
,
10
,
9
,
8
,
7
,
6
,
5
,
4
,
3
,
2
};
/**
* 身份证验证,最后一位为X时,校验码所在数组的位置
*/
private
static
final
int
IDCARD_MOD_X
=
2
;
/**
* 身份证验证,当校验码为10时,最后一位必须是X
*/
private
static
final
String
IDCARD_LAST_X
=
"X"
;
/**
* 身份证校验正则表达式
*/
private
static
final
String
IDCARD_REGEX
=
"^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}$|^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9]|X)$"
;
/**
* 身份证号码长度
*/
private
static
final
int
IDCARD_LENG
=
18
;
/**
* 单个IP的正则表达式
*/
private
static
final
String
SINGLE_IP_REGEX
=
"([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}"
;
/**
* 以逗号分隔的多个IP的正则表达式
*/
private
static
final
String
IP_REGEX
=
SINGLE_IP_REGEX
+
"(,"
+
SINGLE_IP_REGEX
+
")*"
;
private
static
final
Class
<?>[]
NUMBER_TYPES
=
{
Byte
.
class
,
Short
.
class
,
Integer
.
class
,
Long
.
class
,
BigInteger
.
class
,
Float
.
class
,
Double
.
class
,
BigDecimal
.
class
};
/**
* 判断对象是否为空
*
* @param obj 要判断的对象
* @return 判断结果
*/
public
static
boolean
isBlank
(
final
Object
obj
)
{
if
(
obj
==
null
)
{
return
true
;
}
if
(
obj
instanceof
String
)
{
return
""
.
equals
(((
String
)
obj
).
trim
());
}
if
(
obj
instanceof
Collection
)
{
final
Collection
collection
=
(
Collection
)
obj
;
return
collection
.
isEmpty
();
}
return
false
;
}
/**
* 判断对象是否不为空
*
* @param obj
* @return
*/
public
static
boolean
isNotBlank
(
final
Object
obj
)
{
return
!
isBlank
(
obj
);
}
/**
* 检查参数是否有空
*
* @param objects 待验证对象,可以是多个
* @return 验证结果
*/
public
static
boolean
hasBlank
(
final
Object
...
objects
)
{
for
(
final
Object
obj
:
objects
)
{
if
(
isBlank
(
obj
))
{
return
true
;
}
}
return
false
;
}
/**
* 判断是否是Base64字符串
*
* @param str Base64字符串
* @return 判断结果
*/
public
static
boolean
isBase64
(
final
String
str
)
{
return
isMatch
(
str
,
"[A-Za-z0-9\\+\\/\\=]"
);
}
/**
* 判断是否是Email地址字符串
*
* @param strEmail URL地址字符串
* @return 判断结果
*/
public
static
boolean
isEmail
(
final
String
strEmail
)
{
return
isMatch
(
strEmail
,
"^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$"
);
}
/**
* 验证手机号码正确性
*
* @param strMobilePhone 手机号码
* @return
*/
public
static
boolean
isMobilePhoneNo
(
final
String
strMobilePhone
)
{
return
isMatch
(
strMobilePhone
,
"^0?(13[0-9]|14[5-9]|15[012356789]|166|17[0-8]|18[0-9]|19[8-9])[0-9]{8}$"
);
}
/**
* 是否为手机号码
*
* @param phone
* @return
*/
public
static
boolean
isInternationalPhoneNo
(
final
String
phone
)
{
return
isMatch
(
phone
,
"^[0-9]{4,14}$"
);
}
/**
* 判断是否为正整数字符串
*
* @param intStr 数字字符串
* @return 判断结果
*/
public
static
boolean
isInt
(
final
String
intStr
)
{
return
isMatch
(
intStr
,
"^[0-9]*$"
);
}
/**
* 判断是否为IP字符串
*
* @param ipStr ip字符串
* @return 判断结果
*/
public
static
boolean
isIp
(
final
String
ipStr
)
{
return
isMatch
(
ipStr
,
"^((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)$"
);
}
/**
* 判断字符串是否和正则表达式相匹配,大小写敏感
*
* @param str 字符串
* @param regEx 正则表达式
* @return 判断结果
*/
public
static
boolean
isMatch
(
final
String
str
,
final
String
regEx
)
{
return
isMatch
(
str
,
regEx
,
false
);
}
/**
* 判断字符串是否和正则表达式相匹配
*
* @param str 字符串
* @param regEx 正则表达式
* @param caseInsensetive 是否不区分大小写, true为不区分, false为区分
* @return 判断结果
*/
public
static
boolean
isMatch
(
final
String
str
,
final
String
regEx
,
final
boolean
caseInsensetive
)
{
if
(
ValidateUtil
.
isNotBlank
(
str
)
&&
ValidateUtil
.
isNotBlank
(
regEx
))
{
Pattern
pattern
;
if
(
caseInsensetive
)
{
pattern
=
Pattern
.
compile
(
regEx
,
Pattern
.
CASE_INSENSITIVE
);
}
else
{
pattern
=
Pattern
.
compile
(
regEx
);
}
final
Matcher
matcher
=
pattern
.
matcher
(
str
);
return
matcher
.
find
();
}
return
false
;
}
/**
* 判断是否是数字
*
* @param strNumber 数字字符串
* @return 判断结果
*/
public
static
boolean
isNumber
(
final
String
strNumber
)
{
return
isMatch
(
strNumber
,
"^\\d+$"
);
}
/**
* 判断是否是Sql危险字符
*
* @param sqlStr sql字符串
* @return 判断结果
*/
public
static
boolean
isSafeSqlString
(
final
String
sqlStr
)
{
return
isMatch
(
sqlStr
,
SAFE_SQL_REGEX
);
}
/**
* 判断是否是URL地址字符串
*
* @param strUrl URL地址字符串
* @return 判断结果
*/
public
static
boolean
isURL
(
final
String
strUrl
)
{
return
isMatch
(
strUrl
,
URL_REGEX
);
}
/**
* 检查对象类型是不是Number类型
*
* @param type
* @return
*/
public
static
boolean
isNumber
(
Class
<?>
type
)
{
// Class为final类,其equals不能重写,而Class的equals方法是直接继承自Object类
// return (this == obj)
// 所以在比较Class是否相等时,使用equals和使用==是一样的
return
Arrays
.
stream
(
NUMBER_TYPES
).
anyMatch
(
numberType
->
numberType
.
equals
(
type
));
}
/**
* 身份证15位编码规则:dddddd yymmdd xx p
* dddddd:6位地区编码
* yymmdd: 出生年(两位年)月日,如:910215
* xx: 顺序编码,系统产生,无法确定
* p: 性别,奇数为男,偶数为女
* <p>
* 身份证18位编码规则:dddddd yyyymmdd xxx y
* dddddd:6位地区编码
* yyyymmdd: 出生年(四位年)月日,如:19910215
* xxx:顺序编码,系统产生,无法确定,奇数为男,偶数为女
* y: 校验码,该位数值可通过前17位计算获得
* <p>
* 前17位号码加权因子为 Wi = [ 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 ]
* 验证位 Y = [ 1, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2 ]
* 如果验证码恰好是10,为了保证身份证是十八位,那么第十八位将用X来代替
* 校验位计算公式:Y_P = mod( ∑(Ai×Wi),11 )
* i为身份证号码1...17 位; Y_P为校验码Y所在校验码数组位置
*/
public
static
boolean
isIdCardNo
(
final
String
idCardNo
)
{
if
(!
isMatch
(
idCardNo
,
IDCARD_REGEX
))
{
return
false
;
}
if
(
idCardNo
.
length
()
==
IDCARD_LENG
)
{
// 用来保存前17位各自乖以加权因子后的总和
int
idCardWiSum
=
0
;
for
(
int
i
=
0
;
i
<
IDCARD_WI
.
length
;
i
++)
{
idCardWiSum
+=
Integer
.
parseInt
(
idCardNo
.
substring
(
i
,
i
+
1
))
*
IDCARD_WI
[
i
];
}
// 计算出校验码所在数组的位置
int
idCardMod
=
idCardWiSum
%
11
;
// 得到最后一位身份证号码
String
idCardLast
=
idCardNo
.
substring
(
17
);
if
(
idCardMod
==
IDCARD_MOD_X
)
{
return
IDCARD_LAST_X
.
equalsIgnoreCase
(
idCardLast
);
}
else
{
//用计算出的验证码与最后一位身份证号码匹配,如果一致,说明通过,否则是无效的身份证号码
return
String
.
valueOf
(
IDCARD_Y
[
idCardMod
]).
equals
(
idCardLast
);
}
}
return
true
;
}
/**
* 判断字符串是否是以逗号分隔的IP字符串。
* <p>
* 逗号必须是英文半角, 单个ip也认为符合要求, 空字符串返回false
* </p>
*
* @param ipStr
* @return
*/
public
static
boolean
isComaSplitIp
(
String
ipStr
)
{
boolean
result
=
false
;
if
(
ValidateUtil
.
isNotBlank
(
ipStr
))
{
result
=
ipStr
.
matches
(
IP_REGEX
);
}
return
result
;
}
}
imgproc-server/src/main/java/com/zq/imgproc/vo/BendResult.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
vo
;
import
lombok.AllArgsConstructor
;
import
lombok.Builder
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
java.util.List
;
/**
* <p>
* 弯曲检测结果
* </P>
*
* @author chenhao
* @since 2023/11/26
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public
class
BendResult
{
/**
* 标签名称
*/
String
label
;
/**
* 置信度
*/
Double
confidence
;
/**
* 弯曲位置
*/
List
<
Double
>
coord
;
}
imgproc-server/src/main/java/com/zq/imgproc/vo/CompressReq.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
vo
;
import
lombok.Data
;
import
java.util.List
;
/**
* <p>
* 压缩请求
* </p>
*
* @author chenhao
* @since 2023/3/9 17:35
*/
@Data
public
class
CompressReq
{
String
name
;
List
<
ImgVO
>
pathList
;
}
imgproc-server/src/main/java/com/zq/imgproc/vo/DetectionDTO.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
vo
;
import
com.alibaba.excel.annotation.ExcelProperty
;
import
lombok.Data
;
import
lombok.EqualsAndHashCode
;
/**
* <p>
*
* </p>
*
* @author chenhao
* @since 2023/11/24
*/
@Data
@EqualsAndHashCode
public
class
DetectionDTO
{
@ExcelProperty
(
"文件名称"
)
private
String
filename
;
@ExcelProperty
(
"分辨率"
)
private
String
resolution
;
@ExcelProperty
(
"DPI"
)
private
Integer
dpi
;
@ExcelProperty
(
"平均亮度值"
)
private
double
brightness
;
@ExcelProperty
(
"清晰度值"
)
private
double
clarity
;
@ExcelProperty
(
"倾斜角度"
)
private
double
angle
;
@ExcelProperty
(
"弯曲置信度"
)
private
double
bend
;
@ExcelProperty
(
"黑边情况"
)
private
String
black
;
}
imgproc-server/src/main/java/com/zq/imgproc/vo/DetectionResVo.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
vo
;
import
io.swagger.annotations.ApiModelProperty
;
import
lombok.AllArgsConstructor
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
/**
* <p>
* 图片返回结果封装类
* </p>
*
* @author chenhao
* @since 2022/10/27 11:25
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
public
class
DetectionResVo
{
@ApiModelProperty
(
"图片的水平分辨率"
)
private
Integer
widthResolution
;
@ApiModelProperty
(
"图片的垂直分辨率"
)
private
Integer
heightResolution
;
@ApiModelProperty
(
"图片的DPI"
)
private
Integer
dpi
;
@ApiModelProperty
(
"图片的亮度情况,1表示亮度正常,2表示过亮,3表示过暗"
)
private
Integer
brightness
;
@ApiModelProperty
(
"图片的清晰度,true表示清晰,false表示模糊"
)
private
Boolean
clarity
;
@ApiModelProperty
(
"图片的倾斜角度"
)
private
double
angle
;
@ApiModelProperty
(
"图片的黑边检测,true表示可能存在黑边,false表示不存在黑边"
)
private
Boolean
black
;
}
imgproc-server/src/main/java/com/zq/imgproc/vo/DetectionVO.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
vo
;
import
io.swagger.annotations.ApiModelProperty
;
import
lombok.AllArgsConstructor
;
import
lombok.Builder
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
/**
* <p>
* 检测结果
* </P>
*
* @author chenhao
* @since 2023/11/26
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public
class
DetectionVO
{
@ApiModelProperty
(
"图片的水平分辨率"
)
private
Integer
widthResolution
;
@ApiModelProperty
(
"图片的垂直分辨率"
)
private
Integer
heightResolution
;
@ApiModelProperty
(
"图片的DPI"
)
private
Integer
dpi
;
@ApiModelProperty
(
"平均亮度值"
)
private
double
brightness
;
@ApiModelProperty
(
"图片的清晰度指标,值越大越清晰"
)
private
double
clarity
;
@ApiModelProperty
(
"图片的倾斜角度"
)
private
double
angle
;
@ApiModelProperty
(
"图片弯曲检测置信度"
)
private
double
bend
;
@ApiModelProperty
(
"图片的黑边检测,true表示可能存在黑边,false表示不存在黑边"
)
private
Boolean
black
;
}
imgproc-server/src/main/java/com/zq/imgproc/vo/ImgVO.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
vo
;
import
lombok.AllArgsConstructor
;
import
lombok.Builder
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
/**
* <p>
*
* </p>
*
* @author chenhao
* @since 2023/3/7 16:01
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public
class
ImgVO
{
String
id
;
String
fileName
;
String
url
;
DetectionVO
detectionRes
;
}
imgproc-server/src/main/java/com/zq/imgproc/vo/OptimizationReq.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
vo
;
import
lombok.Data
;
/**
* <p>
*
* </p>
*
* @author yww
* @since 2023/4/20
*/
@Data
public
class
OptimizationReq
{
private
String
fileContent
;
private
String
filename
;
}
imgproc-server/src/main/java/com/zq/imgproc/vo/OptimizationVO.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
vo
;
import
io.swagger.annotations.ApiModelProperty
;
import
lombok.AllArgsConstructor
;
import
lombok.Builder
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
/**
* <p>
* 图片优化结果类
* </p>
*
* @author chenhao
* @since 2023/4/20
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public
class
OptimizationVO
{
@ApiModelProperty
(
"base64图片"
)
private
String
fileContent
;
@ApiModelProperty
(
"文件名称"
)
private
String
filename
;
@ApiModelProperty
(
"是否经过亮度调整"
)
private
boolean
isBrightness
;
@ApiModelProperty
(
"图片原始平均亮度值"
)
private
double
originalBrightnessVal
;
@ApiModelProperty
(
"图片修正后的平均亮度值"
)
private
double
brightnessVal
;
@ApiModelProperty
(
"是否经过清晰度调整"
)
private
boolean
isClarity
;
@ApiModelProperty
(
"图片原始清晰度值"
)
private
double
originalClarityVal
;
@ApiModelProperty
(
"图片清晰度调整过后的清晰度值"
)
private
double
clarityVal
;
@ApiModelProperty
(
"是否经过黑边处理"
)
private
boolean
isRemoveBlack
;
@ApiModelProperty
(
"图片纠偏的角度,纠偏出错返回-1"
)
private
double
deskewAngel
;
@ApiModelProperty
(
"图片是否经过弯曲矫正"
)
private
boolean
isCorrect
;
}
imgproc-server/src/main/java/com/zq/imgproc/vo/RotateReq.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
vo
;
import
lombok.AllArgsConstructor
;
import
lombok.Builder
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
java.util.List
;
/**
* <p>
* 图片旋转
* </p>
*
* @author chenhao
* @since 2023/3/10 17:32
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public
class
RotateReq
{
/**
* 旋转角度
*/
Integer
degree
;
List
<
ImgVO
>
pathList
;
}
imgproc-server/src/main/java/com/zq/imgproc/vo/SettingVO.java
deleted
100644 → 0
View file @
7f1d964c
package
com
.
zq
.
imgproc
.
vo
;
import
io.swagger.annotations.ApiModelProperty
;
import
lombok.AllArgsConstructor
;
import
lombok.Builder
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
/**
* <p>
* 亮度和清晰度指标设置
* </p>
*
* @author chenhao
* @since 2023/3/10 17:03
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public
class
SettingVO
{
@ApiModelProperty
(
"亮度值起始范围"
)
private
double
startValue
;
@ApiModelProperty
(
"亮度值结束范围"
)
private
double
endValue
;
@ApiModelProperty
(
"图片的清晰度指标,值越大越清晰"
)
private
double
clarity
;
}
imgproc-server/src/main/resources/application.yml
View file @
a0252026
server
:
server
:
port
:
8900
port
:
9999
# 配置数据源
spring
:
spring
:
application
:
application
:
name
:
IMGPROC-SERVER
name
:
IMGPROC-SERVER
jackson
:
jackson
:
date-format
:
yyyy-MM-dd HH:mm:ss
date-format
:
yyyy-MM-dd HH:mm:ss
time-zone
:
GMT+8
time-zone
:
GMT+8
mvc
:
freemarker
:
format
:
check-template-location
:
false
date-time
:
yyyy-MM-dd HH:mm:ss
redis
:
servlet
:
# 数据库索引
multipart
:
database
:
0
max-file-size
:
100MB
host
:
${redis.url}
max-request-size
:
150MB
port
:
${redis.port}
password
:
# 连接超时时间
timeout
:
5000
datasource
:
druid
:
db-type
:
com.alibaba.druid.pool.DruidDataSource
driver-class-name
:
${db.cloud.driver-class-name}
username
:
${db.cloud.username}
password
:
${db.cloud.password}
url
:
${db.cloud.url.cloud}
# 初始连接数
initial-size
:
5
# 最小连接数
min-idle
:
10
# 最大连接数
max-active
:
20
# 超时时间(以秒数为单位)
remove-abandoned-timeout
:
180
# 获取连接超时时间
max-wait
:
5000
# 连接有效性检测时间
time-between-eviction-runs-millis
:
60000
# 连接在池中最小生存的时间
min-evictable-idle-time-millis
:
300000
# 连接在池中最大生存的时间
max-evictable-idle-time-millis
:
900000
# 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除
test-while-idle
:
true
# 指明是否在从池中取出连接前进行检验,如果检验失败, 则从池中去除连接并尝试取出另一个
test-on-borrow
:
true
# 是否在归还到池中前进行检验
test-on-return
:
false
# 检测连接是否有效
validation-query
:
select 1
# 配置监控统计
webStatFilter
:
enabled
:
true
stat-view-servlet
:
enabled
:
true
url-pattern
:
/admin/druid/*
reset-enable
:
false
allow
:
"
"
filter
:
stat
:
enabled
:
true
# 记录慢SQL
log-slow-sql
:
true
slow-sql-millis
:
1000
merge-sql
:
true
wall
:
config
:
multi-statement-allow
:
true
# mybatis plus 配置
mybatis-plus
:
global-config
:
db-config
:
where-strategy
:
not_empty
update-strategy
:
not_empty
# IP 本地解析
# IP 本地解析
ip
:
ip
:
...
@@ -22,13 +82,3 @@ imgconfig:
...
@@ -22,13 +82,3 @@ imgconfig:
opencv
:
/opt/services/tianjin-backend/lib/opencv_java460.so
opencv
:
/opt/services/tianjin-backend/lib/opencv_java460.so
deskew
:
/opt/services/tianjin-backend/lib/Deskew/Bin/deskew
deskew
:
/opt/services/tianjin-backend/lib/Deskew/Bin/deskew
deskewpy
:
/opt/services/tianjin-backend/lib/correct.py
deskewpy
:
/opt/services/tianjin-backend/lib/correct.py
#imgconfig:
# opencv: /opt/tianjin/lib/opencv_java460.so
# deskew: /opt/tianjin/lib/Deskew/Bin/deskew
# deskewpy: /opt/tianjin/lib/correct.py
#imgconfig:
# opencv: D:/project/imgproc/lib/opencv_java460.dll
# deskew: C:/Users/11419/Desktop/Deskew/Bin/deskew.exe
# deskewpy: D:/project/imgproc/lib/correct.py
imgproc-server/src/main/resources/bootstrap.yml
View file @
a0252026
...
@@ -8,13 +8,27 @@ spring:
...
@@ -8,13 +8,27 @@ spring:
discovery
:
discovery
:
enabled
:
true
enabled
:
true
service-id
:
CONFIG-SERVER
service-id
:
CONFIG-SERVER
username
:
admin
password
:
123456
eureka
:
eureka
:
instance
:
instance
:
prefer-ip-address
:
true
prefer-ip-address
:
true
lease-renewal-interval-in-seconds
:
2
#
每间隔1s,向服务端发送一次心跳,证明自己依然"存活"
lease-renewal-interval-in-seconds
:
2
#
向服务端发送心跳间隔
lease-expiration-duration-in-seconds
:
6
#告诉服务端
,如果我2s之内没有给你发心跳,就代表我"死"了,将我踢出掉。
lease-expiration-duration-in-seconds
:
6
#告诉服务端
多少秒没收到心跳将我踢出掉
instance-id
:
${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
instance-id
:
${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
client
:
client
:
service-url
:
registry-fetch-interval-seconds
:
2
#从服务端注册表中获取注册信息的时间间隔
defaultZone
:
@
eureka.server.url@
serviceUrl
:
defaultZone
:
@
register.url@
feign
:
client
:
config
:
default
:
connect-timeout
:
2000
#连接超时时间
read-timeout
:
10000
#读超时时间
ribbon
:
# ribbon服务列表刷新间隔(单位ms)
ServerListRefreshInterval
:
2000
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment