ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

android – 尝试使用opencv从图像中检测蓝色,并获得意想不到的结果

2019-09-29 08:24:39  阅读:264  来源: 互联网

标签:hsv android opencv opencv4android opencv-drawcontour


我是OpenCV4Android的新手.这是我编写的一些代码,用于检测图像中的蓝色斑点.在下面的图像中,图像1在我的笔记本电脑中.我运行应用程序,OpenCV摄像头捕获的帧是图像2.您可以查看代码以查看其余图像是什么. (正如您在代码中看到的,所有图像都保存在SD卡中.)

我有以下问题:

>为什么在相机捕获的rgba帧中,浅蓝色斑点的颜色变成浅黄色(如图2所示).
>我在最大的蓝色blob周围创建了一个boundingRect,然后通过执行rgbaFrame.submat(detectedBlobRoi)创建了ROI.但是你可以在最后一张图片中看到它看起来像几个灰色像素.我期待蓝色球体与图像的其余部分分开.

我错过了什么或做错了什么?

码:

private void detectColoredBlob () { 
        Highgui.imwrite("/mnt/sdcard/DCIM/rgbaFrame.jpg", rgbaFrame);//check
        Mat hsvImage = new Mat(); 
        Imgproc.cvtColor(rgbaFrame, hsvImage, Imgproc.COLOR_RGB2HSV_FULL);
        Highgui.imwrite("/mnt/sdcard/DCIM/hsvImage.jpg", hsvImage);//check

        Mat maskedImage = new Mat(); 
        Scalar lowerThreshold = new Scalar(170, 0, 0); 
        Scalar upperThreshold = new Scalar(270, 255, 255); 
        Core.inRange(hsvImage, lowerThreshold, upperThreshold, maskedImage);
        Highgui.imwrite("/mnt/sdcard/DCIM/maskedImage.jpg", maskedImage);//check

        Mat dilatedMat= new Mat(); 
        Imgproc.dilate(maskedImage, dilatedMat, new Mat() ); 
        Highgui.imwrite("/mnt/sdcard/DCIM/dilatedMat.jpg", dilatedMat);//check
        List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
        Imgproc.findContours(dilatedMat, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
        //Use only the largest contour. Other contours (any other possible blobs of this color range) will be ignored.
        MatOfPoint largestContour = contours.get(0);
        double largestContourArea = Imgproc.contourArea(largestContour);
        for ( int i=1; i<contours.size(); ++i) {//NB Notice the prefix increment.
            MatOfPoint currentContour = contours.get(0);
            double currentContourArea = Imgproc.contourArea(currentContour);
            if (currentContourArea > largestContourArea) {
                largestContourArea = currentContourArea;
                largestContour = currentContour;
            }
        }

        Rect detectedBlobRoi = Imgproc.boundingRect(largestContour);
        Mat detectedBlobRgba = rgbaFrame.submat(detectedBlobRoi);
        Highgui.imwrite("/mnt/sdcard/DCIM/detectedBlobRgba.jpg", detectedBlobRgba);//check
     }

>计算机中的原始图像,这是通过将手机的相机放在笔记本电脑屏幕前面拍摄的.

enter image description here

> rgbaFrame.jpg

enter image description here

> hsvImage.jpg

enter image description here

> dilatedImage.jpg

enter image description here

> maskedMat.jpg

enter image description here

> detectedBlobRgba.jpg

enter image description here

编辑:

我刚刚使用了Core.inRange(hsvImage,新Scalar(0,50,40),新Scalar(10,255,255),maskedImage); // 3,217,225 — 6,85.09,88.24 …… 3 219 255,我通过给它一个红色的自定义HSV值,即对于OpenCV红色标量(3,217,255)(它落在给定的inRange函数中设置的范围内)捕获了网站colorizer.org的screeshot ,我将通道值缩放到colorizer.org的比例,即H = 0-360,S = 0-100,V = 0-100,通过将H值乘以2,并将S和V值除以255,然后乘以100.这给了我在网站上设置的6,85.09,88.24,并截取了屏幕截图(下图中的第一个).

>原始截图,我抓住了这个框架.

enter image description here

> rgbaFrame.jpg

enter image description here

> hsvImage.jpg

enter image description here

> maskedImage.jpg

enter image description here

> dilatedMat.jpg

enter image description here

> detectedBlobRgba.jpg

enter image description here

重要:

当我触摸rgbaFrame(即在onTouch方法中调用它)时,实际上在我的测试应用程序中调用给定的方法.我还使用以下代码将TextI中已触摸的彩色blob的Hue,Saturation和Value值打印到TextView.当我运行这个应用程序时,我触摸了红色blob,并得到以下值:Hue:3,Saturation:219,Value:255.

public boolean onTouch(View v,MotionEvent motionEvent){
    detectColoredBlob();
    int cols = rgbaFrame.cols();
    int rows = rgbaFrame.rows();

int xOffset = (openCvCameraBridge.getWidth() - cols) / 2;
int yOffset = (openCvCameraBridge.getHeight() - rows) / 2;

int x = (int) motionEvent.getX() - xOffset;
int y = (int) motionEvent.getY() - yOffset;

Log.i(TAG, "Touch image coordinates: (" + x + ", " + y + ")");//check

if ((x < 0) || (y < 0) || (x > cols) || (y > rows)) { return false; }

Rect touchedRect = new Rect();
touchedRect.x = (x > 4) ? x - 4 : 0;
touchedRect.y = (y > 4) ? y - 4 : 0;
touchedRect.width = (x + 4 < cols) ? x + 4 - touchedRect.x : cols - touchedRect.x;
touchedRect.height = (y + 4 < rows) ? y + 4 - touchedRect.y : rows - touchedRect.y;
Mat touchedRegionRgba = rgbaFrame.submat(touchedRect);

Mat touchedRegionHsv = new Mat();
Imgproc.cvtColor(touchedRegionRgba, touchedRegionHsv, Imgproc.COLOR_RGB2HSV_FULL);

double[] channelsDoubleArray = touchedRegionHsv.get(0, 0);//**********
float[] channelsFloatArrayScaled = new float[3];
for (int i = 0; i < channelsDoubleArray.length; i++) {
    if (i == 0) {
        channelsFloatArrayScaled[i] = ((float) channelsDoubleArray[i]) * 2;// TODO Wrap an ArrayIndexOutOfBoundsException wrapper
    } else if (i == 1 || i == 2) {
        channelsFloatArrayScaled[i] = ((float) channelsDoubleArray[i]) / 255;// TODO Wrap an ArrayIndexOutOfBoundsException wrapper
    }
}

int androidColor = Color.HSVToColor(channelsFloatArrayScaled);

view.setBackgroundColor(androidColor);
textView.setText("Hue : " + channelsDoubleArray[0] + "\nSaturation : " + channelsDoubleArray[1] + "\nValue : "
        + channelsDoubleArray[2]);

touchedRegionHsv.release();
return false; // don't need subsequent touch events 

}

解决方法:

将图像转换为HSV颜色空间并使用HSV颜色空间时有多个陷阱.

> OpenCV使用压缩的色调范围,因为原始色调范围从0到360,这意味着值不能适合1个字节(值0到255),而饱和度和值通道完全被1个字节覆盖.因此,OpenCV使用色调值除以2.因此,色调通道将被0到180之间的矩阵条目覆盖.对此,在OpenCV中,您的色调范围170到270应除以2 =范围65到135.
>色调告诉您色调,但饱和度和值对于降低噪点仍然很重要,因此将阈值设置为某个最小饱和度和值
>非常重要:OpenCV使用BGR内存排序进行渲染和图像保存.这意味着如果您的图像具有RGB(a)排序并且您保存它而没有颜色转换,则您交换R和B通道,因此假设红色将变为蓝色等.不幸的是,通常您无法从图像数据本身读取,它是RGB或BGR有序的,所以你应该尝试从图像源找到它. OpenCV允许几个标志从RGB(A)转换为HSV和/或从BGR(A)转换为HSV,和/或从RGB转换为BGR等,这样就没问题,只要你知道你的图像是哪种内存格式使用.但是,显示和保存始终采用BGR排序,因此如果要显示或保存图像,请将其转换为BGR!但是,无论您是使用BGR2HSV转换BGR图像还是使用RGB2HSV转换RGB图像,HSV值都是相同的.但是如果你使用RGB2HSV或带有BGR2HSV的RGB图像转换BGR图像,它将会有错误的值…我不是100%肯定openCV的Java / Python / Android API,但你的图像看起来真的像B和R通道交换或误解(但由于你使用RGBA2HSV转换,hsv颜色没有问题).

关于你的轮廓提取,你的代码中有一个微小的(复制粘贴?)错误,每个人偶尔会观察到:

MatOfPoint largestContour = contours.get(0);
    double largestContourArea = Imgproc.contourArea(largestContour);
    for ( int i=1; i<contours.size(); ++i) {//NB Notice the prefix increment.
        // HERE you had MatOfPoint currentContour = contours.get(0); so you tested the first contour in each iteration
        MatOfPoint currentContour = contours.get(i);
        double currentContourArea = Imgproc.contourArea(currentContour);
        if (currentContourArea > largestContourArea) {
            largestContourArea = currentContourArea;
            largestContour = currentContour;
        }
    }

所以可能只需将此更改为在循环中使用i而不是0

MatOfPoint currentContour = contours.get(i);

标签:hsv,android,opencv,opencv4android,opencv-drawcontour
来源: https://codeday.me/bug/20190929/1830909.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有