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实现。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用 OpenCV 在 Android 上实现最小外接矩形切割图形的功能。下面是一个简单的代码示例: 首先,确保你的 Android 项目已经集成了 OpenCV。然后,创建一个新的 Java 类,例如 `ImageProcessingUtils`,并在其中添加以下代码: ```java import org.opencv.core.Mat; import org.opencv.core.MatOfPoint2f; import org.opencv.core.Point; import org.opencv.core.Rect; import org.opencv.core.RotatedRect; import org.opencv.core.Size; import org.opencv.imgproc.Imgproc; public class ImageProcessingUtils { public static Mat cropToMinBoundingBox(Mat inputImage) { // 转换为灰度图像 Mat grayImage = new Mat(); Imgproc.cvtColor(inputImage, grayImage, Imgproc.COLOR_BGR2GRAY); // 二值化处理 Mat binaryImage = new Mat(); Imgproc.threshold(grayImage, binaryImage, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU); // 查找轮廓 Mat contours = new Mat(); Mat hierarchy = new Mat(); Imgproc.findContours(binaryImage, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); // 找到最大轮廓 int maxContourIndex = -1; double maxContourArea = 0; for (int i = 0; i < contours.size().height; i++) { double area = Imgproc.contourArea(contours.row(i)); if (area > maxContourArea) { maxContourIndex = i; maxContourArea = area; } } // 获取最小外接矩形 MatOfPoint2f maxContour = new MatOfPoint2f(); contours.get(maxContourIndex).convertTo(maxContour, CvType.CV_32F); RotatedRect minBoundingBox = Imgproc.minAreaRect(maxContour); // 旋转最小外接矩形 Mat rotatedImage = new Mat(); Mat rotationMatrix = Imgproc.getRotationMatrix2D(minBoundingBox.center, minBoundingBox.angle, 1.0); Imgproc.warpAffine(inputImage, rotatedImage, rotationMatrix, inputImage.size(), Imgproc.INTER_CUBIC); // 裁剪图像 Rect boundingRect = minBoundingBox.boundingRect(); Mat croppedImage = new Mat(rotatedImage, boundingRect); return croppedImage; } } ``` 然后,在你的 Android Activity 或 Fragment 中调用这个方法来实现最小外接矩形切割图形的功能。例如: ```java import org.opencv.android.Utils; import org.opencv.core.Mat; import org.opencv.core.CvType; import org.opencv.android.OpenCVLoader; public class MainActivity extends AppCompatActivity implements LoaderCallbackInterface { static { if (!OpenCVLoader.initDebug()) { // Handle initialization error } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 加载图像 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.your_image); Mat inputImage = new Mat(bitmap.getHeight(), bitmap.getWidth(), CvType.CV_8UC3); Utils.bitmapToMat(bitmap, inputImage); // 切割图像 Mat croppedImage = ImageProcessingUtils.cropToMinBoundingBox(inputImage); // 显示切割后的图像 Bitmap croppedBitmap = Bitmap.createBitmap(croppedImage.cols(), croppedImage.rows(), Bitmap.Config.ARGB_8888); Utils.matToBitmap(croppedImage, croppedBitmap); ImageView imageView = findViewById(R.id.image_view); imageView.setImageBitmap(croppedBitmap); } } ``` 这个代码示例使用了 OpenCV 的一些功能来处理图像,包括灰度转换、二值化、轮廓查找和最小外接矩形计算。最后,将切割后的图像显示在一个 ImageView 中。 记得将 `R.drawable.your_image` 替换为你的图像资源 ID,并在你的 Android 项目中添加相应的图像资源文件。 希望这个代码示例能够帮助到你实现最小外接矩形切割图形的功能!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值