opencv

您所在的位置:网站首页 基本图形的图片 opencv

opencv

2024-02-25 01:50| 来源: 网络整理| 查看: 265

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 BufferedImageToBase64(BufferedImage bufferedImage) throws IOException { Map 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 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 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实现。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3