Commit a9ca035e by 陈皓

更新

parent 4ba97838
...@@ -23,4 +23,8 @@ spring: ...@@ -23,4 +23,8 @@ spring:
uri: lb://IMGPROC-SERVER uri: lb://IMGPROC-SERVER
predicates: predicates:
- Path=/imgproc/** - Path=/imgproc/**
- id: file
uri: lb://IMGPROC-SERVER
predicates:
- Path=/file/**
...@@ -92,6 +92,11 @@ ...@@ -92,6 +92,11 @@
<version>5.8.19</version> <version>5.8.19</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>com.drewnoakes</groupId> <groupId>com.drewnoakes</groupId>
<artifactId>metadata-extractor</artifactId> <artifactId>metadata-extractor</artifactId>
<version>2.18.0</version> <version>2.18.0</version>
...@@ -111,6 +116,12 @@ ...@@ -111,6 +116,12 @@
<artifactId>springfox-swagger-ui</artifactId> <artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version> <version>2.9.2</version>
</dependency> </dependency>
<!-- guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.3-jre</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
......
...@@ -91,17 +91,7 @@ public class ApiController { ...@@ -91,17 +91,7 @@ public class ApiController {
// 去黑边 // 去黑边
RemoveBlackUtil2.remove(imgPath, savePath); RemoveBlackUtil2.remove(imgPath, savePath);
// 图片弯曲矫正 return ResultVo.success(Base64Encoder.encode(savePath));
HashMap<String, Object> map = new HashMap<>();
map.put("file", FileUtil.file(savePath));
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("图片灰度化") @ApiOperation("图片灰度化")
......
package com.zq.imgproc.controller;
import cn.hutool.core.util.URLUtil;
import com.zq.imgproc.utils.AssertUtils;
import io.swagger.annotations.Api;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
/**
* <p>
*
* </p>
*
* @author zq
* @since 2023/11/15
*/
@Api(tags = "访问文件")
@RestController
@CrossOrigin
@RequestMapping("/")
public class FileController {
@GetMapping(value = "/file/**")
public ResponseEntity<Resource> archive(HttpServletRequest request) {
String requestURI = request.getRequestURI();
requestURI = URLUtil.decode(requestURI);
File file = new File(requestURI);
AssertUtils.isTrue(file.exists(), "访问文件不存在");
String contentDisposition = ContentDisposition
.builder("attachment")
.filename(requestURI)
.build().toString();
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(new FileSystemResource(file));
}
}
package com.zq.imgproc.controller; package com.zq.imgproc.controller;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.servlet.ServletUtil; import cn.hutool.extra.servlet.ServletUtil;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.zq.imgproc.server.ImgProcService; import com.zq.imgproc.server.ImgProcService;
import com.zq.imgproc.utils.AssertUtils; import com.zq.imgproc.utils.AssertUtils;
import com.zq.imgproc.utils.DecompressUtil; import com.zq.imgproc.utils.DecompressUtil;
...@@ -11,6 +14,8 @@ import com.zq.imgproc.vo.*; ...@@ -11,6 +14,8 @@ import com.zq.imgproc.vo.*;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired; 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.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
...@@ -18,8 +23,10 @@ import javax.servlet.http.HttpServletResponse; ...@@ -18,8 +23,10 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
/** /**
* <p> * <p>
...@@ -35,11 +42,20 @@ import java.util.List; ...@@ -35,11 +42,20 @@ import java.util.List;
@RestController @RestController
public class ImgProcController { public class ImgProcController {
// @Resource /**
// RedisUtils redisUtils; * 普通的缓存 Cache
// private final double CAST = 1; */
// private final double DA = 1; private final Cache<String, Double> cache = CacheBuilder.newBuilder()
// private final double CLARITY = 10; .expireAfterAccess(Duration.ofMinutes(30))
.expireAfterWrite(Duration.ofHours(48))
.maximumSize(1024)
.concurrencyLevel(4)
.initialCapacity(1024)
.build();
private final double START_VALUE = 75;
private final double END_VALUE = 175;
private final double CLARITY = 2.5;
private final ImgProcService service; private final ImgProcService service;
@Autowired @Autowired
...@@ -89,6 +105,12 @@ public class ImgProcController { ...@@ -89,6 +105,12 @@ public class ImgProcController {
return ResultVo.success(service.detection(pathList)); 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工具)") @ApiOperation("图片校正(deskew工具)")
@PostMapping("/imageCorrection") @PostMapping("/imageCorrection")
public ResultVo<?> imageCorrection(@RequestBody List<ImgVO> pathList) throws IOException { public ResultVo<?> imageCorrection(@RequestBody List<ImgVO> pathList) throws IOException {
...@@ -107,26 +129,29 @@ public class ImgProcController { ...@@ -107,26 +129,29 @@ public class ImgProcController {
return ResultVo.success(service.removeBlack(pathList)); return ResultVo.success(service.removeBlack(pathList));
} }
// @ApiOperation("获取配置") @ApiOperation("获取配置")
// @PostMapping("/getSetting") @GetMapping("/getSetting")
// public ResultVo<?> getSetting() { public ResultVo<?> getSetting() {
// double cast = Convert.toDouble(redisUtils.get("img-cast")) == null ? CAST : Convert.toDouble(redisUtils.get("img-cast")); Double temp1 = cache.getIfPresent("img-start");
// double da = Convert.toDouble(redisUtils.get("img-da")) == null ? DA : Convert.toDouble(redisUtils.get("img-da")); Double temp2 = cache.getIfPresent("img-end");
// double clarity = Convert.toDouble(redisUtils.get("img-clarity")) == null ? CLARITY : Convert.toDouble(redisUtils.get("img-clarity")); Double temp3 = cache.getIfPresent("img-clarity");
// return ResultVo.success(SettingVO.builder().cast(cast).da(da).clarity(clarity).build()); double start = temp1 == null ? START_VALUE : temp1;
// } double end = temp2 == null ? END_VALUE : temp2;
// double clarity = temp3 == null ? CLARITY : temp3;
// @ApiOperation("设置配置") return ResultVo.success(SettingVO.builder().startValue(start).endValue(end).clarity(clarity).build());
// @PostMapping("/setSetting") }
// public ResultVo<?> setSetting(@RequestBody SettingVO setting) {
// if (setting == null) { @ApiOperation("设置配置")
// return ResultVo.fail("配置不能为空!"); @PostMapping("/setSetting")
// } public ResultVo<?> setSetting(@RequestBody SettingVO setting) {
// redisUtils.setStr("img-cast", String.valueOf(setting.getCast())); if (setting == null) {
// redisUtils.setStr("img-da", String.valueOf(setting.getDa())); return ResultVo.fail("配置不能为空!");
// redisUtils.setStr("img-clarity", String.valueOf(setting.getClarity())); }
// return ResultVo.success(); cache.put("img-start", setting.getStartValue());
// } cache.put("img-end", setting.getEndValue());
cache.put("img-clarity", setting.getClarity());
return ResultVo.success();
}
@ApiOperation("图片旋转") @ApiOperation("图片旋转")
@PostMapping("/rotate") @PostMapping("/rotate")
......
...@@ -6,6 +6,7 @@ import cn.hutool.core.codec.Base64Encoder; ...@@ -6,6 +6,7 @@ import cn.hutool.core.codec.Base64Encoder;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.img.ImgUtil; import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import com.alibaba.excel.EasyExcel;
import com.drew.imaging.ImageMetadataReader; import com.drew.imaging.ImageMetadataReader;
import com.drew.metadata.Directory; import com.drew.metadata.Directory;
import com.drew.metadata.Metadata; import com.drew.metadata.Metadata;
...@@ -23,13 +24,22 @@ import org.opencv.core.MatOfDouble; ...@@ -23,13 +24,22 @@ import org.opencv.core.MatOfDouble;
import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc; import org.opencv.imgproc.Imgproc;
import org.springframework.beans.factory.annotation.Value; 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.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
...@@ -56,7 +66,6 @@ public class ImgProcService { ...@@ -56,7 +66,6 @@ public class ImgProcService {
public List<ImgVO> detection(List<ImgVO> pathList) throws Exception { public List<ImgVO> detection(List<ImgVO> pathList) throws Exception {
List<ImgVO> res = new ArrayList<>(pathList.size()); List<ImgVO> res = new ArrayList<>(pathList.size());
for (ImgVO one : pathList) { for (ImgVO one : pathList) {
DetectionResVo2 vo = new DetectionResVo2(); DetectionResVo2 vo = new DetectionResVo2();
String path = one.getUrl(); String path = one.getUrl();
Mat image = Imgcodecs.imread(path); Mat image = Imgcodecs.imread(path);
...@@ -66,13 +75,10 @@ public class ImgProcService { ...@@ -66,13 +75,10 @@ public class ImgProcService {
// 检测图片的DPI // 检测图片的DPI
vo.setDpi(getDpi(FileUtil.file(path))); vo.setDpi(getDpi(FileUtil.file(path)));
// 检测图片清晰度 // 检测图片清晰度
vo.setClarity(ImageUtil.tenengrad(image)); BigDecimal clarity = BigDecimal.valueOf(ImageUtil.tenengrad(image));
vo.setClarity(clarity.setScale(2, RoundingMode.HALF_UP).doubleValue());
// 检测图片的亮度 // 检测图片的亮度
double[] arr = brightnessDetection(image); vo.setBrightness(Convert.toInt(ImageUtil.brightness(image)));
if (arr != null) {
vo.setCast(arr[0]);
vo.setDa(arr[1]);
}
// 检测图片倾斜角度 // 检测图片倾斜角度
vo.setAngle(getAngle(image)); vo.setAngle(getAngle(image));
// 检测图片的黑边 // 检测图片的黑边
...@@ -438,7 +444,7 @@ public class ImgProcService { ...@@ -438,7 +444,7 @@ public class ImgProcService {
index++; index++;
} }
// 修正 // 修正
return ImageUtil.correct(pathList); return pathList;
} }
public List<ImgVO> deskew(List<ImgVO> pathList) { public List<ImgVO> deskew(List<ImgVO> pathList) {
...@@ -693,5 +699,45 @@ public class ImgProcService { ...@@ -693,5 +699,45 @@ public class ImgProcService {
return Base64Encoder.encode(FileUtil.readBytes(dst)); return Base64Encoder.encode(FileUtil.readBytes(dst));
} }
public ResponseEntity<Resource> getDetection(List<ImgVO> pathList) throws Exception {
if (pathList == null || pathList.size() == 0) {
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) {
DetectionResVo2 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.setBlack(detectionRes.getBlack() ? "是" : "否");
dtos.add(dto);
}
return dtos;
}
} }
...@@ -10,9 +10,6 @@ import java.util.HashMap; ...@@ -10,9 +10,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.opencv.highgui.HighGui.imshow;
import static org.opencv.highgui.HighGui.waitKey;
public class Deskew { public class Deskew {
public static void main(String[] args) { public static void main(String[] args) {
...@@ -136,6 +133,9 @@ public class Deskew { ...@@ -136,6 +133,9 @@ public class Deskew {
int w = image.cols(); int w = image.cols();
int h = image.rows(); int h = image.rows();
if (angle < 0) {
angle = 360 + angle;
}
Point center = new Point((double) w / 2, (double) h / 2); Point center = new Point((double) w / 2, (double) h / 2);
double scale = 1.0; double scale = 1.0;
......
...@@ -152,14 +152,17 @@ public class ImageUtil { ...@@ -152,14 +152,17 @@ public class ImageUtil {
// 灰度化 // 灰度化
Mat mat = Imgcodecs.imread(src); Mat mat = Imgcodecs.imread(src);
Mat gray = mat.clone(); Mat gray = mat.clone();
if (mat.channels() == 4 || mat.channels() == 3) { // 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); Imgproc.cvtColor(mat, gray, Imgproc.COLOR_BGR2GRAY);
} else if (mat.channels() == 2) {
Imgproc.cvtColor(mat, gray, Imgproc.COLOR_BGR5652GRAY);
} else {
gray = mat;
}
saveImage(gray, dst); saveImage(gray, dst);
gray.release();
mat.release();
} }
/** /**
...@@ -383,7 +386,7 @@ public class ImageUtil { ...@@ -383,7 +386,7 @@ public class ImageUtil {
} }
/** /**
* 计算图片平均亮度 * 计算图片平均亮度,范围是0到255
* *
* @param path 图片路径 * @param path 图片路径
* @return 图片平均亮度 * @return 图片平均亮度
......
...@@ -17,14 +17,12 @@ import java.util.Random; ...@@ -17,14 +17,12 @@ import java.util.Random;
* @author chenhao * @author chenhao
* @since 2023/3/8 9:31 * @since 2023/3/8 9:31
*/ */
@SuppressWarnings("all")
public class RemoveBlackUtil { public class RemoveBlackUtil {
public static void main(String[] args) { public static void main(String[] args) {
long start = System.currentTimeMillis(); String testImg = "C:\\Users\\11419\\Desktop\\test\\a.jpg";
String testImg = "C:\\Users\\11419\\Desktop\\project\\company\\test\\9.jpg"; String resImg = "C:\\Users\\11419\\Desktop\\test\\8.jpg";
String resImg = "C:\\Users\\11419\\Desktop\\project\\company\\test\\res9.jpg"; System.load("D:\\project\\imgproc\\lib\\opencv_java460.dll");
System.load("C:\\Users\\11419\\Desktop\\project\\company\\imgproc\\lib\\opencv_java460.dll");
remove(testImg, resImg); remove(testImg, resImg);
} }
...@@ -36,21 +34,21 @@ public class RemoveBlackUtil { ...@@ -36,21 +34,21 @@ public class RemoveBlackUtil {
Mat greyImg = img.clone(); Mat greyImg = img.clone();
//1.彩色转灰色 //1.彩色转灰色
Imgproc.cvtColor(img, greyImg, Imgproc.COLOR_BGR2GRAY); Imgproc.cvtColor(img, greyImg, Imgproc.COLOR_BGR2GRAY);
// ImageUtil.saveImage(greyImg, "C:\\Users\\11419\\Desktop\\project\\company\\test\\guoc\\1.jpg"); ImageUtil.saveImage(greyImg, "C:\\Users\\11419\\Desktop\\test\\1.jpg");
Mat gaussianBlurImg = greyImg.clone(); Mat gaussianBlurImg = greyImg.clone();
// 2.高斯滤波,降噪 // 2.高斯滤波,降噪
Imgproc.GaussianBlur(greyImg, gaussianBlurImg, new Size(3,3),2,2); Imgproc.GaussianBlur(greyImg, gaussianBlurImg, new Size(3,3),0);
// ImageUtil.saveImage(gaussianBlurImg, "C:\\Users\\11419\\Desktop\\project\\company\\test\\guoc\\2.jpg"); ImageUtil.saveImage(greyImg, "C:\\Users\\11419\\Desktop\\test\\2.jpg");
Mat cannyImg = gaussianBlurImg.clone();
// 3.Canny边缘检测 // 3.Canny边缘检测
Imgproc.Canny(gaussianBlurImg, cannyImg, 20, 60, 3, false); Mat cannyImg = gaussianBlurImg.clone();
// ImageUtil.saveImage(cannyImg, "C:\\Users\\11419\\Desktop\\project\\company\\test\\guoc\\3.jpg"); Imgproc.Canny(gaussianBlurImg, cannyImg, 50, 200);
ImageUtil.saveImage(cannyImg, "C:\\Users\\11419\\Desktop\\test\\3.jpg");
// 4.膨胀,连接边缘 // 4.膨胀,连接边缘
Mat dilateImg = cannyImg.clone(); Mat dilateImg = cannyImg.clone();
Imgproc.dilate(cannyImg, dilateImg, new Mat(), new Point(-1, -1), 2, 1, new Scalar(1)); Imgproc.dilate(cannyImg, dilateImg, new Mat(), new Point(-1, -1), 3, 1, new Scalar(1));
// ImageUtil.saveImage(dilateImg, "C:\\Users\\11419\\Desktop\\project\\company\\test\\guoc\\4.jpg"); ImageUtil.saveImage(dilateImg, "C:\\Users\\11419\\Desktop\\test\\4.jpg");
//5.对边缘检测的结果图再进行轮廓提取 //5.对边缘检测的结果图再进行轮廓提取
List<MatOfPoint> contours = new ArrayList<>(); List<MatOfPoint> contours = new ArrayList<>();
List<MatOfPoint> drawContours = new ArrayList<>(); List<MatOfPoint> drawContours = new ArrayList<>();
...@@ -64,8 +62,7 @@ public class RemoveBlackUtil { ...@@ -64,8 +62,7 @@ public class RemoveBlackUtil {
MatOfPoint2f approx = new MatOfPoint2f(); MatOfPoint2f approx = new MatOfPoint2f();
approx.convertTo(approx, CvType.CV_32F); approx.convertTo(approx, CvType.CV_32F);
for (int i = 0; i < contours.size(); i++) { for (MatOfPoint contour : contours) {
MatOfPoint contour = contours.get(i);
// 边框的凸包 // 边框的凸包
Imgproc.convexHull(contour, hull); Imgproc.convexHull(contour, hull);
// 用凸包计算出新的轮廓点 // 用凸包计算出新的轮廓点
...@@ -78,7 +75,7 @@ public class RemoveBlackUtil { ...@@ -78,7 +75,7 @@ public class RemoveBlackUtil {
MatOfPoint2f contourHull = new MatOfPoint2f(); MatOfPoint2f contourHull = new MatOfPoint2f();
contourHull.fromList(newPoints); contourHull.fromList(newPoints);
// 多边形拟合凸包边框(此时的拟合的精度较低) // 多边形拟合凸包边框(此时的拟合的精度较低)
Imgproc.approxPolyDP(contourHull, approx, Imgproc.arcLength(contourHull, true)*0.02, true); Imgproc.approxPolyDP(contourHull, approx, Imgproc.arcLength(contourHull, true) * 0.02, true);
MatOfPoint mat = new MatOfPoint(); MatOfPoint mat = new MatOfPoint();
mat.fromArray(approx.toArray()); mat.fromArray(approx.toArray());
drawContours.add(mat); drawContours.add(mat);
...@@ -89,7 +86,7 @@ public class RemoveBlackUtil { ...@@ -89,7 +86,7 @@ public class RemoveBlackUtil {
Imgproc.isContourConvex(approxf1)) { Imgproc.isContourConvex(approxf1)) {
double maxCosine = 0; double maxCosine = 0;
for (int j = 2; j < 5; j++) { for (int j = 2; j < 5; j++) {
double cosine = Math.abs(getAngle(approxf1.toArray()[j%4], approxf1.toArray()[j-2], approxf1.toArray()[j-1])); double cosine = Math.abs(getAngle(approxf1.toArray()[j % 4], approxf1.toArray()[j - 2], approxf1.toArray()[j - 1]));
maxCosine = Math.max(maxCosine, cosine); maxCosine = Math.max(maxCosine, cosine);
} }
// 角度大概72度 // 角度大概72度
...@@ -103,38 +100,35 @@ public class RemoveBlackUtil { ...@@ -103,38 +100,35 @@ public class RemoveBlackUtil {
} }
//这里是把提取出来的轮廓通过不同颜色的线描述出来,具体效果可以自己去看 //这里是把提取出来的轮廓通过不同颜色的线描述出来,具体效果可以自己去看
Random r = new Random(); Random r = new Random();
// for (int i = 0; i < drawContours.size(); i++) { for (int i = 0; i < drawContours.size(); i++) {
// Imgproc.drawContours(linePic, drawContours, i, new Scalar(r.nextInt(255),r.nextInt(255), r.nextInt(255))); Imgproc.drawContours(linePic, drawContours, i, new Scalar(r.nextInt(255),r.nextInt(255), r.nextInt(255)));
// } }
// ImageUtil.saveImage(linePic, "C:\\Users\\11419\\Desktop\\project\\company\\test\\guoc\\5.jpg"); ImageUtil.saveImage(linePic, "C:\\Users\\11419\\Desktop\\test\\5.jpg");
//7.找出最大的矩形 //7.找出最大的矩形
int index = findLargestSquare(squares); int index = findLargestSquare(squares);
MatOfPoint largest_square = null; MatOfPoint largest_square;
if(squares != null && squares.size() > 0){ if(squares.size() > 0){
largest_square = squares.get(index); largest_square = squares.get(index);
}else{ }else{
System.out.println("图片无法识别"); System.out.println("图片无法识别");
return; return;
} }
Mat polyPic = Mat.zeros(img.size(), CvType.CV_8UC3); Mat polyPic = Mat.zeros(img.size(), CvType.CV_8UC3);
// Imgproc.drawContours(polyPic, squares, index, new Scalar(0, 0,255), 2); Imgproc.drawContours(polyPic, squares, index, new Scalar(0, 0,255), 2);
// ImageUtil.saveImage(polyPic, "C:\\Users\\11419\\Desktop\\project\\company\\test\\guoc\\6.jpg"); ImageUtil.saveImage(polyPic, "C:\\Users\\11419\\Desktop\\test\\6.jpg");
//存储矩形的四个凸点 //存储矩形的四个凸点
hull = new MatOfInt(); hull = new MatOfInt();
Imgproc.convexHull(largest_square, hull, false); Imgproc.convexHull(largest_square, hull, false);
List<Integer> hullList = hull.toList(); List<Integer> hullList = hull.toList();
List<Point> polyContoursList = largest_square.toList(); List<Point> polyContoursList = largest_square.toList();
List<Point> hullPointList = new ArrayList<>(); List<Point> hullPointList = new ArrayList<>();
List<Point> lastHullPointList = new ArrayList<>(); for (Integer integer : hullList) {
for(int i = 0; i < hullList.size();i++){ Imgproc.circle(polyPic, polyContoursList.get(integer), 10, new Scalar(r.nextInt(255), r.nextInt(255), r.nextInt(255), 3));
Imgproc.circle(polyPic, polyContoursList.get(hullList.get(i)), 10, new Scalar(r.nextInt(255),r.nextInt(255), r.nextInt(255), 3)); hullPointList.add(polyContoursList.get(integer));
hullPointList.add(polyContoursList.get(hullList.get(i)));
} }
Core.addWeighted(polyPic, 1, img, 1, 0, img); Core.addWeighted(polyPic, 1, img, 1, 0, img);
// ImageUtil.saveImage(img, "C:\\Users\\11419\\Desktop\\project\\company\\test\\guoc\\7.jpg"); ImageUtil.saveImage(img, "C:\\Users\\11419\\Desktop\\test\\7.jpg");
for(int i = 0; i < hullPointList.size(); i++){ List<Point> lastHullPointList = new ArrayList<>(hullPointList);
lastHullPointList.add(hullPointList.get(i));
}
//dstPoints储存的是变换后各点的坐标,依次为左上,右上,右下, 左下 //dstPoints储存的是变换后各点的坐标,依次为左上,右上,右下, 左下
//srcPoints储存的是上面得到的四个角的坐标 //srcPoints储存的是上面得到的四个角的坐标
Point[] dstPoints = {new Point(0,0), new Point(img.cols(),0), new Point(img.cols(),img.rows()), new Point(0,img.rows())}; Point[] dstPoints = {new Point(0,0), new Point(img.cols(),0), new Point(img.cols(),img.rows()), new Point(0,img.rows())};
......
...@@ -18,7 +18,7 @@ public class RemoveBlackUtil2 { ...@@ -18,7 +18,7 @@ public class RemoveBlackUtil2 {
/** /**
* 去黑边"全黑"阈值 * 去黑边"全黑"阈值
*/ */
private static final Integer BLACK_VALUE = 200; private static final Integer BLACK_VALUE = 100;
public static void remove(String src, String dst) { public static void remove(String src, String dst) {
Mat mat = Imgcodecs.imread(src); Mat mat = Imgcodecs.imread(src);
......
package com.zq.imgproc.utils;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import java.io.IOException;
/**
* <p>
*
* </p>
*
* @author chenhao
* @since 2023/11/20
*/
public class Test2 {
public static void main(String[] args) throws IOException {
System.load("D:\\project\\imgproc\\lib\\opencv_java460.dll");
String src = "C:\\Users\\11419\\Desktop\\Deskew\\TestImages\\6.png";
String dst = "C:\\Users\\11419\\Desktop\\Deskew\\TestImages\\6res.png";
// 读取输入图像
Mat inputImage = Imgcodecs.imread(src);
// 将图像转换为灰度图像
Mat grayImage = new Mat();
Imgproc.cvtColor(inputImage, grayImage, Imgproc.COLOR_BGR2GRAY);
// 对灰度图像进行高斯模糊处理
Mat blurredImage = new Mat();
Imgproc.GaussianBlur(grayImage, blurredImage, new org.opencv.core.Size(3, 3), 0);
// 使用拉普拉斯算子进行边缘检测
Mat laplacianImage = new Mat();
Imgproc.Laplacian(blurredImage, laplacianImage, CvType.CV_16S, 3, 1, 0, Core.BORDER_DEFAULT);
Core.convertScaleAbs(laplacianImage, laplacianImage);
// 对滤波后的图像进行阈值处理,找到孤立点
double threshold = 30.0;
Mat binaryImage = new Mat();
Imgproc.threshold(laplacianImage, binaryImage, threshold, 255, Imgproc.THRESH_BINARY);
// 计算检测到的孤立点数量
int isolatedPointCount = Core.countNonZero(binaryImage);
// 输出孤立点数量
System.out.println("检测到的孤立点数量:" + isolatedPointCount);
// 可以在结果图像上标记检测到的孤立点,或者保存结果图像以便查看
Imgcodecs.imwrite(dst, binaryImage);
}
}
package com.zq.imgproc.utils; package com.zq.imgproc.utils;
import cn.hutool.core.convert.Convert; 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.DetectionResVo3;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.opencv.core.*; import org.apache.commons.imaging.ImageInfo;
import org.apache.commons.imaging.Imaging;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import java.util.ArrayList; import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.opencv.highgui.HighGui.imshow;
import static org.opencv.highgui.HighGui.waitKey;
@Slf4j @Slf4j
public class TestUtil { public class TestUtil {
public static void main(String[] args) { public static void main(String[] args) throws Exception {
System.load("C:/Users/11419/Desktop/lib/opencv_java460.dll"); System.load("D:/project/imgproc/lib/opencv_java460.dll");
Mat image = Imgcodecs.imread("C:/Users/11419/Desktop/Deskew/TestImages/res.png"); String src = "C:\\Users\\11419\\Desktop\\4.png";
String dst = "C:\\Users\\11419\\Desktop\\res.png";
// 转换为灰度图像 // deskew(src, dst);
Mat grayImage = new Mat(); RemoveBlackUtil2.remove(src, dst);
Imgproc.cvtColor(image, grayImage, Imgproc.COLOR_BGR2GRAY); // bright(src, dst);
// im(src, dst);
// 创建一个掩码,初始化为全黑 }
Mat mask = Mat.zeros(new Size(grayImage.cols() + 2, grayImage.rows() + 2), CvType.CV_8U);
// 执行漫水填充算法
Imgproc.floodFill(grayImage, mask, new org.opencv.core.Point(0, 0), new Scalar(255), null,
new Scalar(5, 5, 5, 5), new Scalar(5, 5, 5, 5));
// 进行二值化处理
Mat binaryImage = new Mat();
Imgproc.threshold(grayImage, binaryImage, 1, 255, Imgproc.THRESH_BINARY);
// 创建一个与二值化图像大小相同的全白图像
Mat filledImage = Mat.zeros(binaryImage.size(), CvType.CV_8U);
filledImage.setTo(new Scalar(255));
// 将非黑色区域从二值化图像复制到全白图像中
binaryImage.copyTo(filledImage, binaryImage);
imshow("test", binaryImage); public static void deskew(String src, String dst) {
waitKey(); 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);
} }
private static void test() { public static void bright(String src, String dst) {
String testImg = "C:/Users/11419/Desktop/Deskew/TestImages/4.png"; double brightness = ImageUtil.brightness(src);
String resImg = "C:/Users/11419/Desktop/Deskew/TestImages/res.png"; System.out.println(brightness);
long start = System.currentTimeMillis(); // 亮度异常执行亮度矫正
Mat src = Imgcodecs.imread(testImg); if (brightness < 100 || brightness > 500) {
// 图片灰度化 // 计算亮度调整值
Mat gray = src.clone(); double alpha = 300 / brightness;
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY); // 执行亮度调整
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) {
// @Cleanup double laplacianScore = ImageUtil.imageSharpnessDetector(src);
// UMat blur = gray.clone(); System.out.println(laplacianScore);
// GaussianBlur(gray, blur, new Size(9, 9), 0); if (laplacianScore < 500) {
// // 二值化(?) Mat unsharpMaskingMat = ImageUtil.unsharpMasking(src);
// @Cleanup // 保存调整后的图像
// UMat thresh = blur.clone(); Imgcodecs.imwrite(dst, unsharpMaskingMat);
// threshold(blur, thresh, 0, 255, THRESH_BINARY_INV + THRESH_OTSU); }
}
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5, 5)); public static DetectionResVo3 detection(String src) throws Exception {
// 图片膨胀 DetectionResVo3 res = new DetectionResVo3();
Mat erode = gray.clone(); Mat image = Imgcodecs.imread(src);
Imgproc.erode(gray, erode, kernel); // 检测图片的分辨率
// 图片腐蚀 res.setWidthResolution(image.width());
Mat dilate = erode.clone(); res.setHeightResolution(image.height());
Imgproc.dilate(erode, dilate, kernel); // 检测图片的DPI
// 边缘检测 res.setDpi(getDpi(FileUtil.file(src)));
Mat canny = dilate.clone(); // 检测图片清晰度
Imgproc.Canny(dilate, canny, 50, 150); res.setClarity(ImageUtil.imageSharpnessDetector(image));
// 霍夫变换得到线条 // 检测图片的亮度
Mat lines = new Mat(); res.setBrightness(ImageUtil.brightness(image));
//累加器阈值参数,小于设置值不返回 // 检测图片倾斜角度
int threshold = 90; res.setAngle(Deskew.getDeskewAngle(image));
//最低线段长度,低于设置值则不返回 // 检测图片的黑边
double minLineLength = 100; res.setBlack(blackDetection(image));
//间距小于该值的线当成同一条线 return res;
double maxLineGap = 10;
// 霍夫变换,通过步长为1,角度为PI/180来搜索可能的直线
Imgproc.HoughLinesP(canny, lines, 1, Math.PI / 180, threshold, minLineLength, maxLineGap);
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.0;
// }
// 可能还得需要考虑方差来决定选择平均数还是众数
int angle = most(angelList);
Mat rotated = rotate(src, angle, 1.0);
gray.release();
kernel.release();
erode.release();
dilate.release();
lines.release();
canny.release();
} }
/** /**
* 求数组众数 * 获取图片DPI
* *
* @param angelList 数组 * @param file 图片文件
* @return 数组众数 * @return [水平DPI,垂直DPI]
*/ */
private static int most(List<Integer> angelList) { private static Integer getDpi(File file) throws Exception {
if (angelList.isEmpty()) { int res;
return 0; 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());
} }
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; return res;
} }
/** /**
* 计算直线的倾斜角 * 黑边像素阈值
*/
private static final Integer THRESHOLD = 30;
/**
* 黑边检测
*
* @param src 图片矩阵
* @return true表示可能存在黑边
*/ */
private static int calculateAngle(double x1, double y1, double x2, double y2) { public static boolean blackDetection(Mat src) {
double dx = x2 - x1; int width = src.width();
double dy = y2 - y1; int height = src.height();
if (Math.abs(dx) < 1e-4) { // 定义初始边界
return 90; int top = 0;
} else if (Math.abs(dy) < 1e-4) { int left = 0;
return 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 { } else {
double radians = Math.atan2(dy, dx); break;
double degrees = Math.toDegrees(radians);
return Convert.toInt(Math.round(degrees));
} }
} }
// 左边黑边判断
public static Mat rotate(Mat image, double angle, double scale) { for (int col = 0; col < width; col++) {
int w = image.cols(); if (ImageUtil.sum(src.col(col)) / height < THRESHOLD) {
int h = image.rows(); left = col;
} else {
Point center = new Point((double) w / 2, (double) h / 2); break;
}
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);
imshow("test", rotatedImage);
waitKey();
return rotatedImage;
} }
// 右边黑边判断
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;
}
} }
package com.zq.imgproc.vo;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.annotations.ApiModelProperty;
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 String black;
}
...@@ -27,11 +27,8 @@ public class DetectionResVo2 { ...@@ -27,11 +27,8 @@ public class DetectionResVo2 {
@ApiModelProperty("图片的DPI") @ApiModelProperty("图片的DPI")
private Integer dpi; private Integer dpi;
@ApiModelProperty("亮度值") @ApiModelProperty("平均亮度值")
private double cast; private double brightness;
@ApiModelProperty("亮度异常值")
private double da;
@ApiModelProperty("图片的清晰度指标,值越大越清晰") @ApiModelProperty("图片的清晰度指标,值越大越清晰")
private double clarity; private double clarity;
......
...@@ -20,11 +20,11 @@ import lombok.NoArgsConstructor; ...@@ -20,11 +20,11 @@ import lombok.NoArgsConstructor;
@Builder @Builder
public class SettingVO { public class SettingVO {
@ApiModelProperty("亮度值") @ApiModelProperty("亮度值起始范围")
private double cast; private double startValue;
@ApiModelProperty("亮度异常值") @ApiModelProperty("亮度值结束范围")
private double da; private double endValue;
@ApiModelProperty("图片的清晰度指标,值越大越清晰") @ApiModelProperty("图片的清晰度指标,值越大越清晰")
private double clarity; private double clarity;
......
...@@ -18,12 +18,12 @@ spring: ...@@ -18,12 +18,12 @@ spring:
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/tianjin/lib/correct.py
imgconfig: imgconfig:
opencv: D:/project/imgproc/lib/opencv_java460.dll opencv: /opt/tianjin/lib/opencv_java460.so
deskew: C:/Users/11419/Desktop/Deskew/Bin/deskew.exe deskew: /opt/tianjin/lib/Deskew/Bin/deskew
deskewpy: D:/project/imgproc/lib/correct.py deskewpy: /opt/tianjin/lib/correct.py
#imgconfig:
# opencv: D:/project/imgproc/lib/opencv_java460.dll
# deskew: C:/Users/11419/Desktop/Deskew/Bin/deskew.exe
# deskewpy: D:/project/imgproc/lib/correct.py
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment