前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android OpenCV 4.6.0 颜色追踪

Android OpenCV 4.6.0 颜色追踪

作者头像
zinyan.com
发布2022-12-07 17:34:54
1.6K2
发布2022-12-07 17:34:54
举报
文章被收录于专栏:zinyanzinyan

1. 介绍

通过OpenCV实现,实时识别摄像头中的固定颜色块的坐标位置,并进行标注。

简而言之,追踪摄像头中纯色物体的运动轨迹。

我们可以通过OpenCV来识别视频中的纯色物体的移动轨迹。

利用了openCV中的ColorBlobDetector功能。

2. 实现

步骤比较简单:

  1. 获取摄像头拍摄数据,得到ImageProxy 并转为Mat进行计算。
  2. Android 拍摄的图片默认为rgba格式,将该格式转为HSV。
  3. 使用Core.inRange() 将指定颜色范围内的色块从图片中分割出来。
  4. 进行膨胀处理,可以使用morphologyEx 也可以使用dilate。
  5. 针对膨胀完毕的数据,执行轮廓提取。
  6. 遍历轮廓数组得到轮廓面积最大的坐标集合。
  7. 完成

主要步骤为上面这几种。下面,将会介绍如何实现。

2.1 调用摄像头获取Mat

Android CameraX 的初始化就不多赘述了。网上有不少的示例。

我们可以不用预览功能,而单纯使用ImageAnalysis分析功能。

得到ImageProxy对象,然后将ImageProxy对象直接转为Mat对象。

转换方法可以参考:Android ImageProxy 转 OpenCV Mat对象

有完整的介绍。包括图片方向的矫正。

当我们得到Mat后就会开始进行下面的操作了。

PS:本来打算用VideoCapture对象的,但是老是崩溃错误。没办法。

2.2 转HSV

Android拍摄的照片颜色是RGBa格式的。我们需要将该格式转为HSV才能进行下一步。

代码语言:javascript
复制
Mat hsv = new Mat();
Imgproc.cvtColor(mat, hsv, Imgproc.COLOR_RGB2HSV_FULL); //颜色通道转换

将得到的mat对象传进去,然后使用:Imgproc.COLOR_RGB2HSV_FULL选项进行转换。

可以将得到的hsv对象转为Bitmap,扔给ImageView进行显示,下面的每个步骤都可以将得到的Mat进行显示,这样我们可以了解整个转换过程中的效果。

2.3 inRange 色块提取

我们转换完毕的HSV格式的Mat对象,可以直接进行色块提取。示例:

代码语言:javascript
复制
Mat dst = new Mat();
//颜色检查的上限和下限
Core.inRange(hsv, new Scalar(0,140,121), new Scalar(30,255,255), dst);

其中的两个Scalar 是hsv格式的颜色对象。

第一个是开始值,后面的是结束值。然后openCV就会在这两个颜色范围内进行分割。将属于该颜色范围的地方设置为白色。

不属于的就设置为黑色。

而具体里面的参数应该写多少,就根据大家实际需要采集的颜色进行判断了。

给几个示例:

代码语言:javascript
复制
Core.inRange(hsv, Scalar(30, 40, 50), Scalar(40, 255, 255), dsty);  //黄色
Core.inRange(hsv, Scalar(45, 55, 55), Scalar(90, 255, 255), dstg);  //绿色
Core.inRange(hsv, Scalar(0,180,121), Scalar(30, 255, 255), dsto);  //橙色

大家根据自己的实际需求,可以通过HSV颜色卡,设置不同的颜色。

PS:实在没办法,也可以通过openCV的 samples工程中的 color-blob-detection 示例代码。实现点击触摸获取当前图片的HSV颜色值。 ColorBlobDetector 类中,下面的方法可以打印看看结果值。

代码语言:javascript
复制
 public void setHsvColor(Scalar hsvColor) {
        double minH = (hsvColor.val[0] >= mColorRadius.val[0]) ? hsvColor.val[0]-mColorRadius.val[0] : 0;
        double maxH = (hsvColor.val[0]+mColorRadius.val[0] <= 255) ? hsvColor.val[0]+mColorRadius.val[0] : 255;

        mLowerBound.val[0] = minH;
        mUpperBound.val[0] = maxH;

        mLowerBound.val[1] = hsvColor.val[1] - mColorRadius.val[1];
        mUpperBound.val[1] = hsvColor.val[1] + mColorRadius.val[1];

        mLowerBound.val[2] = hsvColor.val[2] - mColorRadius.val[2];
        mUpperBound.val[2] = hsvColor.val[2] + mColorRadius.val[2];

        mLowerBound.val[3] = 0;
        mUpperBound.val[3] = 255;

        Mat spectrumHsv = new Mat(1, (int)(maxH-minH), CvType.CV_8UC3);

        for (int j = 0; j < maxH-minH; j++) {
            byte[] tmp = {(byte)(minH+j), (byte)255, (byte)255};
            spectrumHsv.put(0, j, tmp);
        }
//        System.out.println("颜色值 低值:"+ Arrays.toString(mLowerBound.val));
//        System.out.println("颜色值 高值:"+ Arrays.toString(mUpperBound.val));
        Imgproc.cvtColor(spectrumHsv, mSpectrum, Imgproc.COLOR_HSV2RGB_FULL, 4);
    }

2.4 膨胀

我们执行完毕inRange方法后,就能得到色块了。但是复杂环境下会有一些噪点。我们可以通过膨胀算法进行降噪。

有两种方法:1 通过morphologyEx进行。2 通过dilate方法进行

代码语言:javascript
复制
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5, 5));
Imgproc.morphologyEx(dst, dst, Imgproc.MORPH_OPEN, kernel);//进行开运算

还可以直接通过dilate进行:

代码语言:javascript
复制
Imgproc.dilate(dst, dst, new Mat());

运算完毕之后,得到的dst对象就是处理结果了。

2.5 轮廓提取

当我们得到膨胀结束后的对,就可以进行轮廓提取findContours了。示例:

代码语言:javascript
复制
List<MatOfPoint> contours = new ArrayList<MatOfPoint>(); //存储提取后的轮廓对象集合
Mat hireachy = new Mat();//起到一个提取过程中间转换暂存的作用。
Imgproc.findContours(dst, contours, hireachy, Imgproc.RETR_EXTERNAL,
                Imgproc.CHAIN_APPROX_SIMPLE); //执行轮廓提取

执行完毕后,contours集合中就存储了各种坐标了。

2.6 最大面积区域提取

我们可以根据区域面积进行区分哪些是我们需要的。哪些是误判的。通常面积最大的就应该是我们需要的物体了。

代码语言:javascript
复制
// 查找最大面积
double maxArea = 0;
Iterator<MatOfPoint> each = contours.iterator();
Rect rect = null;
while (each.hasNext()) {
   MatOfPoint wrapper = each.next();
   double area = Imgproc.contourArea(wrapper);
   if (area > maxArea) {
       maxArea = area;
       rect = Imgproc.boundingRect(wrapper);//将该区域转为Rect矩形对象
   }
}

上面示例中,我是将面积最大的数据,转为了矩形对象。

如果不想转矩形对象。那么可以转为MatOfPoint对象。它绘制的时候将会是多边形,示例:

代码语言:javascript
复制
 // 查找最大面积
double maxArea = 0;
Iterator<MatOfPoint> each = contours.iterator();
while (each.hasNext()) {
    MatOfPoint wrapper = each.next();
    double area = Imgproc.contourArea(wrapper);
    if (area > maxArea){
        maxArea = area;
    }   
}

List<MatOfPoint> mContours = new ArrayList<MatOfPoint>();
each = contours.iterator();
while (each.hasNext()) {
        MatOfPoint contour = each.next();
        if (Imgproc.contourArea(contour) > 0.1*maxArea) {
           mContours.add(contour);
        }
}

我们可以将数据过滤,得到MatOfPoint对象,也可以是Rect对象。

下一步,就是绘制该对象了

2.7 绘制提取区域

我们得到的数据可能为空,所以要进行判断,如果不为空,那么就绘制一个红色边框的矩形。边框宽度为2。

代码语言:javascript
复制
//得到最大的对象
if (rect != null) {
   Imgproc.rectangle(mat, rect, new Scalar(255, 0, 0), 2); //在mat中绘制一个矩形
}

我们如果是MatOfPoint对象,绘制方法如下:

代码语言:javascript
复制
Imgproc.drawContours(mat, mContours, -1,  new Scalar(255, 0, 0),2);

2.8 Mat 转 Bitmap

到这一步的时候,就可以将mat转为Bitmap,并给到ImageView进行显示了。OpenCV提供了转换工具:

代码语言:javascript
复制
Bitmap bitmap = Bitmap.createBitmap(mat.width(), mat.height(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(mat, bitmap);
imageView.setImageBitmap(bitmap); //根据自己的ImageView进行替换就可以了。

2.9 release Mat对象

我们在上面的步骤中大量创建和使用了Mat对象,要注意Mat对象的销毁。如果使用完毕后,需要主动调用mat.release()进行释放对象哦,否则会占用资源。

3. 小结

到这里追踪效果就实现了,我们也可以拿到实时的坐标数据进行其他的业务计算了。

整体实现的代码大部分参考openCV SDK中的samples示例代码。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-11-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 zinyan 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 介绍
  • 2. 实现
    • 2.1 调用摄像头获取Mat
      • 2.2 转HSV
        • 2.3 inRange 色块提取
          • 2.4 膨胀
            • 2.5 轮廓提取
              • 2.6 最大面积区域提取
                • 2.7 绘制提取区域
                  • 2.8 Mat 转 Bitmap
                    • 2.9 release Mat对象
                    • 3. 小结
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档