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
003b7634
Commit
003b7634
authored
Nov 28, 2023
by
陈皓
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
init
parent
cae76626
Hide whitespace changes
Inline
Side-by-side
Showing
47 changed files
with
4865 additions
and
3 deletions
+4865
-3
file-server/src/main/java/com/zq/file/controller/AccessController.java
+15
-0
gateway-server/src/main/resources/application.yml
+4
-0
imgproc-server/pom.xml
+80
-0
imgproc-server/src/main/java/com/zq/imgproc/config/FeignConfig.java
+83
-0
imgproc-server/src/main/java/com/zq/imgproc/config/InitConfig.java
+26
-0
imgproc-server/src/main/java/com/zq/imgproc/config/JwtAccessDeniedHandler.java
+38
-0
imgproc-server/src/main/java/com/zq/imgproc/config/JwtAuthenticationEntryPoint.java
+40
-0
imgproc-server/src/main/java/com/zq/imgproc/config/SpringSecurityConfig.java
+189
-0
imgproc-server/src/main/java/com/zq/imgproc/config/SwaggerConfig.java
+65
-0
imgproc-server/src/main/java/com/zq/imgproc/config/TokenConfigurer.java
+42
-0
imgproc-server/src/main/java/com/zq/imgproc/config/TokenFilter.java
+91
-0
imgproc-server/src/main/java/com/zq/imgproc/config/TokenProvider.java
+137
-0
imgproc-server/src/main/java/com/zq/imgproc/constant/Threshold.java
+45
-0
imgproc-server/src/main/java/com/zq/imgproc/controller/ApiController.java
+166
-0
imgproc-server/src/main/java/com/zq/imgproc/controller/ImageDetectionController.java
+51
-0
imgproc-server/src/main/java/com/zq/imgproc/controller/ImgProcController.java
+167
-0
imgproc-server/src/main/java/com/zq/imgproc/controller/ImgSettingController.java
+71
-0
imgproc-server/src/main/java/com/zq/imgproc/dao/ImageDetectionDao.java
+29
-0
imgproc-server/src/main/java/com/zq/imgproc/dao/ImgSettingDao.java
+17
-0
imgproc-server/src/main/java/com/zq/imgproc/entity/ImageDetection.java
+102
-0
imgproc-server/src/main/java/com/zq/imgproc/entity/ImgSetting.java
+119
-0
imgproc-server/src/main/java/com/zq/imgproc/server/ImageDetectionService.java
+169
-0
imgproc-server/src/main/java/com/zq/imgproc/server/ImgProcService.java
+658
-0
imgproc-server/src/main/java/com/zq/imgproc/utils/BendUtil.java
+83
-0
imgproc-server/src/main/java/com/zq/imgproc/utils/DecompressUtil.java
+155
-0
imgproc-server/src/main/java/com/zq/imgproc/utils/DemoUtil.java
+64
-0
imgproc-server/src/main/java/com/zq/imgproc/utils/Deskew.java
+163
-0
imgproc-server/src/main/java/com/zq/imgproc/utils/ImageCorrectionUtil.java
+168
-0
imgproc-server/src/main/java/com/zq/imgproc/utils/ImageUtil.java
+588
-0
imgproc-server/src/main/java/com/zq/imgproc/utils/RemoveBlackUtil.java
+210
-0
imgproc-server/src/main/java/com/zq/imgproc/utils/RemoveBlackUtil2.java
+142
-0
imgproc-server/src/main/java/com/zq/imgproc/utils/Test2.java
+170
-0
imgproc-server/src/main/java/com/zq/imgproc/utils/TestUtil.java
+166
-0
imgproc-server/src/main/java/com/zq/imgproc/utils/UploadUtils.java
+46
-0
imgproc-server/src/main/java/com/zq/imgproc/vo/BendResult.java
+39
-0
imgproc-server/src/main/java/com/zq/imgproc/vo/CompressReq.java
+22
-0
imgproc-server/src/main/java/com/zq/imgproc/vo/DetectionDTO.java
+43
-0
imgproc-server/src/main/java/com/zq/imgproc/vo/DetectionPageReq.java
+87
-0
imgproc-server/src/main/java/com/zq/imgproc/vo/DetectionResVo.java
+42
-0
imgproc-server/src/main/java/com/zq/imgproc/vo/DetectionVO.java
+47
-0
imgproc-server/src/main/java/com/zq/imgproc/vo/ImgVO.java
+32
-0
imgproc-server/src/main/java/com/zq/imgproc/vo/OptimizationReq.java
+20
-0
imgproc-server/src/main/java/com/zq/imgproc/vo/OptimizationVO.java
+56
-0
imgproc-server/src/main/java/com/zq/imgproc/vo/RotateReq.java
+31
-0
imgproc-server/src/main/java/com/zq/imgproc/vo/SettingVO.java
+32
-0
imgproc-server/src/main/resources/application.yml
+13
-3
imgproc-server/src/main/resources/mapper/ImageDetectionMapper.xml
+42
-0
No files found.
file-server/src/main/java/com/zq/file/controller/AccessController.java
View file @
003b7634
...
@@ -56,4 +56,19 @@ public class AccessController {
...
@@ -56,4 +56,19 @@ public class AccessController {
.
body
(
new
FileSystemResource
(
file
));
.
body
(
new
FileSystemResource
(
file
));
}
}
@GetMapping
(
value
=
"/data/**"
)
public
ResponseEntity
<
Resource
>
downloadData
(
HttpServletRequest
request
)
{
File
file
=
new
File
(
request
.
getRequestURI
());
AssertUtils
.
isTrue
(
file
.
exists
(),
"访问文件不存在"
);
String
contentDisposition
=
ContentDisposition
.
builder
(
"attachment"
)
.
filename
(
request
.
getRequestURI
())
.
build
().
toString
();
return
ResponseEntity
.
ok
()
.
header
(
HttpHeaders
.
CONTENT_DISPOSITION
,
contentDisposition
)
.
contentType
(
MediaType
.
APPLICATION_OCTET_STREAM
)
.
body
(
new
FileSystemResource
(
file
));
}
}
}
gateway-server/src/main/resources/application.yml
View file @
003b7634
...
@@ -33,6 +33,10 @@ spring:
...
@@ -33,6 +33,10 @@ spring:
uri
:
lb://FILE-SERVER
uri
:
lb://FILE-SERVER
predicates
:
predicates
:
-
Path=/file/**
-
Path=/file/**
-
id
:
data
uri
:
lb://FILE-SERVER
predicates
:
-
Path=/data/**
-
id
:
user
-
id
:
user
uri
:
lb://USER-SERVER
uri
:
lb://USER-SERVER
predicates
:
predicates
:
...
...
imgproc-server/pom.xml
View file @
003b7634
...
@@ -81,6 +81,86 @@
...
@@ -81,6 +81,86 @@
<artifactId>
mysql-connector-java
</artifactId>
<artifactId>
mysql-connector-java
</artifactId>
<scope>
runtime
</scope>
<scope>
runtime
</scope>
</dependency>
</dependency>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-bootstrap
</artifactId>
</dependency>
<!-- OpenCv -->
<!-- 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>
<groupId>
org.tukaani
</groupId>
<artifactId>
xz
</artifactId>
<version>
1.9
</version>
</dependency>
<dependency>
<groupId>
cn.hutool
</groupId>
<artifactId>
hutool-all
</artifactId>
<version>
5.8.19
</version>
</dependency>
<dependency>
<groupId>
com.alibaba
</groupId>
<artifactId>
easyexcel
</artifactId>
<version>
3.3.2
</version>
</dependency>
<dependency>
<groupId>
com.drewnoakes
</groupId>
<artifactId>
metadata-extractor
</artifactId>
<version>
2.18.0
</version>
</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>
</dependencies>
</dependencies>
<build>
<build>
...
...
imgproc-server/src/main/java/com/zq/imgproc/config/FeignConfig.java
0 → 100644
View file @
003b7634
package
com
.
zq
.
imgproc
.
config
;
import
com.zq.common.constant.FeignHeader
;
import
feign.RequestInterceptor
;
import
feign.RequestTemplate
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.web.context.request.RequestAttributes
;
import
org.springframework.web.context.request.RequestContextHolder
;
import
org.springframework.web.context.request.ServletRequestAttributes
;
import
javax.servlet.http.HttpServletRequest
;
import
java.util.Arrays
;
import
java.util.Enumeration
;
import
java.util.List
;
/**
* @author wilmiam
* @since 2021-07-09 10:34
*/
@Configuration
public
class
FeignConfig
{
/**
* 转发请求头
*/
private
static
final
List
<
String
>
FORWARD_HEADERS
=
Arrays
.
asList
(
"AUTHORIZATION"
,
"X-FORWARDED-FOR"
,
"X-FORWARDED-PROTO"
,
"X-FORWARDED-PORT"
,
"X-FORWARDED-HOST"
,
"FORWARDED"
,
"PROXY-CLIENT-IP"
,
"WL-PROXY-CLIENT-IP"
,
"HTTP_X_FORWARDED_FOR"
,
"HTTP_X_FORWARDED"
,
"HTTP_X_CLUSTER_CLIENT_IP"
,
"HTTP_CLIENT_IP"
,
"HTTP_FORWARDED_FOR"
,
"HTTP_FORWARDED"
,
"HTTP_VIA"
,
"REMOTE_ADDR"
,
"X-REAL-IP"
,
"HOST"
);
/**
* 解决fein远程调用丢失请求头
*
* @return
*/
@Bean
public
RequestInterceptor
requestInterceptor
()
{
return
new
RequestInterceptor
()
{
@Override
public
void
apply
(
RequestTemplate
template
)
{
RequestAttributes
requestAttributes
=
RequestContextHolder
.
getRequestAttributes
();
if
(
requestAttributes
==
null
)
{
return
;
}
HttpServletRequest
request
=
((
ServletRequestAttributes
)
requestAttributes
).
getRequest
();
template
.
header
(
FeignHeader
.
API_TOKEN
,
request
.
getParameter
(
"token"
));
Enumeration
<
String
>
headerNames
=
request
.
getHeaderNames
();
if
(
headerNames
!=
null
)
{
while
(
headerNames
.
hasMoreElements
())
{
String
name
=
headerNames
.
nextElement
();
// 不要设置content-length
if
(
"content-length"
.
equals
(
name
))
{
continue
;
}
if
(
FORWARD_HEADERS
.
contains
(
name
.
toUpperCase
()))
{
String
values
=
request
.
getHeader
(
name
);
template
.
header
(
name
,
values
);
}
}
}
}
};
}
}
imgproc-server/src/main/java/com/zq/imgproc/config/InitConfig.java
0 → 100644
View file @
003b7634
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 chenhao
* @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/config/JwtAccessDeniedHandler.java
0 → 100644
View file @
003b7634
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
zq
.
imgproc
.
config
;
import
org.springframework.security.access.AccessDeniedException
;
import
org.springframework.security.web.access.AccessDeniedHandler
;
import
org.springframework.stereotype.Component
;
import
javax.servlet.http.HttpServletRequest
;
import
javax.servlet.http.HttpServletResponse
;
import
java.io.IOException
;
/**
* @author Zheng Jie
*/
@Component
public
class
JwtAccessDeniedHandler
implements
AccessDeniedHandler
{
@Override
public
void
handle
(
HttpServletRequest
request
,
HttpServletResponse
response
,
AccessDeniedException
accessDeniedException
)
throws
IOException
{
//当用户在没有授权的情况下访问受保护的REST资源时,将调用此方法发送403 Forbidden响应
response
.
sendError
(
HttpServletResponse
.
SC_FORBIDDEN
,
accessDeniedException
.
getMessage
());
}
}
imgproc-server/src/main/java/com/zq/imgproc/config/JwtAuthenticationEntryPoint.java
0 → 100644
View file @
003b7634
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
zq
.
imgproc
.
config
;
import
org.springframework.security.core.AuthenticationException
;
import
org.springframework.security.web.AuthenticationEntryPoint
;
import
org.springframework.stereotype.Component
;
import
javax.servlet.http.HttpServletRequest
;
import
javax.servlet.http.HttpServletResponse
;
import
java.io.IOException
;
/**
* @author Zheng Jie
*/
@Component
public
class
JwtAuthenticationEntryPoint
implements
AuthenticationEntryPoint
{
@Override
public
void
commence
(
HttpServletRequest
request
,
HttpServletResponse
response
,
AuthenticationException
authException
)
throws
IOException
{
// 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应
response
.
sendError
(
HttpServletResponse
.
SC_UNAUTHORIZED
,
authException
==
null
?
"Unauthorized"
:
authException
.
getMessage
());
}
}
imgproc-server/src/main/java/com/zq/imgproc/config/SpringSecurityConfig.java
0 → 100644
View file @
003b7634
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
zq
.
imgproc
.
config
;
import
com.zq.common.annotation.AnonymousAccess
;
import
com.zq.common.config.redis.RedisUtils
;
import
com.zq.common.config.security.SecurityProperties
;
import
com.zq.common.utils.RequestMethodEnum
;
import
lombok.RequiredArgsConstructor
;
import
org.springframework.context.ApplicationContext
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.http.HttpMethod
;
import
org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity
;
import
org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
;
import
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
;
import
org.springframework.security.config.core.GrantedAuthorityDefaults
;
import
org.springframework.security.config.http.SessionCreationPolicy
;
import
org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
;
import
org.springframework.security.crypto.password.PasswordEncoder
;
import
org.springframework.web.bind.annotation.RequestMethod
;
import
org.springframework.web.method.HandlerMethod
;
import
org.springframework.web.servlet.mvc.method.RequestMappingInfo
;
import
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
;
import
java.util.*
;
/**
* @author Zheng Jie
*/
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity
(
prePostEnabled
=
true
,
securedEnabled
=
true
)
public
class
SpringSecurityConfig
extends
WebSecurityConfigurerAdapter
{
private
final
TokenProvider
tokenProvider
;
private
final
JwtAuthenticationEntryPoint
authenticationErrorHandler
;
private
final
JwtAccessDeniedHandler
jwtAccessDeniedHandler
;
private
final
ApplicationContext
applicationContext
;
private
final
SecurityProperties
properties
;
private
final
RedisUtils
redisUtils
;
@Bean
public
GrantedAuthorityDefaults
grantedAuthorityDefaults
()
{
// 去除 ROLE_ 前缀
return
new
GrantedAuthorityDefaults
(
""
);
}
@Bean
public
PasswordEncoder
passwordEncoder
()
{
// 密码加密方式
return
new
BCryptPasswordEncoder
();
}
@Override
protected
void
configure
(
HttpSecurity
httpSecurity
)
throws
Exception
{
// 搜寻匿名标记 url: @AnonymousAccess
RequestMappingHandlerMapping
requestMappingHandlerMapping
=
(
RequestMappingHandlerMapping
)
applicationContext
.
getBean
(
"requestMappingHandlerMapping"
);
Map
<
RequestMappingInfo
,
HandlerMethod
>
handlerMethodMap
=
requestMappingHandlerMapping
.
getHandlerMethods
();
// 获取匿名标记
Map
<
String
,
Set
<
String
>>
anonymousUrls
=
getAnonymousUrl
(
handlerMethodMap
);
Set
<
String
>
allType
=
anonymousUrls
.
get
(
RequestMethodEnum
.
ALL
.
getType
());
//不使用注解的时候在这添加url放行
allType
.
add
(
"/archive/api/**"
);
httpSecurity
// 禁用 CSRF
.
csrf
().
disable
()
// .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
// 授权异常
.
exceptionHandling
()
.
authenticationEntryPoint
(
authenticationErrorHandler
)
.
accessDeniedHandler
(
jwtAccessDeniedHandler
)
// 防止iframe 造成跨域
.
and
()
.
headers
()
.
frameOptions
()
.
disable
()
// 不创建会话
.
and
()
.
sessionManagement
()
.
sessionCreationPolicy
(
SessionCreationPolicy
.
STATELESS
)
.
and
()
.
authorizeRequests
()
// 静态资源等等
.
antMatchers
(
HttpMethod
.
GET
,
"/*.html"
,
"/**/*.html"
,
"/**/*.css"
,
"/**/*.js"
,
"/webSocket/**"
).
permitAll
()
// swagger 文档
.
antMatchers
(
"/swagger-ui.html"
).
permitAll
()
.
antMatchers
(
"/swagger-resources/**"
).
permitAll
()
.
antMatchers
(
"/webjars/**"
).
permitAll
()
.
antMatchers
(
"/*/api-docs"
).
permitAll
()
// 文件
.
antMatchers
(
"/avatar/**"
).
permitAll
()
.
antMatchers
(
"/file/**"
).
permitAll
()
// 阿里巴巴 druid
.
antMatchers
(
"/druid/**"
).
permitAll
()
// 放行OPTIONS请求
.
antMatchers
(
HttpMethod
.
OPTIONS
,
"/**"
).
permitAll
()
// 自定义匿名访问所有url放行:允许匿名和带Token访问,细腻化到每个 Request 类型
// GET
.
antMatchers
(
HttpMethod
.
GET
,
anonymousUrls
.
get
(
RequestMethodEnum
.
GET
.
getType
()).
toArray
(
new
String
[
0
])).
permitAll
()
// POST
.
antMatchers
(
HttpMethod
.
POST
,
anonymousUrls
.
get
(
RequestMethodEnum
.
POST
.
getType
()).
toArray
(
new
String
[
0
])).
permitAll
()
// PUT
.
antMatchers
(
HttpMethod
.
PUT
,
anonymousUrls
.
get
(
RequestMethodEnum
.
PUT
.
getType
()).
toArray
(
new
String
[
0
])).
permitAll
()
// PATCH
.
antMatchers
(
HttpMethod
.
PATCH
,
anonymousUrls
.
get
(
RequestMethodEnum
.
PATCH
.
getType
()).
toArray
(
new
String
[
0
])).
permitAll
()
// DELETE
.
antMatchers
(
HttpMethod
.
DELETE
,
anonymousUrls
.
get
(
RequestMethodEnum
.
DELETE
.
getType
()).
toArray
(
new
String
[
0
])).
permitAll
()
// 所有类型的接口都放行
.
antMatchers
(
allType
.
toArray
(
new
String
[
0
])).
permitAll
()
// 所有请求都需要认证
.
anyRequest
().
authenticated
()
.
and
().
apply
(
securityConfigurerAdapter
());
}
private
TokenConfigurer
securityConfigurerAdapter
()
{
return
new
TokenConfigurer
(
tokenProvider
,
redisUtils
,
properties
);
}
private
Map
<
String
,
Set
<
String
>>
getAnonymousUrl
(
Map
<
RequestMappingInfo
,
HandlerMethod
>
handlerMethodMap
)
{
Map
<
String
,
Set
<
String
>>
anonymousUrls
=
new
HashMap
<>(
6
);
Set
<
String
>
get
=
new
HashSet
<>();
Set
<
String
>
post
=
new
HashSet
<>();
Set
<
String
>
put
=
new
HashSet
<>();
Set
<
String
>
patch
=
new
HashSet
<>();
Set
<
String
>
delete
=
new
HashSet
<>();
Set
<
String
>
all
=
new
HashSet
<>();
for
(
Map
.
Entry
<
RequestMappingInfo
,
HandlerMethod
>
infoEntry
:
handlerMethodMap
.
entrySet
())
{
HandlerMethod
handlerMethod
=
infoEntry
.
getValue
();
AnonymousAccess
anonymousAccess
=
handlerMethod
.
getMethodAnnotation
(
AnonymousAccess
.
class
);
if
(
null
!=
anonymousAccess
)
{
List
<
RequestMethod
>
requestMethods
=
new
ArrayList
<>(
infoEntry
.
getKey
().
getMethodsCondition
().
getMethods
());
RequestMethodEnum
request
=
RequestMethodEnum
.
find
(
requestMethods
.
size
()
==
0
?
RequestMethodEnum
.
ALL
.
getType
()
:
requestMethods
.
get
(
0
).
name
());
switch
(
Objects
.
requireNonNull
(
request
))
{
case
GET:
get
.
addAll
(
infoEntry
.
getKey
().
getPatternsCondition
().
getPatterns
());
break
;
case
POST:
post
.
addAll
(
infoEntry
.
getKey
().
getPatternsCondition
().
getPatterns
());
break
;
case
PUT:
put
.
addAll
(
infoEntry
.
getKey
().
getPatternsCondition
().
getPatterns
());
break
;
case
PATCH:
patch
.
addAll
(
infoEntry
.
getKey
().
getPatternsCondition
().
getPatterns
());
break
;
case
DELETE:
delete
.
addAll
(
infoEntry
.
getKey
().
getPatternsCondition
().
getPatterns
());
break
;
default
:
all
.
addAll
(
infoEntry
.
getKey
().
getPatternsCondition
().
getPatterns
());
break
;
}
}
}
anonymousUrls
.
put
(
RequestMethodEnum
.
GET
.
getType
(),
get
);
anonymousUrls
.
put
(
RequestMethodEnum
.
POST
.
getType
(),
post
);
anonymousUrls
.
put
(
RequestMethodEnum
.
PUT
.
getType
(),
put
);
anonymousUrls
.
put
(
RequestMethodEnum
.
PATCH
.
getType
(),
patch
);
anonymousUrls
.
put
(
RequestMethodEnum
.
DELETE
.
getType
(),
delete
);
anonymousUrls
.
put
(
RequestMethodEnum
.
ALL
.
getType
(),
all
);
return
anonymousUrls
;
}
}
imgproc-server/src/main/java/com/zq/imgproc/config/SwaggerConfig.java
0 → 100644
View file @
003b7634
package
com
.
zq
.
imgproc
.
config
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.web.servlet.config.annotation.EnableWebMvc
;
import
org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
;
import
org.springframework.web.servlet.config.annotation.WebMvcConfigurer
;
import
springfox.documentation.builders.ApiInfoBuilder
;
import
springfox.documentation.builders.PathSelectors
;
import
springfox.documentation.builders.RequestHandlerSelectors
;
import
springfox.documentation.service.ApiInfo
;
import
springfox.documentation.spi.DocumentationType
;
import
springfox.documentation.spring.web.plugins.Docket
;
/**
* <p>
* Swagger配置类
* </p>
*
* @ClassName SwaggerConfig
* @Author chenhao
* @Date 2022/10/14 18:04
*/
@Configuration
@EnableWebMvc
public
class
SwaggerConfig
implements
WebMvcConfigurer
{
@Value
(
"${spring.cloud.config.profile:product}"
)
private
String
profile
;
@Override
public
void
addResourceHandlers
(
ResourceHandlerRegistry
registry
)
{
registry
.
addResourceHandler
(
"/**"
).
addResourceLocations
(
"classpath:/static/"
);
registry
.
addResourceHandler
(
"swagger-ui.html"
).
addResourceLocations
(
"classpath:/META-INF/resources/"
);
registry
.
addResourceHandler
(
"/webjars/**"
).
addResourceLocations
(
"classpath:/META-INF/resources/webjars/"
);
WebMvcConfigurer
.
super
.
addResourceHandlers
(
registry
);
}
@Bean
public
Docket
api
()
{
return
new
Docket
(
DocumentationType
.
SWAGGER_2
)
.
groupName
(
"项目"
)
// 生产环境关闭
.
enable
(!
"product"
.
equals
(
profile
))
.
select
()
.
apis
(
RequestHandlerSelectors
.
basePackage
(
"com.zq.archive.controller"
))
.
paths
(
PathSelectors
.
any
())
.
build
()
.
apiInfo
(
apiInfo
());
}
private
ApiInfo
apiInfo
()
{
return
new
ApiInfoBuilder
()
.
title
(
"后台接口文档"
)
.
description
(
"查看接口文档"
)
.
build
();
}
}
imgproc-server/src/main/java/com/zq/imgproc/config/TokenConfigurer.java
0 → 100644
View file @
003b7634
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
zq
.
imgproc
.
config
;
import
com.zq.common.config.redis.RedisUtils
;
import
com.zq.common.config.security.SecurityProperties
;
import
lombok.RequiredArgsConstructor
;
import
org.springframework.security.config.annotation.SecurityConfigurerAdapter
;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity
;
import
org.springframework.security.web.DefaultSecurityFilterChain
;
import
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
;
/**
* @author /
*/
@RequiredArgsConstructor
public
class
TokenConfigurer
extends
SecurityConfigurerAdapter
<
DefaultSecurityFilterChain
,
HttpSecurity
>
{
private
final
TokenProvider
tokenProvider
;
private
final
RedisUtils
redisUtils
;
private
final
SecurityProperties
properties
;
@Override
public
void
configure
(
HttpSecurity
http
)
{
TokenFilter
customFilter
=
new
TokenFilter
(
tokenProvider
,
properties
,
redisUtils
);
http
.
addFilterBefore
(
customFilter
,
UsernamePasswordAuthenticationFilter
.
class
);
}
}
imgproc-server/src/main/java/com/zq/imgproc/config/TokenFilter.java
0 → 100644
View file @
003b7634
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
zq
.
imgproc
.
config
;
import
cn.hutool.core.util.StrUtil
;
import
com.zq.common.config.redis.BaseCacheKeys
;
import
com.zq.common.config.redis.RedisUtils
;
import
com.zq.common.config.security.SecurityProperties
;
import
com.zq.common.context.ContextUtils
;
import
com.zq.common.vo.OnlineUserDto
;
import
org.apache.commons.lang3.StringUtils
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.security.core.Authentication
;
import
org.springframework.security.core.context.SecurityContextHolder
;
import
org.springframework.web.filter.GenericFilterBean
;
import
javax.servlet.FilterChain
;
import
javax.servlet.ServletException
;
import
javax.servlet.ServletRequest
;
import
javax.servlet.ServletResponse
;
import
javax.servlet.http.HttpServletRequest
;
import
java.io.IOException
;
import
java.util.Objects
;
/**
* @author /
*/
public
class
TokenFilter
extends
GenericFilterBean
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
TokenFilter
.
class
);
private
final
TokenProvider
tokenProvider
;
private
final
RedisUtils
redisUtils
;
private
SecurityProperties
properties
;
/**
* @param tokenProvider Token
* @param adminFeignClient admin
*/
public
TokenFilter
(
TokenProvider
tokenProvider
,
SecurityProperties
properties
,
RedisUtils
redisUtils
)
{
this
.
tokenProvider
=
tokenProvider
;
this
.
properties
=
properties
;
this
.
redisUtils
=
redisUtils
;
}
@Override
public
void
doFilter
(
ServletRequest
servletRequest
,
ServletResponse
servletResponse
,
FilterChain
filterChain
)
throws
IOException
,
ServletException
{
HttpServletRequest
httpServletRequest
=
(
HttpServletRequest
)
servletRequest
;
String
token
=
tokenProvider
.
getToken
(
httpServletRequest
);
// 对于 Token 为空的不需要去查 Redis
if
(
StrUtil
.
isNotBlank
(
token
))
{
OnlineUserDto
onlineUserDto
=
null
;
boolean
cleanUserCache
=
false
;
try
{
onlineUserDto
=
redisUtils
.
getObj
(
properties
.
getOnlineKey
()
+
token
,
OnlineUserDto
.
class
);
}
catch
(
Exception
e
)
{
log
.
error
(
e
.
getMessage
());
cleanUserCache
=
true
;
}
finally
{
if
(
cleanUserCache
||
Objects
.
isNull
(
onlineUserDto
))
{
String
username
=
String
.
valueOf
(
tokenProvider
.
getClaims
(
token
).
get
(
TokenProvider
.
AUTHORITIES_KEY
));
redisUtils
.
hdel
(
BaseCacheKeys
.
USER_DATA_MAP_KEY
,
username
);
}
}
if
(
onlineUserDto
!=
null
&&
StringUtils
.
isNotBlank
(
token
))
{
Authentication
authentication
=
tokenProvider
.
getAuthentication
(
token
);
SecurityContextHolder
.
getContext
().
setAuthentication
(
authentication
);
// 设置当前用户
ContextUtils
.
setAdminContext
(
onlineUserDto
);
}
}
filterChain
.
doFilter
(
servletRequest
,
servletResponse
);
}
}
imgproc-server/src/main/java/com/zq/imgproc/config/TokenProvider.java
0 → 100644
View file @
003b7634
package
com
.
zq
.
imgproc
.
config
;
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
cn.hutool.core.date.DateField
;
import
cn.hutool.core.date.DateUtil
;
import
cn.hutool.core.util.IdUtil
;
import
cn.hutool.core.util.ObjectUtil
;
import
com.zq.common.config.redis.RedisUtils
;
import
com.zq.common.config.security.SecurityProperties
;
import
io.jsonwebtoken.Claims
;
import
io.jsonwebtoken.Jwts
;
import
io.jsonwebtoken.SignatureAlgorithm
;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.beans.factory.InitializingBean
;
import
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
;
import
org.springframework.security.core.Authentication
;
import
org.springframework.security.core.GrantedAuthority
;
import
org.springframework.security.core.authority.SimpleGrantedAuthority
;
import
org.springframework.security.core.userdetails.User
;
import
org.springframework.stereotype.Component
;
import
javax.crypto.spec.SecretKeySpec
;
import
javax.servlet.http.HttpServletRequest
;
import
javax.xml.bind.DatatypeConverter
;
import
java.security.Key
;
import
java.util.Arrays
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Date
;
import
java.util.concurrent.TimeUnit
;
import
java.util.stream.Collectors
;
/**
* @author /
*/
@Slf4j
@Component
@RequiredArgsConstructor
public
class
TokenProvider
implements
InitializingBean
{
private
final
RedisUtils
redisUtils
;
private
final
SecurityProperties
properties
;
public
static
final
String
AUTHORITIES_KEY
=
"auth"
;
private
static
Key
key
;
private
static
SignatureAlgorithm
signatureAlgorithm
;
@Override
public
void
afterPropertiesSet
()
{
signatureAlgorithm
=
SignatureAlgorithm
.
HS512
;
byte
[]
keyBytes
=
DatatypeConverter
.
parseBase64Binary
(
properties
.
getBase64Secret
());
key
=
new
SecretKeySpec
(
keyBytes
,
signatureAlgorithm
.
getJcaName
());
}
public
static
String
createToken
(
Authentication
authentication
)
{
String
authorities
=
authentication
.
getAuthorities
().
stream
()
.
map
(
GrantedAuthority:
:
getAuthority
)
.
collect
(
Collectors
.
joining
(
","
));
return
Jwts
.
builder
()
.
setSubject
(
authentication
.
getName
())
.
claim
(
AUTHORITIES_KEY
,
authorities
)
.
signWith
(
signatureAlgorithm
,
key
)
// 加入ID确保生成的 Token 都不一致
.
setId
(
IdUtil
.
simpleUUID
())
.
compact
();
}
public
Claims
getClaims
(
String
token
)
{
return
Jwts
.
parser
()
.
setSigningKey
(
DatatypeConverter
.
parseBase64Binary
(
properties
.
getBase64Secret
()))
.
parseClaimsJws
(
token
)
.
getBody
();
}
public
Authentication
getAuthentication
(
String
token
)
{
Claims
claims
=
getClaims
(
token
);
// fix bug: 当前用户如果没有任何权限时,在输入用户名后,刷新验证码会抛IllegalArgumentException
Object
authoritiesStr
=
claims
.
get
(
AUTHORITIES_KEY
);
Collection
<?
extends
GrantedAuthority
>
authorities
=
ObjectUtil
.
isNotEmpty
(
authoritiesStr
)
?
Arrays
.
stream
(
authoritiesStr
.
toString
().
split
(
","
))
.
map
(
SimpleGrantedAuthority:
:
new
)
.
collect
(
Collectors
.
toList
())
:
Collections
.
emptyList
();
User
principal
=
new
User
(
claims
.
getSubject
(),
"******"
,
authorities
);
return
new
UsernamePasswordAuthenticationToken
(
principal
,
token
,
authorities
);
}
/**
* @param token 需要检查的token
*/
public
void
checkRenewal
(
String
token
)
{
// 判断是否续期token,计算token的过期时间
long
time
=
redisUtils
.
getExpire
(
properties
.
getOnlineKey
()
+
token
)
*
1000
;
Date
expireDate
=
DateUtil
.
offset
(
new
Date
(),
DateField
.
MILLISECOND
,
(
int
)
time
);
// 判断当前时间与过期时间的时间差
long
differ
=
expireDate
.
getTime
()
-
System
.
currentTimeMillis
();
// 如果在续期检查的范围内,则续期
if
(
differ
<=
properties
.
getDetect
())
{
long
renew
=
time
+
properties
.
getRenew
();
redisUtils
.
expire
(
properties
.
getOnlineKey
()
+
token
,
renew
,
TimeUnit
.
MILLISECONDS
);
}
}
public
String
getToken
(
HttpServletRequest
request
)
{
String
bearerToken
=
request
.
getHeader
(
properties
.
getHeader
());
if
(
StringUtils
.
isBlank
(
bearerToken
))
{
return
null
;
}
if
(
bearerToken
.
startsWith
(
properties
.
getTokenStartWith
()))
{
// 去掉令牌前缀
return
bearerToken
.
replace
(
properties
.
getTokenStartWith
(),
""
);
}
else
{
log
.
debug
(
"非法Token:{}"
,
bearerToken
);
}
return
null
;
}
}
imgproc-server/src/main/java/com/zq/imgproc/constant/Threshold.java
0 → 100644
View file @
003b7634
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
0 → 100644
View file @
003b7634
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.common.annotation.rest.AnonymousGetMapping
;
import
com.zq.common.utils.AssertUtils
;
import
com.zq.common.utils.UuidUtils
;
import
com.zq.common.vo.ResultVo
;
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
(
"测试"
)
@AnonymousGetMapping
(
"/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/ImageDetectionController.java
0 → 100644
View file @
003b7634
package
com
.
zq
.
imgproc
.
controller
;
import
com.zq.common.utils.AssertUtils
;
import
com.zq.common.vo.PageVo
;
import
com.zq.common.vo.ResultVo
;
import
com.zq.imgproc.entity.ImageDetection
;
import
com.zq.imgproc.server.ImageDetectionService
;
import
com.zq.imgproc.vo.DetectionPageReq
;
import
io.swagger.annotations.ApiOperation
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.*
;
import
org.springframework.web.multipart.MultipartFile
;
/**
* <p>
* 前端控制器
* </p>
*
* @author chenhao
* @since 2023/11/28
*/
@RequestMapping
(
"/imgproc/detection"
)
@RestController
public
class
ImageDetectionController
{
@Autowired
ImageDetectionService
service
;
@ApiOperation
(
"分页查询"
)
@PostMapping
(
"/selectPage"
)
public
ResultVo
<
PageVo
<
ImageDetection
>>
selectPage
(
@RequestBody
DetectionPageReq
req
)
{
return
ResultVo
.
success
(
service
.
selectPage
(
req
));
}
@ApiOperation
(
"根据ID查询记录"
)
@GetMapping
(
"/selectOne/{id}"
)
public
ResultVo
<
ImageDetection
>
selectOne
(
@PathVariable
Long
id
)
{
AssertUtils
.
notNull
(
id
,
"数据ID不能为空!"
);
return
ResultVo
.
success
(
service
.
selectOne
(
id
));
}
@ApiOperation
(
"文件解压"
)
@PostMapping
(
"/upload"
)
public
ResultVo
<?>
upload
(
@RequestParam
Long
userId
,
@RequestParam
String
nickName
,
@RequestPart
MultipartFile
[]
file
)
throws
Exception
{
AssertUtils
.
notNull
(
userId
,
"用户ID不能为空!"
);
return
service
.
detection
(
userId
,
nickName
,
file
);
}
}
imgproc-server/src/main/java/com/zq/imgproc/controller/ImgProcController.java
0 → 100644
View file @
003b7634
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.common.utils.AssertUtils
;
import
com.zq.common.vo.ResultVo
;
import
com.zq.imgproc.server.ImgProcService
;
import
com.zq.imgproc.utils.DecompressUtil
;
import
com.zq.imgproc.utils.ImageUtil
;
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.InputStream
;
import
java.nio.charset.StandardCharsets
;
import
java.time.Duration
;
import
java.util.List
;
/**
* <p>
* 图片检测接口
* </p>
*
* @author chenhao
* @since 2022/10/24 14:58
*/
@Api
(
tags
=
"图片检测接口"
)
@RequestMapping
(
"/imgproc/server"
)
@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
(
"文件压缩(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
(
"图片旋转"
)
@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/controller/ImgSettingController.java
0 → 100644
View file @
003b7634
package
com
.
zq
.
imgproc
.
controller
;
import
cn.hutool.core.date.DateUtil
;
import
com.baomidou.mybatisplus.core.toolkit.Wrappers
;
import
com.zq.common.vo.ResultVo
;
import
com.zq.imgproc.dao.ImgSettingDao
;
import
com.zq.imgproc.entity.ImgSetting
;
import
io.swagger.annotations.ApiOperation
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.*
;
/**
* <p>
*
* </p>
*
* @author chenhao
* @since 2023/11/28
*/
@io
.
swagger
.
annotations
.
Api
(
tags
=
"图片指标"
)
@RequestMapping
(
"/imgproc/setting"
)
@RestController
public
class
ImgSettingController
{
@Autowired
ImgSettingDao
imgSettingDao
;
@ApiOperation
(
"根据用户ID获取指标"
)
@GetMapping
(
"/getSetting/{userId}"
)
public
ResultVo
<?>
getSetting
(
@PathVariable
long
userId
)
{
return
ResultVo
.
success
(
imgSettingDao
.
selectOne
(
Wrappers
.
lambdaQuery
(
ImgSetting
.
builder
().
userId
(
userId
).
build
()))
);
}
@ApiOperation
(
"设置或更新图片指标"
)
@PostMapping
(
"/setSetting"
)
public
ResultVo
<?>
setSetting
(
@RequestBody
ImgSetting
imgSetting
)
{
if
(
imgSetting
.
getUserId
()
==
null
||
imgSetting
.
getUserId
()
<
0
)
{
return
ResultVo
.
fail
(
"用户ID不能为空!"
);
}
if
(
imgSetting
.
getBrightnessStart
()
!=
null
)
{
if
(
imgSetting
.
getBrightnessStart
()
<
0
||
imgSetting
.
getBrightnessStart
()
>
255
)
{
return
ResultVo
.
fail
(
"亮度值的范围在0到255之间!"
);
}
}
if
(
imgSetting
.
getBrightnessEnd
()
!=
null
)
{
if
(
imgSetting
.
getBrightnessEnd
()
<
0
||
imgSetting
.
getBrightnessEnd
()
>
255
)
{
return
ResultVo
.
fail
(
"亮度值的范围在0到255之间!"
);
}
}
if
(
imgSetting
.
getBend
()
!=
null
)
{
if
(
imgSetting
.
getBend
()
<
0
||
imgSetting
.
getBend
()
>
1
)
{
return
ResultVo
.
fail
(
"弯曲置信度的范围在0到1之间!"
);
}
}
if
(
imgSetting
.
getId
()
==
null
)
{
imgSetting
.
setCreateTime
(
DateUtil
.
date
());
imgSetting
.
setUpdateTime
(
DateUtil
.
date
());
imgSettingDao
.
insert
(
imgSetting
);
}
else
{
imgSettingDao
.
updateById
(
imgSetting
);
}
return
ResultVo
.
success
(
imgSetting
);
}
}
imgproc-server/src/main/java/com/zq/imgproc/dao/ImageDetectionDao.java
0 → 100644
View file @
003b7634
package
com
.
zq
.
imgproc
.
dao
;
import
com.baomidou.mybatisplus.core.mapper.BaseMapper
;
import
com.baomidou.mybatisplus.extension.plugins.pagination.Page
;
import
com.zq.imgproc.entity.ImageDetection
;
import
com.zq.imgproc.vo.DetectionPageReq
;
import
org.apache.ibatis.annotations.Param
;
import
org.springframework.stereotype.Repository
;
import
java.util.List
;
/**
* <p>
*
* </p>
*
* @author chenhao
* @since 2023/11/28
*/
@Repository
public
interface
ImageDetectionDao
extends
BaseMapper
<
ImageDetection
>
{
Page
<
ImageDetection
>
queryPageList
(
Page
<
ImageDetection
>
page
,
@Param
(
"req"
)
DetectionPageReq
req
);
/**
* 批量插入
*/
Integer
insertBatch
(
List
<
ImageDetection
>
list
);
}
imgproc-server/src/main/java/com/zq/imgproc/dao/ImgSettingDao.java
0 → 100644
View file @
003b7634
package
com
.
zq
.
imgproc
.
dao
;
import
com.baomidou.mybatisplus.core.mapper.BaseMapper
;
import
com.zq.imgproc.entity.ImgSetting
;
import
org.springframework.stereotype.Repository
;
/**
* <p>
*
* </p>
*
* @author chenhao
* @since 2023/11/28
*/
@Repository
public
interface
ImgSettingDao
extends
BaseMapper
<
ImgSetting
>
{
}
imgproc-server/src/main/java/com/zq/imgproc/entity/ImageDetection.java
0 → 100644
View file @
003b7634
package
com
.
zq
.
imgproc
.
entity
;
import
com.baomidou.mybatisplus.annotation.IdType
;
import
com.baomidou.mybatisplus.annotation.TableField
;
import
com.baomidou.mybatisplus.annotation.TableId
;
import
com.baomidou.mybatisplus.annotation.TableName
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
io.swagger.annotations.ApiModelProperty
;
import
lombok.AllArgsConstructor
;
import
lombok.Builder
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
java.io.Serializable
;
import
java.util.Date
;
/**
* <p>
*
* </p>
*
* @author chenhao
* @since 2023-11-28
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName
(
"image_detection"
)
public
class
ImageDetection
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
@ApiModelProperty
(
"数据ID"
)
@TableId
(
value
=
"id"
,
type
=
IdType
.
AUTO
)
private
Long
id
;
@ApiModelProperty
(
"用户ID"
)
@TableField
(
"user_id"
)
private
Long
userId
;
@ApiModelProperty
(
"用户名称"
)
@TableField
(
"nick_name"
)
private
String
nickName
;
@ApiModelProperty
(
"图片名称"
)
@TableField
(
"img_name"
)
private
String
imgName
;
@ApiModelProperty
(
"文件名称"
)
@TableField
(
"file_name"
)
private
String
fileName
;
@ApiModelProperty
(
"文件路径"
)
@TableField
(
"file_url"
)
private
String
fileUrl
;
@ApiModelProperty
(
"图片大小"
)
@TableField
(
"size"
)
private
Double
size
;
@ApiModelProperty
(
"水平分辨率"
)
@TableField
(
"width_resolution"
)
private
Integer
widthResolution
;
@ApiModelProperty
(
"垂直分辨率"
)
@TableField
(
"height_resolution"
)
private
Integer
heightResolution
;
@ApiModelProperty
(
"DPI"
)
@TableField
(
"dpi"
)
private
Integer
dpi
;
@ApiModelProperty
(
"图片清晰度"
)
@TableField
(
"clarity"
)
private
Double
clarity
;
@ApiModelProperty
(
"图片弯曲置信度"
)
@TableField
(
"bend"
)
private
Double
bend
;
@ApiModelProperty
(
"图片亮度起始范围"
)
@TableField
(
"brightness"
)
private
Integer
brightness
;
@ApiModelProperty
(
"图片偏离角度"
)
@TableField
(
"deskew_angel"
)
private
Integer
deskewAngel
;
@ApiModelProperty
(
"图片黑边数量"
)
@TableField
(
"black"
)
private
Integer
black
;
@ApiModelProperty
(
"是否合格,0不合格,1合格"
)
@TableField
(
"qualified"
)
private
Integer
qualified
;
@ApiModelProperty
(
"上传时间"
)
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
uploadTime
;
}
imgproc-server/src/main/java/com/zq/imgproc/entity/ImgSetting.java
0 → 100644
View file @
003b7634
package
com
.
zq
.
imgproc
.
entity
;
import
com.baomidou.mybatisplus.annotation.IdType
;
import
com.baomidou.mybatisplus.annotation.TableField
;
import
com.baomidou.mybatisplus.annotation.TableId
;
import
com.baomidou.mybatisplus.annotation.TableName
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
io.swagger.annotations.ApiModelProperty
;
import
lombok.AllArgsConstructor
;
import
lombok.Builder
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
java.io.Serializable
;
import
java.util.Date
;
/**
* <p>
*
* </p>
*
* @author chenhao
* @since 2023/11/28
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName
(
value
=
"img_setting"
)
public
class
ImgSetting
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
@ApiModelProperty
(
"数据ID"
)
@TableId
(
value
=
"id"
,
type
=
IdType
.
AUTO
)
private
Long
id
;
@ApiModelProperty
(
"用户ID"
)
@TableField
(
"user_id"
)
private
Long
userId
;
@ApiModelProperty
(
"图片大小"
)
@TableField
(
"size"
)
private
Integer
size
;
@ApiModelProperty
(
"是否启用,0是禁用,1是启用"
)
@TableField
(
"size_enabled"
)
private
Integer
sizeEnabled
;
@ApiModelProperty
(
"图片DPI"
)
@TableField
(
"dpi"
)
private
Integer
dpi
;
@ApiModelProperty
(
"是否启用,0是禁用,1是启用"
)
@TableField
(
"dpi_enabled"
)
private
Integer
dpiEnabled
;
@ApiModelProperty
(
"图片清晰度"
)
@TableField
(
"clarity"
)
private
Double
clarity
;
@ApiModelProperty
(
"是否启用,0是禁用,1是启用"
)
@TableField
(
"clarity_enabled"
)
private
Integer
clarityEnabled
;
@ApiModelProperty
(
"图片亮度起始范围"
)
@TableField
(
"brightness_start"
)
private
Integer
brightnessStart
;
@ApiModelProperty
(
"图片亮度结束范围"
)
@TableField
(
"brightness_end"
)
private
Integer
brightnessEnd
;
@ApiModelProperty
(
"是否启用,0是禁用,1是启用"
)
@TableField
(
"brightness_enabled"
)
private
Integer
brightnessEnabled
;
@ApiModelProperty
(
"图片偏离角度"
)
@TableField
(
"deskew_angel"
)
private
Integer
deskewAngel
;
@ApiModelProperty
(
"是否启用,0是禁用,1是启用"
)
@TableField
(
"deskew_enabled"
)
private
Integer
deskewEnabled
;
@ApiModelProperty
(
"图片水平分辨率"
)
@TableField
(
"width_resolution"
)
private
Integer
widthResolution
;
@ApiModelProperty
(
"图片垂直分辨率"
)
@TableField
(
"height_resolution"
)
private
Integer
heightResolution
;
@ApiModelProperty
(
"是否启用,0是禁用,1是启用"
)
@TableField
(
"resolution_enabled"
)
private
Integer
resolutionEnabled
;
@ApiModelProperty
(
"黑边的数值"
)
@TableField
(
"black"
)
private
Integer
black
;
@ApiModelProperty
(
"是否启用,0是禁用,1是启用"
)
@TableField
(
"black_enabled"
)
private
Integer
blackEnabled
;
@ApiModelProperty
(
"弯曲的置信度"
)
@TableField
(
"bend"
)
private
Double
bend
;
@ApiModelProperty
(
"是否启用,0是禁用,1是启用"
)
@TableField
(
"bend_enabled"
)
private
Integer
bendEnabled
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
updateTime
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
createTime
;
}
imgproc-server/src/main/java/com/zq/imgproc/server/ImageDetectionService.java
0 → 100644
View file @
003b7634
package
com
.
zq
.
imgproc
.
server
;
import
cn.hutool.core.convert.Convert
;
import
cn.hutool.core.date.DateUtil
;
import
cn.hutool.core.io.FileUtil
;
import
com.baomidou.mybatisplus.core.toolkit.Wrappers
;
import
com.baomidou.mybatisplus.extension.plugins.pagination.Page
;
import
com.zq.common.utils.AssertUtils
;
import
com.zq.common.vo.PageVo
;
import
com.zq.common.vo.ResultVo
;
import
com.zq.imgproc.dao.ImageDetectionDao
;
import
com.zq.imgproc.dao.ImgSettingDao
;
import
com.zq.imgproc.entity.ImageDetection
;
import
com.zq.imgproc.entity.ImgSetting
;
import
com.zq.imgproc.utils.DecompressUtil
;
import
com.zq.imgproc.utils.Deskew
;
import
com.zq.imgproc.utils.ImageUtil
;
import
com.zq.imgproc.vo.DetectionPageReq
;
import
com.zq.imgproc.vo.ImgVO
;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.opencv.core.Mat
;
import
org.opencv.imgcodecs.Imgcodecs
;
import
org.springframework.stereotype.Service
;
import
org.springframework.web.multipart.MultipartFile
;
import
java.io.File
;
import
java.math.BigDecimal
;
import
java.math.RoundingMode
;
import
java.util.ArrayList
;
import
java.util.List
;
/**
* <p>
*
* </p>
*
* @author chenhao
* @since 2023/11/28
*/
@Slf4j
@RequiredArgsConstructor
@Service
public
class
ImageDetectionService
{
private
final
ImgSettingDao
imgSettingDao
;
private
final
ImageDetectionDao
imageDetectionDao
;
public
ResultVo
<?>
detection
(
long
userId
,
String
nickName
,
MultipartFile
[]
file
)
throws
Exception
{
// 查询用户配置
ImgSetting
imgSetting
=
imgSettingDao
.
selectOne
(
Wrappers
.
lambdaQuery
(
ImgSetting
.
builder
().
userId
(
userId
).
build
())
);
if
(
imgSetting
==
null
)
{
return
ResultVo
.
fail
(
"查询不到用户的配置信息!"
);
}
// 解压文件
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
,
one
.
getOriginalFilename
()));
}
else
{
String
url
=
ImageUtil
.
saveTempFile
(
one
);
imgs
.
add
(
ImgVO
.
builder
().
fileName
(
one
.
getOriginalFilename
()).
name
(
one
.
getOriginalFilename
()).
url
(
url
).
build
());
}
}
// 保存上传信息
List
<
ImageDetection
>
detections
=
new
ArrayList
<>();
File
image
;
for
(
ImgVO
img
:
imgs
)
{
image
=
FileUtil
.
file
(
img
.
getUrl
());
if
(!
image
.
exists
())
{
continue
;
}
// 图片检测
ImageDetection
detection
=
new
ImageDetection
();
// 设置上传信息
detection
.
setUserId
(
userId
);
detection
.
setNickName
(
nickName
);
detection
.
setUploadTime
(
DateUtil
.
date
());
// 设置文件信息
detection
.
setFileName
(
img
.
getName
());
detection
.
setImgName
(
img
.
getFileName
());
detection
.
setFileUrl
(
img
.
getUrl
());
detection
.
setSize
(
transformMb
(
image
.
length
()));
// 设置图片检测信息
Mat
src
=
Imgcodecs
.
imread
(
img
.
getUrl
());
// 检测图片的分辨率
detection
.
setWidthResolution
(
src
.
width
());
detection
.
setHeightResolution
(
src
.
height
());
// 检测图片的DPI
detection
.
setDpi
(
ImageUtil
.
getDpi
(
image
));
// 检测图片清晰度
BigDecimal
clarity
=
BigDecimal
.
valueOf
(
ImageUtil
.
tenengrad
(
src
));
detection
.
setClarity
(
clarity
.
setScale
(
2
,
RoundingMode
.
HALF_UP
).
doubleValue
());
// 检测图片的亮度
detection
.
setBrightness
(
Convert
.
toInt
(
ImageUtil
.
brightness
(
src
)));
// 检测图片倾斜角度
detection
.
setDeskewAngel
(
Math
.
abs
(
Deskew
.
getDeskewAngle
(
src
)));
// 检测图片的黑边
detection
.
setBlack
(
ImageUtil
.
blackDetection2
(
src
));
// 图片弯曲检测
// BendResult bendResult = BendUtil.getBendResult(img.getUrl());
// detection.setBend(bendResult.getConfidence());
detection
.
setBend
(
0.0
);
// 检查是否合格
detection
.
setQualified
(
check
(
detection
,
imgSetting
));
detections
.
add
(
detection
);
}
// 批量插入
imageDetectionDao
.
insertBatch
(
detections
);
return
ResultVo
.
success
(
detections
);
}
/**
* 检查图片是否合格
* 1. 亮度
* 2. 清晰度
* 3. 偏离度
* 4. 黑边
* 5. 弯曲
*/
private
Integer
check
(
ImageDetection
detection
,
ImgSetting
imgSetting
)
{
// 亮度
if
(
detection
.
getBrightness
()
<
imgSetting
.
getBrightnessStart
()
||
detection
.
getBrightness
()
>
imgSetting
.
getBrightnessEnd
())
{
return
0
;
}
// 清晰度
if
(
detection
.
getClarity
().
compareTo
(
imgSetting
.
getClarity
())
<
0
)
{
return
0
;
}
// 偏离度
if
(
detection
.
getDeskewAngel
()
>
imgSetting
.
getDeskewAngel
())
{
return
0
;
}
// 黑边
if
(
detection
.
getBlack
()
>
imgSetting
.
getBlack
())
{
return
0
;
}
// 弯曲度
if
(
detection
.
getBend
().
compareTo
(
imgSetting
.
getBend
())
>
0
)
{
return
0
;
}
return
1
;
}
/**
* 将字节数转换为MB
*/
private
double
transformMb
(
long
size
)
{
BigDecimal
sizeDecimal
=
BigDecimal
.
valueOf
(
size
);
BigDecimal
mb
=
BigDecimal
.
valueOf
(
1024
*
1024
);
return
sizeDecimal
.
divide
(
mb
,
2
,
RoundingMode
.
DOWN
).
doubleValue
();
}
public
PageVo
<
ImageDetection
>
selectPage
(
DetectionPageReq
req
)
{
Page
<
ImageDetection
>
ipage
=
Page
.
of
(
req
.
getPage
(),
req
.
getSize
());
imageDetectionDao
.
queryPageList
(
ipage
,
req
);
return
PageVo
.
ofReqVo
(
req
,
ipage
.
getRecords
(),
Convert
.
toInt
(
ipage
.
getTotal
()));
}
public
ImageDetection
selectOne
(
Long
id
)
{
return
imageDetectionDao
.
selectById
(
id
);
}
}
imgproc-server/src/main/java/com/zq/imgproc/server/ImgProcService.java
0 → 100644
View file @
003b7634
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.common.utils.UuidUtils
;
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/BendUtil.java
0 → 100644
View file @
003b7634
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.common.vo.ResultVo
;
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
0 → 100644
View file @
003b7634
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.common.utils.UuidUtils
;
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
,
String
name
)
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
).
name
(
name
).
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
0 → 100644
View file @
003b7634
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
0 → 100644
View file @
003b7634
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
;
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
0 → 100644
View file @
003b7634
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
0 → 100644
View file @
003b7634
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.common.exception.BusinessException
;
import
com.zq.common.utils.UuidUtils
;
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
;
}
/**
* 检测黑边数量
*/
public
static
int
blackDetection2
(
Mat
src
)
{
int
blackValue
=
40
;
int
width
=
src
.
width
();
int
height
=
src
.
height
();
int
rows
=
0
;
int
cols
=
0
;
for
(
int
row
=
0
;
row
<
height
;
row
++)
{
Scalar
mean
=
Core
.
mean
(
src
.
row
(
row
));
if
(
Convert
.
toInt
(
mean
.
val
[
0
])
<
blackValue
)
{
rows
++;
}
}
for
(
int
col
=
0
;
col
<
width
;
col
++)
{
Scalar
mean
=
Core
.
mean
(
src
.
col
(
col
));
if
(
Convert
.
toInt
(
mean
.
val
[
0
])
<
blackValue
)
{
cols
++;
}
}
return
Math
.
max
(
rows
,
cols
);
}
/**
* 获取图片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
0 → 100644
View file @
003b7634
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
0 → 100644
View file @
003b7634
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/Test2.java
0 → 100644
View file @
003b7634
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
0 → 100644
View file @
003b7634
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
0 → 100644
View file @
003b7634
package
com
.
zq
.
imgproc
.
utils
;
import
cn.hutool.core.io.FileUtil
;
import
com.zq.common.exception.BusinessException
;
import
com.zq.common.utils.UuidUtils
;
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/vo/BendResult.java
0 → 100644
View file @
003b7634
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
0 → 100644
View file @
003b7634
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
0 → 100644
View file @
003b7634
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/DetectionPageReq.java
0 → 100644
View file @
003b7634
package
com
.
zq
.
imgproc
.
vo
;
import
com.zq.common.vo.PageReqVo
;
import
lombok.AllArgsConstructor
;
import
lombok.Builder
;
import
lombok.NoArgsConstructor
;
/**
* <p>
*
* </p>
*
* @author chenhao
* @since 2023/11/28
*/
@Builder
@NoArgsConstructor
@AllArgsConstructor
public
class
DetectionPageReq
extends
PageReqVo
{
/**
* 用户名称
*/
String
nickName
;
/**
* 图片名称
*/
String
imgName
;
/**
* 文件名称
*/
String
fileName
;
/**
* 上传日期起始
*/
String
startTime
;
/**
* 上传日期结束
*/
String
endTime
;
public
String
getNickName
()
{
return
nickName
;
}
public
void
setNickName
(
String
nickName
)
{
this
.
nickName
=
nickName
;
}
public
String
getImgName
()
{
return
imgName
;
}
public
void
setImgName
(
String
imgName
)
{
this
.
imgName
=
imgName
;
}
public
String
getFileName
()
{
return
fileName
;
}
public
void
setFileName
(
String
fileName
)
{
this
.
fileName
=
fileName
;
}
public
String
getStartTime
()
{
return
startTime
;
}
public
void
setStartTime
(
String
startTime
)
{
this
.
startTime
=
startTime
;
}
public
String
getEndTime
()
{
return
endTime
;
}
public
void
setEndTime
(
String
endTime
)
{
this
.
endTime
=
endTime
;
}
}
imgproc-server/src/main/java/com/zq/imgproc/vo/DetectionResVo.java
0 → 100644
View file @
003b7634
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
0 → 100644
View file @
003b7634
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
0 → 100644
View file @
003b7634
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
;
String
name
;
}
imgproc-server/src/main/java/com/zq/imgproc/vo/OptimizationReq.java
0 → 100644
View file @
003b7634
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
0 → 100644
View file @
003b7634
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
0 → 100644
View file @
003b7634
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
0 → 100644
View file @
003b7634
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 @
003b7634
...
@@ -78,7 +78,17 @@ mybatis-plus:
...
@@ -78,7 +78,17 @@ mybatis-plus:
ip
:
ip
:
local-parsing
:
true
local-parsing
:
true
#imgconfig:
# opencv: /opt/services/tianjin-backend/lib/opencv_java460.so
# deskew: /opt/services/tianjin-backend/lib/Deskew/Bin/deskew
# 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
:
imgconfig
:
opencv
:
/opt/services/tianjin-backend/lib/opencv_java460.so
opencv
:
D:/project/image-backend/config/lib/opencv_java460.dll
deskew
:
/opt/services/tianjin-backend/lib/Deskew/Bin/deskew
deskew
:
C:/Users/11419/Desktop/Deskew/Bin/deskew.exe
deskewpy
:
/opt/services/tianjin-backend
/lib/correct.py
deskewpy
:
D:/project/imgproc
/lib/correct.py
imgproc-server/src/main/resources/mapper/ImageDetectionMapper.xml
0 → 100644
View file @
003b7634
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper
namespace=
"com.zq.imgproc.dao.ImageDetectionDao"
>
<select
id=
"queryPageList"
resultType=
"com.zq.imgproc.entity.ImageDetection"
>
SELECT *
FROM image_detection AS detection
<where>
<if
test=
"req.nickName != null and req.nickName != ''"
>
AND detection.nick_name like concat('%', #{req.nickName}, '%')
</if>
<if
test=
"req.imgName != null and req.imgName != ''"
>
AND detection.img_name like concat('%', #{req.imgName}, '%')
</if>
<if
test=
"req.fileName != null and req.fileName != ''"
>
AND detection.file_name like concat('%', #{req.fileName}, '%')
</if>
<if
test=
"req.startTime != null and req.startTime != ''"
>
AND detection.upload_time
>
= #{req.startTime}
</if>
<if
test=
"req.endTime != null and req.endTime != ''"
>
AND detection.upload_time
<
= #{req.endTime}
</if>
</where>
</select>
<insert
id=
"insertBatch"
parameterType=
"java.util.List"
>
insert into image_detection (user_id, nick_name, img_name, file_name, file_url,
size, width_resolution, height_resolution, dpi, clarity, bend, brightness,
deskew_angel, black, qualified, upload_time)
values
<foreach
collection=
"list"
item=
"item"
separator=
","
>
(#{item.userId}, #{item.nickName}, #{item.imgName}, #{item.fileName},
#{item.fileUrl}, #{item.size}, #{item.widthResolution}, #{item.heightResolution},
#{item.dpi}, #{item.clarity}, #{item.bend}, #{item.brightness},
#{item.deskewAngel}, #{item.black}, #{item.qualified}, #{item.uploadTime})
</foreach>
</insert>
</mapper>
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