OpenCV:检测颜色并在颜色上绘制线

时间:2017-08-02 08:49:38

标签: android c++ opencv

我的应用程序将计算电池的反弹高度,我打算使用蓝色条带来定义" base"我在其中用来计算它离电池的像素数。

如何检测蓝色并在蓝色纸条的底部画一条线,以便绘制的线可用于像素距离计算?

我知道opencv有一个斑点检测应用程序可以围绕选择的颜色绘制轮廓,但我需要的是自动检测颜色和颜色的应用程序。给我它的协调,以便我可以申请

canvas.drawLine(0, 0, 20, 20, p);

画线

注意:检测&线条图是在从视频中提取的位图图像上完成的。

enter image description here

修改 当我测试它时,它没有检测到蓝色。我甚至在有蓝色和绿色纸的照片上进行了测试,但输出dint检测到蓝色......

这是我的照片: Outputoutput Inputinput 这是我目前的代码:

Mat hsvMat = new Mat();
            //Mat black_hue_range = new Mat();
            //Core.inRange(hsvMat, new Scalar(0, 0, 0), new Scalar(180, 255, 30, 0), black_hue_range);
            Mat blue_hue = new Mat();
            Scalar lower_blue = new Scalar(110,50,50);
            Scalar upper_blue = new Scalar(130,255,255);

            //Convert BGR to HSV
            Imgproc.cvtColor(srcMat, hsvMat, Imgproc.COLOR_BGR2HSV);

            //Threshold the HSV image to get only blue colors
            Core.inRange(hsvMat, lower_blue, upper_blue, blue_hue); // hue == a colour or shade
            Mat tempMat22 = new Mat();
            Core.bitwise_and(hsvMat,hsvMat,tempMat22,blue_hue);
            Utils.matToBitmap(tempMat22, b);

            //Bitmap mutableBitmap = b.copy(Bitmap.Config.ARGB_8888, true);
            imgR.setImageBitmap(b);

修改

以下代码返回了三个值,我假设为H = data[0], S data[1], V = data[2] 既然我有HSV值我怎么得到上限和下限? Alexander Reynolds here给出的答案似乎是RGB而不是HSV。 注意:现在读取的彩色像素为绿色,不再是蓝色。

  

E /数据:H:90.0 S:113.0 V:144.0

if (getIntent().hasExtra("byteArray")) {

            bitmap = BitmapFactory.decodeByteArray(getIntent().getByteArrayExtra("byteArray"), 0, getIntent().getByteArrayExtra("byteArray").length);

            int width= bitmap.getWidth();
            int height=bitmap.getHeight();

            int centerX=width/2;
            int centerY=height/2;
            srcMat = new Mat();
            Utils.bitmapToMat(bitmap, srcMat);
            Imgproc.cvtColor(srcMat, srcMat, Imgproc.COLOR_BGR2HSV);
            srcMat.convertTo(srcMat, CvType.CV_64FC3); //http://answers.opencv.org/question/14961/using-get-and-put-to-access-pixel-values-in-java/
            double[] data = srcMat.get(centerX, centerY);
            Log.e("data", String.valueOf("H:"+data[0]+" S:"+data[1]+" V:"+data[2]));
            Log.e("dlength", String.valueOf(data.length));
            Mat matHSV = new Mat(0,0,CvType.CV_64FC3);

另外,通过添加以下三行代码,我会收到一个错误,说位图== null,所以我不确定像素读数是否有效。

matHSV.put(0,0,data);
Utils.matToBitmap(matHSV, bb);
imgDisplay.setImageBitmap(bb);

EDIT2:

尝试使用Rect指定roi时出错:

  

引起:CvException [org.opencv.core.CvException:cv :: Exception:/build/master_pack-android/opencv/modules/core/src/matrix.cpp:483:错误:(-215)0&lt ; = _rowRange.start&& _rowRange.start< = _rowRange.end&& _rowRange.end< = m.rows in function cv :: Mat :: Mat(const cv :: Mat&,const cv :: Range&,const cv :: Range&)

bitmap = globals.getBmp();
        Mat srcMat = new Mat();
        Utils.bitmapToMat(bitmap, srcMat);

        Mat hsvMat = new Mat();
        Imgproc.cvtColor(srcMat,hsvMat,Imgproc.COLOR_BGR2HSV);

    Mat roiMat;
            Rect rectangle = new Rect(177,1571,822,1680);// 177,1571(top right corner),   820,1680 (btm right) 820, 1565(topright)
            roiMat = new Mat(hsvMat,rectangle);
            Utils.matToBitmap(roiMat, temp);

            ImageView imageView = (ImageView) findViewById(R.id.imageView);
            imageView.setImageBitmap(temp);

我也尝试过使用Range

 Range rowRange = new Range(177, 822);
        Range colRange = new Range(1571, 1680);
        roiMat = new Mat(hsvMat, rowRange, colRange); // public Mat(Mat m, Range rowRange, Range colRange)

EDIT2.5:

变化:

roiMat = new Mat(hsvMat, rowRange, colRange);

为:

roiMat = new Mat(hsvMat, colRange, rowRange); 

似乎已经解决了这个问题,但现在它已经说了我的bmp

  

java.lang.IllegalArgumentException:bmp == null

编辑3: 最后设法转换Alexander Reynolds回答的python代码, 但我似乎无法查看结果,因为我收到了错误:

  

java.lang.IllegalArgumentException:bmp == null

Utils.matToBitmap(idk,temp);

bitmap = cn.getBmp();
    Mat srcMat = new Mat();
    Utils.bitmapToMat(bitmap, srcMat);

    Mat hsvMat = new Mat();
    Imgproc.cvtColor(srcMat,hsvMat,Imgproc.COLOR_BGR2HSV);

    Mat roiMat;
    Rect rectangle = new Rect(177,1571,822,1680);// 177,1571(top right corner),   820,1680 (btm right) 820, 1565(topright)
    Range rowRange = new Range(177, 822);
    Range colRange = new Range(1571, 1680);
    roiMat = new Mat(hsvMat, colRange, rowRange); // public Mat(Mat m, Range rowRange, Range colRange)

    MatOfDouble mu = new MatOfDouble();
    MatOfDouble sig = new MatOfDouble();

    Core.meanStdDev(roiMat,mu,sig);


    double m = mu.get(0,0)[0];
    double d = sig.get(0,0)[0];
    int a = 9;
    Log.e("m , d", "m "+String.valueOf(m)+" d"+String.valueOf(d));
    Mat blue_mask = new Mat();
    Core.inRange(hsvMat, new Scalar(m-a*d), new Scalar(m+a*d), blue_mask); // javadoc: inRange(src, lowerb, upperb, dst)
    Mat idk = new Mat();
    Core.bitwise_and(hsvMat,hsvMat,idk,blue_mask);
    Utils.matToBitmap(idk,temp);
    Bitmap mutableBitmap = temp.copy(Bitmap.Config.ARGB_8888, true);

1 个答案:

答案 0 :(得分:3)

您可以使用内置的OpenCV方法inRange()执行颜色过滤,这将允许您创建包含白色的蒙版,只要任何像素值落在定义的下限和上限之间,否则为黑色。从那里你可以简单地找到面具中白色像素的位置。

有关示例,请参阅this tutorial

另外,我之前的回答here提供了一些寻找好的下限和上限的建议 - 特别是,找到你想知道的图像中的一个区域(如链接的那个),并且找到这些值的平均值和标准差(在任何颜色空间中,但我可能建议从HSV或HLS开始)。然后,您可以轻松地将下限设置为mean-a*stddev,将上限设置为mean+b*stddev,其中ab是您可以尝试的一些值,以便查看哪些值最适合选择蓝色(只有蓝色)。您可以从a=b开始,并为它们使用整数值(1,2,3,...)并从那里进行磨练。

一旦你有了面具,它可能会在其中有一些洞或图像中其他地方的外来白色像素。您可以使用轮廓检测​​,霍夫线检测或斑点检测来获得正确的矩形。在这种情况下,我建议使用findContours()在蒙版上使用轮廓检测​​,找到最大的轮廓,找到它周围的boundingRect,这将直接为您提供像素位置。

Python示例

您正在使用C ++,但Python提供示例的速度更快,因此您必须翻译。我将使用第一张图片的调整大小版本:

Starting image

import numpy as np
import cv2

img = cv2.imread('image.jpg')
img = cv2.resize(img, None, fx=0.1, fy=0.1)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
roi = hsv[430:450, 20:170]

这里我只是调整图像的大小(主要是因为我可以轻松地显示它),转换颜色空间,以及定义仅包含蓝色像素的感兴趣区域(ROI)。在BGR中,ROI看起来像这样:

Blue ROI

此ROI仅包含蓝色像素,因此我可以找到蓝色值的 mean 蓝色值和蓝色值的标准差,以用作{{1 }}

inRange()

因此我们只有一个蓝色值的掩码:

Blue mask

从这里你可以进行正常的轮廓检测并找到它周围的边界框:

mu, sig = cv2.meanStdDev(roi)
a = 9

blue_mask = cv2.inRange(hsv, mu-a*sig, mu+a*sig)

现在我们在原始图像中的石蕊试纸周围有一个边界矩形(我们有左上角的位置,宽度和高度):

Detected blue