opencv-java实现基本图形操作

opencv-java实现基本图形操作

这是我图像处理大作业完成后摸出来的第二篇博客,上期已经简单介绍过如何在springboot环境下使用opencv,这期就承接上回简单讲讲Java版opencv的基本操作。

在我的理解中,关于opencv的基本操作差不多就是旋转,水平镜像翻转,放大缩小,以及裁剪绘制等。都是一些没什么技术含量的操作,大都是调用函数即可完成,有过opencv基础的同学肯定可以很快掌握。

但是在讲解之前,有一点需要和大家提前探讨,那就是java版opencv对几个核心库的封装,c版opencv中只需要cv.什么就可以调用各类函数,但是在java中对其中部分函数中了重新封装(大部分没变)。

core中依旧是核心功能模块,尤其是底层数据结构和算法函数,如Mat,Core等。
imgproc,图像处理模块,基本上需要用的图像处理功能都在这个里面。
highgui,高层GUI显示模块,提供了各位图形和媒体接口,常用的imshow就在里面。
imgcodecs,这个库我没具体搞懂什么意思,但是imread封装在里面。
calib3d,相机校准和三维重建相关的内容。
features2d,2D功能框架 ,包含兴趣点检测子、描述子以及兴趣点匹配框架。
ml,机器学习模块。
objdetect,物体检测模块。
video,视频分析组件。

当然以上介绍了很多,实际上重新封装的也只有imgcodecs这个库,其余的貌似和opencv没太多区别,也不排除imgcodecs在新版opencv中就已经存在了,因为我用的是3.x的,至少那个时候没有。对于基本图像处理实际需要的就是最开始介绍了三个库。

由于我是使用SpringBoot单间前后端分离项目,所以我在处理完图片封装为Mat之后还需要将其转化为Base64格式发送给前端的React,之后的代码中会涉及到几个函数【Mat转image再转bufferedimage】,bufferedimage是java自己的图片保存格式,类似于matlab中的变量区,可以保存图片的一些基本信息。这里放的两个转换函数经过验证,可以直接使用,写的很简单,可以根据实际需求微调。

public class Converter {

    public static Map<String,Object> BufferedImageToBase64(BufferedImage bufferedImage) throws IOException {
        Map<String,Object> result = new HashMap<>();
        //将图片转换成字符串给前端
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        ImageIO.write(bufferedImage, "png", stream);

        Base64.Encoder encoder = Base64.getEncoder();
        String base64 = encoder.encodeToString(stream.toByteArray());

        stream.flush();
        stream.close();
        //封装数据

        result.put("image", "data:image/png;base64,"+base64);
        return result;
    }

    public static BufferedImage ImageToBufferedImage(Image image){
        if (image instanceof BufferedImage) {
            return (BufferedImage)image;
        }
        image = new ImageIcon(image).getImage();
        BufferedImage bimage = null;
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        try {
            int transparency = Transparency.OPAQUE;
            GraphicsDevice gs = ge.getDefaultScreenDevice();
            GraphicsConfiguration gc = gs.getDefaultConfiguration();
            bimage = gc.createCompatibleImage(
                    image.getWidth(null), image.getHeight(null), transparency);
        } catch (HeadlessException e) {
        }

        if (bimage == null) {
            int type = BufferedImage.TYPE_INT_RGB;
            bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
        }
        Graphics g = bimage.createGraphics();

        g.drawImage(image, 0, 0, null);
        g.dispose();

        return bimage;
    }
}

图片读取

我的图片读取使用了swing组件调用文件选择器,简单编写的画不需要这么复杂,直接将需要的文件路径传过来就行,至于简单的系统组件,我放到之后的几期中再讲。

@PostMapping("open")
public Map<String,Object> open(HttpServletResponse response) throws IOException {

    response.setContentType("application/octet-stream; charset=utf-8");
    response.setContentType("image/png");

    /*打开文件选择器*/
    FileChooser f = new FileChooser();
    String imgPath = f.getPath();
    System.out.println(imgPath);

    /*转换成BufferedImage*/
    BufferedImage read = ImageIO.read(new FileInputStream(imgPath));

    /*存一份到OpenCV里面*/
    MatImage.matImage=imread(imgPath);
    History.AllMethod(MatImage.matImage);
    System.out.println(MatImage.matImage);

    /*封装base64转给前端*/
    return Converter.BufferedImageToBase64(read);
}

上面的代码是我controller里面具体的写法,因为需要发送给前端显示所以读了两份,其实最好的写法是Mat读第一次,读好了之后直接存到后端里面做备份(这样就不用前端往后端发送需要操作的图片了,直接后端操作好了让前端显示就行)。备份好了直接讲Mat转base64发过去,我上面的写法臃肿了。

如果是直接想要看读取和显示的话一下这种写法就够了

Mat image = Imgcodecs.imread("D:/test.bmp");
HighGui.imshow("yt",image);
HighGui.waitKey(13);

imshow是单独开个小窗口,如果是web项目,建议参考上面的话修改第一段代码。


旋转

旋转任意角度

/**
    * 逆时针旋转,但是图片宽高不用变,此方法与rotateLeft和rotateRight不兼容
    *
    * @param src    源
    * @param angele 旋转的角度
    * @return 旋转后的对象
    */
public static Mat rotate(Mat src, double angele) {
    Mat dst = src.clone();
    org.opencv.core.Point center = new org.opencv.core.Point(src.width() / 2.0, src.height() / 2.0);
    Mat affineTrans = Imgproc.getRotationMatrix2D(center, angele, 1.0);
    Imgproc.warpAffine(src, dst, affineTrans, dst.size(), Imgproc.INTER_NEAREST);
    return dst;
}

旋转90°,垂直、水平翻转

@RequestMapping("spin")
public Map<String,Object> spin(HttpServletResponse response,
                                @RequestParam("key") String key) throws IOException {

    response.setContentType("application/octet-stream; charset=utf-8");
    response.setContentType("image/png");

    /*从constants中取Mat*/
    Mat mat = MatImage.matImage;
    Mat temp = new Mat();
    Mat result = new Mat();

    switch (key) {
        case "0": {

            /*顺时针90*/
            Core.transpose(mat, temp);
            Core.flip(temp, result, 1);

            /*更改存储的图片*/
            MatImage.matImage = result;
            History.AllMethod(MatImage.matImage);

            Image image = toBufferedImage(result);
            BufferedImage bufferedImage = Converter.ImageToBufferedImage(image);
            return Converter.BufferedImageToBase64(bufferedImage);
        }
        case "1": {

            /*逆时针90*/
            Core.transpose(mat, temp);
            Core.flip(temp, result, 0);

            /*更改存储的图片*/
            MatImage.matImage = result;
            History.AllMethod(MatImage.matImage);

            Image image = toBufferedImage(result);
            BufferedImage bufferedImage = Converter.ImageToBufferedImage(image);
            return Converter.BufferedImageToBase64(bufferedImage);
        }
        case "2": {

            /*转180*/

            Core.flip(mat, result, -1);
            /*更改存储的图片*/
            MatImage.matImage = result;
            History.AllMethod(MatImage.matImage);

            Image image = toBufferedImage(result);
            BufferedImage bufferedImage = Converter.ImageToBufferedImage(image);
            return Converter.BufferedImageToBase64(bufferedImage);
        }
        case "3":{

            /*水平翻转*/
            Core.flip(mat,result,1);

            MatImage.matImage=result;
            History.AllMethod(MatImage.matImage);

            Image image = toBufferedImage(result);
            BufferedImage bufferedImage = Converter.ImageToBufferedImage(image);
            return Converter.BufferedImageToBase64(bufferedImage);

        }
        case "4":{
            /*垂直翻转*/
            Core.flip(mat,result,0);

            MatImage.matImage=result;
            History.AllMethod(MatImage.matImage);

            Image image = toBufferedImage(result);
            BufferedImage bufferedImage = Converter.ImageToBufferedImage(image);
            return Converter.BufferedImageToBase64(bufferedImage);
        }
        default:
            return null;
    }
}

上面是springboot中的写法,翻译成普通maven也很好理解,随便讲一个case里面的内容扒出来,将Core.flip后面的全删掉加imshow和waitkey即可。

因为实际需要的就是Core中的flip,接受的参数为mat(需要操作图片),res(输出图片),type(操作方法)。当第三个参数type为0的时候顺时针90°,1的时候逆时针90°。再配合transpose旋转,很容易理解道理做了什么操作。


裁剪

Rect rect = new Rect(x,y,ex-x,ey-y);
Mat result = new Mat(mat,rect);

Rect是core中封装的内容,表示矩形框,这里输入的是左上角第一个点坐标和width,height。传入Mat之后就可以实现裁剪,其实就是重新定义了一个对象,但是限定其范围了。

说到裁剪,就不得不说到前端技术,这里也是我参考了很多文献和教程整出的手动裁剪框,基于React和js实现。

你可能感兴趣的:(opencv-java,java,opencv,java,人工智能)