栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

基于AI的计算机视觉识别在Java项目中的使用 (二) —— OpenCV的使用

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于AI的计算机视觉识别在Java项目中的使用 (二) —— OpenCV的使用

​先说句题外话。前两天刷到一个做银行系统 40 多岁的 C 语言工程师,因为公司技术转型被裁,失业半年,家里都快揭不开锅了!他现在焦虑得不行,问大家现在学 Java 编程晚不晚?我也不知道晚不晚,就是他那个状态搞得别人也焦虑了。

都说软件行业是青春饭,我觉得对。要想做得长久,就要想办法让青春永驻、容颜不老 ---- 这是误解!我的理解是这个行业不太容易 “混” 到退休,无论你是做管理还是做研发,都需要保持对新技术的敏感和热情,心态要年轻。软件产业相对其他行业卷得比较厉害,如果没点热情,还是不要转什么 Java了,继续做下去其实挺煎熬的,转行也许能远离痛苦。

下面切到正题。计算机视觉领域的应用一般都会以OpenCV为基础。产品里我们使用OpenCV来做图像的读写、色彩变化、图像截取和拼接、图像畸变矫正、轮廓分析,另外还有图像矩阵的均值、标准差的计算和图像矩阵加减乘除运算等。

OpenCV在Java项目中的配置

服务端用的SpringBoot,所以需要在SpringBoot项目里使用OpenCV。OpenCV没有Java版本的实现,但提供了JNI来调用 C 编译出的动态库。缺点是引入麻烦,优点是 C语言对于包含大量矩阵计算的图形操作执行速度很快。

Windows

开发环境用的windows10,首先下载安装windows版本的OpenCV,然后在安装目录 opencvbuildjava 下找到opencv-xxx.jar 和 opencv_javaxxx.dll (xxx是版本号)两个文件。

jar文件可copy到项目的resource目录中,我放到 srcmainresourceslib.opencv目录下。这个jar里面封装了对底层 C 方法调用接口,在Windows和Linux环境下通用。

另外的 opencv_javaxxx.dll 文件需要复制到 C:WindowsSystem32 目录下。要将jar添加到项目依赖的库中,还需要在pom.xml中增加 dependency 配置:

  
    ...
    
    
        org
        opencv
        4.5.3
        system
        ${pom.basedir}/src/main/resources/lib.opencv/opencv-453.jar
    
    ...
  
  ...
Docker(Linux)

部署用的Docker,因为没有找到版本合适的Java+OpenCV的Docker Image,所以只能用一个Java Docker Image作为基础,在基础 Image 上再安装OpenCV。

Linux下安装OpenCV需要用cmake对OpenCV的源码做编译,稍微麻烦一些。索性我绕过这个环节找了一个 sh脚本 搞定(这个脚本里可以设置安装版本等参数)。

另外,将SpringBoot项目打包为Docker Image时, 需要在SpringBoot项目的pom.xml配置文件中增加如下设置,让批处理将指定的 jar 文件打包到的项目依赖的 lib 目录下。

  ...
  
     ...
        
            
                src/main/resources
                .
                
                    ***.jar
                
            
            
                src/main/resources
                BOOT-INF/classes/
                
                    **
String path = "D:\demo.jpg";
Mat img = Imgcodecs.imread(path);


String savePath = "D:\demo_bak.jpg";
Imgcodecs.imwrite(savePath, img);

用 Imgproc 进行简单图像处理

图形灰度化

import org.opencv.imgproc.Imgproc;


Mat grayImg = new Mat();


Imgproc.cvtColor(
  img, grayImg, Imgproc.COLOR_BGR2GRAY
);

Imgcodecs.imwrite(
  "D:\card_test\process\gray.jpg", 
  grayImg);

图形二值化

Mat blurImg = new Mat();

Imgproc.GaussianBlur(
  grayImg, 
  blurImg, 
  new Size(3,3), 2, 2
);


Mat binaryImg = new Mat();

Imgproc.adaptiveThreshold(
  blurImg, 
  binaryImg, 
  255, 
  Imgproc.ADAPTIVE_THRESH_MEAN_C, 
  Imgproc.THRESH_BINARY, 
  45,  
  11
);

Imgcodecs.imwrite(
  "D:\card_test\process\binary.jpg", 
  binaryImg);

Canny边缘检测

Mat cannyImg = new Mat();
Imgproc.Canny(
 binaryImg, 
 cannyImg, 
 20, 
 60, 
 3, 
 false);

Imgcodecs.imwrite(
  "D:\card_test\process\Canny.jpg", 
  cannyImg);

膨胀增强边缘

Mat dilateImg = new Mat();
Imgproc.dilate(
  cannyImg, 
  dilateImg, 
  new Mat(), 
  new Point(-1,-1), 
  3, 1, 
  new Scalar(1));

Imgcodecs.imwrite(
  "D:\card_test\process\dilateImg.jpg", 
  dilateImg);

轮廓查找

List contours = new ArrayList();
Mat hierarchy = new Mat();

Imgproc.findContours(
  binaryImg, 
  contours, 
  hierarchy, 
  Imgproc.RETR_EXTERNAL, 
  Imgproc.CHAIN_APPROX_SIMPLE
);


int maxIdx = 0;
double maxSize = 0;
for (int i = 0; i < contours.size(); i++) {
    double size = Imgproc.contourArea(
      contours.get(i)
    );
    if(maxSize < size) {
        maxIdx = i;
        maxSize = size;
    }
}

MatOfPoint maxContour = contours.get(maxIdx);


Mat imgCopy = img.clone();
Imgproc.drawContours(
  imgCopy, 
  contours, 
  maxIdx, 
  new Scalar(0, 0, 255), 
  4, 
  LINE_8
);


Imgcodecs.imwrite(
  "D:\card_test\process\contour.jpg", 
  imgCopy);

外接矩形

Rect rect = Imgproc.boundingRect(maxContour);


Mat rectImg = img.clone();
Imgproc.rectangle(
        rectImg,
        rect,
        new Scalar(0, 0, 255),
        2,
        Imgproc.LINE_8
);

Imgcodecs.imwrite(
        "D:\card_test\process\rect.jpg",
        rectImg);

综合应用(切取图中的轮廓并矫正为矩形)

切图效果

MatOfInt hull = new MatOfInt();
Imgproc.convexHull(maxContour, hull);


Point[] contourPoints = maxContour.toArray();
int[] indices = hull.toArray();
List newPoints = new ArrayList();
for (int index : indices) {
    newPoints.add(contourPoints[index]);
}
MatOfPoint2f contourHull = new MatOfPoint2f();
contourHull.fromList(newPoints);


double thresholdL = Imgproc.arcLength(contourHull, true) * 0.01;


MatOfPoint2f approx = new MatOfPoint2f();
approx.convertTo(approx, CvType.CV_32F);
Imgproc.approxPolyDP(contourHull, approx, thresholdL, true);
List points = approx.toList();


List lines = new ArrayList();
for (int i = 0; i < points.size(); i++) {
    Point p1 = points.get(i);
    Point p2 = points.get((i + 1) % points.size());
    if (getSpacePointToPoint(p1, p2) > 4 * thresholdL) {
        lines.add(new double[]{p1.x, p1.y, p2.x, p2.y});
    }
}


List corners = new ArrayList();
for (int i = 0; i < lines.size(); i++) {
    Point corner = computeIntersect(
            lines.get(i),
            lines.get((i + 1) % lines.size()));
    corners.add(corner);
}


sortCorners(corners);


Point p0 = corners.get(0);
Point p1 = corners.get(1);
Point p2 = corners.get(2);
Point p3 = corners.get(3);
double imgWidth = getSpacePointToPoint(p0, p1);
double imgHeight = getSpacePointToPoint(p3, p0);


Mat dstMat = Mat.zeros(
        (int) imgHeight,
        (int) imgWidth,
        CvType.CV_8UC3);


MatOfPoint2f src = new MatOfPoint2f(p0, p1, p2, p3);


MatOfPoint2f dst = new MatOfPoint2f(
        new Point(0, 0),
        new Point(imgWidth, 0),
        new Point(imgWidth, imgHeight),
        new Point(0, imgHeight));


Mat trans = Imgproc.getPerspectiveTransform(src, dst);
Imgproc.warpPerspective(img, dstMat, trans, dstMat.size());

Imgcodecs.imwrite(
        "D:\card_test\process\cutMat.jpg",
        dstMat);
上述过程中用到得一些方法实现

计算点到点的距离

public double getSpacePointToPoint(Point p1, 
        Point p2) {
    if (p1 == null || p2 == null) {
        return 0;
    }
    double a = p1.x - p2.x;
    double b = p1.y - p2.y;
    return Math.sqrt(a * a + b * b);
}

计算两直线的交点

public Point computeIntersect(double[] a, double[] b) {
    if (a.length != 4 || b.length != 4)
        throw new ClassFormatError();
    double x1 = a[0], y1 = a[1], x2 = a[2], y2 = a[3], x3 = b[0], y3 = b[1], x4 = b[2], y4 = b[3];
    double d = ((x1 - x2) * (y3 - y4)) - ((y1 - y2) * (x3 - x4));
    if (d != 0) {
        Point pt = new Point();
        pt.x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
        pt.y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
        return pt;
    } else {
        return new Point(-1, -1);
    }
}

对多个点按顺时针排序(右上、左上、左下、右下)

public void sortCorners(List corners) {
    if (corners.size() == 0) return;
    // 首先取得矩形中心点的 x,y值
    int centerX = 0, centerY = 0;
    for (Point point : corners) {
        centerX += point.x;
        centerY += point.y;
    }
    centerX = centerX / 4;
    centerY = centerY / 4;

    
    Point[] result = new Point[4];
    for (Point point : corners) {
        if (point.x < centerX && point.y < centerY) {
            result[3] = point;
        } else if (point.x > centerX && point.y < centerY) {
            result[2] = point;
        } else if (point.x > centerX && point.y > centerY) {
            result[1] = point;
        } else if (point.x < centerX && point.y > centerY) {
            result[0] = point;
        }
    }
    corners.clear();
    for (Point point : result) {
        corners.add(0, point);
    }
}

本期到此为止。《基于AI的计算机视觉识别在Java项目中的使用》专题将按下列章节展开,欢迎关注我的个人公众号和CSDN。

一、《基于AI的计算机视觉识别在Java项目中的使用 —— 背景》

二、《基于AI的计算机视觉识别在Java项目中的使用 —— OpenCV的使用》

三、《基于AI的计算机视觉识别在Java项目中的使用 —— 搭建基于Docker的深度学习训练环境》

四、《基于AI的计算机视觉识别在Java项目中的使用 —— 准备深度学习训练数据》

五、《基于AI的计算机视觉识别在Java项目中的使用 —— 深度模型的训练调优》

六、《基于AI的计算机视觉识别在Java项目中的使用 —— 深度模型在Java环境中的部署》

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/922067.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号