前言
java+opencv实现图像矫正,并解决中文文件名报错问题,同时兼容windows、linux两个环境
一、实现效果
矫正前效果图:
矫正后效果:
二、前期准备
需要引入对应环境#的.dll(windows)与.so(linux)文件,将文件下载到本地,拷贝文件路径替换代码中对应的路径,下载文件资源地址(包含一张测试图片):https://pan.baidu.com/s/1O9WKeD9KU6qEYLYSvbfBjg
提取码:38uv
二、代码部分
1.引入库
直接引入javacv依赖会很臃肿,精简一下只引入opencv部分即可;如需要javacv其他依赖包,根据实际情况引入。
pom依赖:
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.5.1-1.5.5</version>
</dependency>
2.代码
图像矫正功能代码组成为工具类(RectifyImageUtil)+入参对象(Image)
Image代码如下:
import lombok.Data;
import java.io.Serializable;
/**
* 图像信息
* @Date 2023/3/24 9:42
*/
@Data
public class Image implements Serializable {
/**
* 写入图片路径
*/
private String imgUrlInput;
/**
* 输出图片路径(如果为空,将覆盖写入图片路径)
*/
private String imgUrlOut;
/**
* 左上x
*/
private float ltX;
/**
* 左上y
*/
private float ltY;
/**
* 右上x
*/
private float rtX;
/**
* 右上y
*/
private float rtY;
/**
* 左下x
*/
private float lbX;
/**
* 左下y
*/
private float lbY;
/**
* 右下x
*/
private float rbX;
/**
* 右下y
*/
private float rbY;
/**
* 图像宽
*/
private float imgWidth;
/**
* 图像高
*/
private float imgHeight;
}
RectifyImageUtil代码如下:
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import javax.imageio.ImageIO;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.DataBufferByte;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
/**
* @author lqt
* @date 2023/3/24 9:48
* @version 1.0
*/
@Slf4j
public class RectifyImageUtil {
// 根据服务器环境动态加载依赖 (只支持windows与linux)
static {
String os = System.getProperty("os.name");
log.info("当前环境" + os);
if (os != null && os.toLowerCase().startsWith("windows")) {
//Windows操作系统
// todo 替换本地 .dll文件路径 (例如:D:\\data\\windows\\x64\\opencv_java460.dll)
System.load("(.dll文件本地路径)\\opencv_java460.dll");
} else if (os != null && os.toLowerCase().startsWith("linux")) {
//Linux操作系统
// todo 部署服务器修改.so文件路径 (例如:/data/so/libopencv_java460.so)
System.load("(.so文件服务器路径)/libopencv_java460.so");
}
}
public static void main(String[] args) {
Image img = new Image();
img.setImgUrlInput("(测试图片路径,可以使用网片链接中的图片测试)\\qiaoba_副本.png");
img.setImgUrlOut("(输出图片路径)\\qiaoba_中文2.png");
// 以下数值是网盘链接图片的测试数据,如果有自己图片和数据直接替换
img.setLtX(71.5f);
img.setLtY(112);
img.setRtX(298.5f);
img.setRtY(113);
img.setLbX(53.5f);
img.setLbY(573);
img.setRbX(306.5f);
img.setRbY(624);
img.setImgWidth(600);
img.setImgHeight(800);
boolean flag = rectifyImage(img);
log.info("操作:" + flag);
}
/**
* 图像矫正
* @date 2023/3/24 10:10
* @param img 矫正信息
* @return boolean
* @author lqt
*/
public static boolean rectifyImage(Image img){
try {
if(StringUtils.isEmpty(img.getImgUrlInput())){
return false;
}
File file = new File(img.getImgUrlInput());
if(!file.exists()){
return false;
}
Mat src = imRead(img.getImgUrlInput());
Mat dst = new Mat();
// 输入图像
Point[] pt1 = new Point[4];
pt1[0]=new Point(img.getLtX(),img.getLtY());
pt1[1]=new Point(img.getRtX(),img.getRtY());
pt1[2]=new Point(img.getLbX(),img.getLbY());
pt1[3]=new Point(img.getRbX(),img.getRbY());
//输出图像
Point[] pt2 = new Point[4];
pt2[0]=new Point(0,0);
pt2[1]=new Point(img.getImgWidth(),0);
pt2[2]=new Point(0,img.getImgHeight());
pt2[3]=new Point(img.getImgWidth(),img.getImgHeight());
MatOfPoint2f mop1 = new MatOfPoint2f(pt1);
MatOfPoint2f mop2 = new MatOfPoint2f(pt2);
Mat perspectiveMat = Imgproc.getPerspectiveTransform(mop1, mop2);
Imgproc.warpPerspective(src,dst,perspectiveMat,new Size(img.getImgWidth(),img.getImgHeight()));
// 输出图像路径为空,覆盖输入图像
imWrite(dst,StringUtils.isEmpty(img.getImgUrlOut()) ? img.getImgUrlInput() : img.getImgUrlOut());
}catch (Exception e){
log.info("图像矫正异常:", e);
return false;
}
return true;
}
/**
* 读取图像
* @param filePath 文件路径,可以包含中文
* @return Mat
* @author lqt
*/
public static Mat imRead(String filePath){
Mat mat = null;
try {
//使用java2D读取图像
BufferedImage image = ImageIO.read(new FileInputStream(filePath));
BufferedImage convertToRgbFormatImage = convertToRgbFormat(image);
byte[] data = ((DataBufferByte) convertToRgbFormatImage.getRaster().getDataBuffer()).getData();
mat = new Mat(image.getHeight(), convertToRgbFormatImage.getWidth(), CvType.CV_8UC3);
mat.put(0, 0, data);
// 小型图片可以输出 查看下
// HighGui.imshow("book",src);
// HighGui.waitKey(0);
} catch (Exception e) {
log.error("读取图像出现异常:", e);
}
return mat;
}
/**
* BufferedImage均转为TYPE_3BYTE_BGR,RGB格式
*
* @param input 未知格式BufferedImage图片
* @return TYPE_3BYTE_BGR格式的BufferedImage图片
* @author lqt
*/
public static BufferedImage convertToRgbFormat(BufferedImage input) {
if (BufferedImage.TYPE_3BYTE_BGR != input.getType()){
BufferedImage inputRgb = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_sRGB), null).filter(input, inputRgb);
return inputRgb;
} else{
return input;
}
}
/**
* 保存mat到指定路径
* @param mat 要保存的Mat
* @param filePath 保存路径
* @author lqt
*/
public static void imWrite(Mat mat, String filePath){
try {
File file = new File(filePath);
String fileName = file.getName();
String fileType = "";
if(fileName.contains(".")){
fileType = fileName.substring(fileName.lastIndexOf("."));
}
// 将mat转为java的BufferedImage
MatOfByte mob = new MatOfByte();
Imgcodecs.imencode(fileType, mat, mob);
BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(mob.toArray()));
String type = fileType.substring(fileType.lastIndexOf(".") + 1).toUpperCase();
ImageIO.write(bufferedImage, type, new File(filePath));
} catch (Exception e) {
log.error("保存文件出现异常:", e);
}
}
}