根据图像识别二维码角度
- pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.tykj</groupId>
<artifactId>opencv</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>identification</name>
<description>identification</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.zxing/core -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.1</version>
</dependency>
<!-- 如果是 web 应用添加-->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
</configuration>
</plugin>
</plugins>
</build>
</project>
- common
public class OpenCVCommon {
public static String filePath = "E:\\java\\idea\\data\\spring\\boot\\QRCode\\code\\4.jpg";
}
- controller
@RestController
@RequestMapping("/opencv")
public class OpenCVController {
@Autowired
private QRCodeToolsUtils qrcodeTools;
@RequestMapping("/mat")
public void mat(){
Mat src = Imgcodecs.imread(OpenCVCommon.filePath ,1);
OpenCVUtil.imgCorrection(src);
}
@RequestMapping("/direction")
public void direction(){
Point[] points = new OpenCVQrCodePosition().getPosition(OpenCVCommon.filePath);
if(points == null) {
System.out.println("可能不包含二维码:"+OpenCVCommon.filePath);
return;
}
for (int i = 0; i < points.length; i++) {
Point p = points[i];
System.out.println("点" + i + ": " + "(" + p.x + "," + p.y + ")");
}
String qrCodeText = OpenCVQrCodePosition.decodeQRcode(OpenCVCommon.filePath);
System.out.println("二维码内容:"+ qrCodeText);
}
@RequestMapping("/position")
public Integer position(){
Integer encode = qrcodeTools.deEncodeByPath("E:\\java\\idea\\data\\spring\\boot\\QRCode\\code\\30.jpg");
return encode;
}
}
- utils
public class BufferedImageLuminanceSource extends LuminanceSource {
private final BufferedImage image;
private final int left;
private final int top;
public BufferedImageLuminanceSource(BufferedImage image) {
this(image, 0, 0, image.getWidth(), image.getHeight());
}
public BufferedImageLuminanceSource(BufferedImage image, int left, int top, int width, int height) {
super(width, height);
int sourceWidth = image.getWidth();
int sourceHeight = image.getHeight();
if (left + width > sourceWidth || top + height > sourceHeight) {
throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
}
for (int y = top; y < top + height; y++) {
for (int x = left; x < left + width; x++) {
if ((image.getRGB(x, y) & 0xFF000000) == 0) {
image.setRGB(x, y, 0xFFFFFFFF);
}
}
}
this.image = new BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE_BYTE_GRAY);
this.image.getGraphics().drawImage(image, 0, 0, null);
this.left = left;
this.top = top;
}
public byte[] getRow(int y, byte[] row) {
if (y < 0 || y >= getHeight()) {
throw new IllegalArgumentException("Requested row is outside the image: " + y);
}
int width = getWidth();
if (row == null || row.length < width) {
row = new byte[width];
}
image.getRaster().getDataElements(left, top + y, width, 1, row);
return row;
}
public byte[] getMatrix() {
int width = getWidth();
int height = getHeight();
int area = width * height;
byte[] matrix = new byte[area];
image.getRaster().getDataElements(left, top, width, height, matrix);
return matrix;
}
public boolean isCropSupported() {
return true;
}
public LuminanceSource crop(int left, int top, int width, int height) {
return new BufferedImageLuminanceSource(image, this.left + left, this.top + top, width, height);
}
public boolean isRotateSupported() {
return true;
}
public LuminanceSource rotateCounterClockwise() {
int sourceWidth = image.getWidth();
int sourceHeight = image.getHeight();
AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, sourceWidth);
BufferedImage rotatedImage = new BufferedImage(sourceHeight, sourceWidth, BufferedImage.TYPE_BYTE_GRAY);
Graphics2D g = rotatedImage.createGraphics();
g.drawImage(image, transform, null);
g.dispose();
int width = getWidth();
return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), getHeight(), width);
}
}
public class OpenCVQrCodePosition {
private boolean isDebug = true;
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
public Point[] getPosition(String imgUrl){
Mat src = Imgcodecs.imread(imgUrl ,1);
Mat src_gray = new Mat();
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Imgproc.cvtColor(src ,src_gray ,Imgproc.COLOR_RGB2GRAY);
Imgproc.GaussianBlur(src_gray, src_gray, new Size(3,3), 0);
Imgproc.Canny(src_gray,src_gray,112,255);
Mat hierarchy = new Mat();
Imgproc.findContours(src_gray ,contours ,hierarchy ,Imgproc.RETR_TREE ,Imgproc.CHAIN_APPROX_NONE);
List<MatOfPoint> markContours = this.markContour(src_gray, contours, hierarchy);
markContours = this.filterByAngle(markContours);
Point[] points = this.centerCals(markContours);
if(points != null){
this.cutRect(src, points, 12);
}
return points;
}
public BufferedImage cutRect(Mat src,Point[] points, int offset){
if(points == null || points.length < 3){
System.out.println("-------没有点---------");
return null;
}
int left_x = (int)Math.min(points[0].x, Math.min(points[1].x, points[2].x)) - offset;
int right_x = (int)Math.max(points[0].x, Math.max(points[1].x, points[2].x)) + offset;
int y = (int)Math.min(points[0].y, Math.min(points[1].y, points[2].y)) - offset;
Rect roiArea = new Rect(left_x, y, right_x - left_x, right_x - left_x);
Mat dstRoi = new Mat(src, roiArea);
this.printImg("cutRect.jpg", dstRoi);
return this.mat2BufImg(dstRoi, ".png");
}
private List<MatOfPoint> filterByAngle(List<MatOfPoint> markContours) {
if (markContours.size() < 3){
return null;
}else{
for (int i=0; i<markContours.size()-2; i++){
List<MatOfPoint> threePointList = new ArrayList<>();
for (int j=i+1;j<markContours.size()-1; j++){
for (int k=j+1;k<markContours.size();k++){
threePointList.add(markContours.get(i));
threePointList.add(markContours.get(j));
threePointList.add(markContours.get(k));
if(capture(threePointList)){
return threePointList;
}else{
threePointList.clear();
}
}
}
}
}
return null;
}
private boolean capture(List<MatOfPoint> contours){
Point[] pointthree = this.centerCals(contours);
double angle1 = this.angle(pointthree[1], pointthree[0], pointthree[2]);
double angle2 = this.angle(pointthree[0], pointthree[1], pointthree[2]);
double angle3 = this.angle(pointthree[1], pointthree[2], pointthree[0]);
System.out.println("angle1:"+angle1+",angle2:"+angle2+",angle3:"+angle3);
if (Double.isNaN(angle1) || Double.isNaN(angle2) || Double.isNaN(angle3)){
return false;
}
double maxAngle = Math.max(angle3,Math.max(angle1,angle2));
double minAngle = Math.min(angle3,Math.min(angle1,angle2));
if (maxAngle<85 || maxAngle>95 || minAngle < 40 || minAngle > 50){
return false;
}
return true;
}
private double angle(Point p1, Point p2, Point p3){
double[] ca = new double[2];
double[] cb = new double[2];
ca[0] = p1.x - p2.x;
ca[1] = p1.y - p2.y;
cb[0] = p3.x - p2.x;
cb[1] = p3.y - p2.y;
return 180/3.1415*Math.acos((ca[0]*cb[0]+ca[1]*cb[1])/(Math.sqrt(ca[0]*ca[0]+ca[1]*ca[1])*Math.sqrt(cb[0]*cb[0]+cb[1]*cb[1])));
}
private Point[] centerCals(List<MatOfPoint> matOfPoint){
if(matOfPoint == null || matOfPoint.size() == 0){
return null;
}
Point[] pointthree = new Point[matOfPoint.size()];
for(int i=0; i<matOfPoint.size(); i++){
pointthree[i] = centerCal(matOfPoint.get(i));
}
return pointthree;
}
private Point centerCal(MatOfPoint matOfPoint){
double centerx=0,centery=0;
MatOfPoint2f mat2f = new MatOfPoint2f( matOfPoint.toArray() );
RotatedRect rect = Imgproc.minAreaRect( mat2f );
Point vertices[] = new Point[4];
rect.points(vertices);
centerx = ((vertices[0].x + vertices[1].x)/2 + (vertices[2].x + vertices[3].x)/2)/2;
centery = ((vertices[0].y + vertices[1].y)/2 + (vertices[2].y + vertices[3].y)/2)/2;
Point point= new Point(centerx,centery);
return point;
}
private List<MatOfPoint> markContour(Mat src_gray, List<MatOfPoint> contours, Mat hierarchy) {
List<MatOfPoint> markContours = new ArrayList<>();
for (int i = 0; i< contours.size(); i++ ) {
MatOfPoint2f newMtx = new MatOfPoint2f(contours.get(i).toArray() );
RotatedRect rotRect = Imgproc.minAreaRect( newMtx );
double w = rotRect.size.width;
double h = rotRect.size.height;
double rate = Math.max(w, h)/Math.min(w, h) ;
if (rate < 1.3 && w < src_gray.cols()/4 && h< src_gray.rows()/4 && Imgproc.contourArea(contours.get(i))>60) {
double[] ds = hierarchy.get(0, i);
if (ds != null && ds.length>3){
int count =0;
if (ds[3] == -1){
continue;
}
while ((int) ds[2] !=-1){
++count;
ds = hierarchy.get(0 ,(int) ds[2]);
}
if (count >= 4){
markContours.add(contours.get(i));
}
}
}
}
return markContours;
}
private void printImg(String name, Mat img){
if(isDebug) {
String path = String.format("%s/%s", System.getProperty("user.dir"), name);
System.out.println("out:" + path);
Imgcodecs.imwrite(path, img);
}
}
public static BufferedImage mat2BufImg(Mat matrix, String fileExtension) {
MatOfByte mob = new MatOfByte();
Imgcodecs.imencode(fileExtension, matrix, mob);
byte[] byteArray = mob.toArray();
BufferedImage bufImage = null;
try {
InputStream in = new ByteArrayInputStream(byteArray);
bufImage = ImageIO.read(in);
} catch (Exception e) {
e.printStackTrace();
}
return bufImage;
}
public static Mat bufImg2Mat (BufferedImage original, int imgType, int matType) {
if (original == null) {
throw new IllegalArgumentException("original == null");
}
if (original.getType() != imgType) {
BufferedImage image = new BufferedImage(original.getWidth(), original.getHeight(), imgType);
Graphics2D g = image.createGraphics();
try {
g.setComposite(AlphaComposite.Src);
g.drawImage(original, 0, 0, null);
} finally {
g.dispose();
}
}
DataBufferByte dbi = (DataBufferByte) original.getRaster().getDataBuffer();
byte[] pixels = dbi.getData();
Mat mat = Mat.eye(original.getHeight(), original.getWidth(), matType);
mat.put(0, 0, pixels);
return mat;
}
public static String decodeQRcode(String qrCodePath){
String qrCodeText = null;
try {
BufferedImage image = ImageIO.read(new File(qrCodePath));
LuminanceSource source = new BufferedImageLuminanceSource(image);
Binarizer binarizer = new HybridBinarizer(source);
BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);
Map<DecodeHintType, Object> hints = new HashMap();
hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
Result result = new MultiFormatReader().decode(binaryBitmap, hints);
qrCodeText = result.getText();
} catch (Exception e) {
qrCodeText = new QRCodeDetector().detectAndDecode(Imgcodecs.imread(qrCodePath, 1));
}
return qrCodeText;
}
}
@Component
public class OpenCVUtil {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
public static Mat imgCorrection(Mat srcImage) {
Mat binary = ImgBinarization(srcImage);
Mat preprocess = preprocess(binary);
List<RotatedRect> rects = findTextRegion(preprocess) ;
Mat correction = correction(rects,srcImage);
return correction;
}
public static Mat correction(List<RotatedRect> rects,Mat srcImage) {
double degree = 0;
double degreeCount = 0;
for(int i = 0; i < rects.size();i++){
if(rects.get(i).angle >= -90 && rects.get(i).angle < -45){
degree = rects.get(i).angle;
if(rects.get(i).angle != 0){
degree += 90;
}
}
if(rects.get(i).angle > -45 && rects.get(i).angle <= 0){
degree = rects.get(i).angle;
}
if(rects.get(i).angle <= 90 && rects.get(i).angle > 45){
degree = rects.get(i).angle;
if(rects.get(i).angle != 0){
degree -= 90;
}
}
if(rects.get(i).angle < 45 && rects.get(i).angle >= 0){
degree = rects.get(i).angle;
}
if(degree > -5 && degree < 5){
degreeCount += degree;
}
}
if(degreeCount != 0){
degree = degreeCount/rects.size();
}
Point center = new Point(srcImage.cols() / 2, srcImage.rows() / 2);
Mat rotm = Imgproc.getRotationMatrix2D(center, degree, 1.0);
Mat dst = new Mat();
Imgproc.warpAffine(srcImage, dst, rotm, srcImage.size(), Imgproc.INTER_LINEAR, 0, new Scalar(255, 255, 255));
return dst;
}
public static Mat preprocess(Mat binary){
Mat element1 = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(20, 4));
Mat element2 = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(1, 1));
Mat dilate1 = new Mat();
Imgproc.dilate(binary, dilate1, element2);
Mat erode1 = new Mat();
Imgproc.erode(dilate1, erode1, element1);
Mat dilate2 = new Mat();
Imgproc.dilate(erode1, dilate2, element2);
return dilate2;
}
public static Mat ImgBinarization(Mat srcImage){
Mat gray_image = null;
try {
gray_image = new Mat(srcImage.height(), srcImage.width(), CvType.CV_8UC1);
Imgproc.cvtColor(srcImage,gray_image,Imgproc.COLOR_RGB2GRAY);
} catch (Exception e) {
gray_image = srcImage.clone();
gray_image.convertTo(gray_image, CvType.CV_8UC1);
System.out.println("原文异常,已处理...");
}
Mat thresh_image = new Mat(srcImage.height(), srcImage.width(), CvType.CV_8UC1);
Imgproc.threshold(gray_image, thresh_image,100, 255, Imgproc.THRESH_BINARY);
return thresh_image;
}
public static List<RotatedRect> findTextRegion(Mat img)
{
List<RotatedRect> rects = new ArrayList();
List<MatOfPoint> contours = new ArrayList();
Mat hierarchy = new Mat();
Imgproc.findContours(img, contours, hierarchy, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_SIMPLE, new Point(0, 0));
int img_width = img.width();
int img_height = img.height();
int size = contours.size();
for (int i = 0; i < size; i++){
double area = Imgproc.contourArea(contours.get(i));
if (area < 1000)
continue;
double epsilon = 0.001*Imgproc.arcLength(new MatOfPoint2f(contours.get(i).toArray()), true);
MatOfPoint2f approxCurve = new MatOfPoint2f();
Imgproc.approxPolyDP(new MatOfPoint2f(contours.get(i).toArray()), approxCurve, epsilon, true);
RotatedRect rect = Imgproc.minAreaRect(new MatOfPoint2f(contours.get(i).toArray()));
int m_width = rect.boundingRect().width;
int m_height = rect.boundingRect().height;
rects.add(rect);
}
System.out.println(rects);
return rects;
}
}
@Slf4j
@Component
public class QRCodeToolsUtils {
public Integer deEncodeByPath(String filePath) {
try {
BufferedImage readImage = ImageIO.read(new File(filePath));
LuminanceSource source = new BufferedImageLuminanceSource(readImage);
Binarizer binarizer = new HybridBinarizer(source);
BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);
Map<DecodeHintType, Object> hints = new HashMap<DecodeHintType, Object>();
hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
Result result = null;
result = new MultiFormatReader().decode(binaryBitmap, hints);
ResultPoint[] resultPoint = result.getResultPoints();
float point1X = resultPoint[0].getX();
float point1Y = resultPoint[0].getY();
float point2X = resultPoint[1].getX();
float point2Y = resultPoint[1].getY();
float point3X = resultPoint[2].getX();
float point3Y = resultPoint[2].getY();
float point4X = resultPoint[3].getX();
float point4Y = resultPoint[3].getY();
System.out.println(point1X+"-"+point1Y+"-"+point2X+"-"+point2Y+"-"+point3X+"-"+point3Y+"-"+point4X+"-"+point4Y);
if(point1X > 150 && point1Y > 150){
return 1;
}
if(point1X > 150 && point1Y < 150){
return 2;
}
if(point1X < 150 && point1Y < 150){
return 3;
}
if(point1X < 150 && point1Y > 150){
return 4;
}
}
catch (IOException e) {
log.error("资源读取失败" + e.getMessage());
e.printStackTrace();
}
catch (NotFoundException e) {
log.error("读取图片二维码坐标前发生异常:" + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}
@SpringBootApplication
public class IdentificationApplication {
public static void main(String[] args) {
SpringApplication.run(IdentificationApplication.class, args);
}
}
- lib