Попытка обнаружить синий цвет по изображению с помощью opencv и получить неожиданный результат

Я новичок в OpenCV4Android. Вот код, который я написал, чтобы обнаружить синюю каплю на изображении. Среди следующих изображений изображение 1 было в моем ноутбуке. Я запускаю приложение, и кадр, снятый камерой OpenCV, является изображением 2. Вы можете посмотреть на код, чтобы увидеть, что представляют собой остальные изображения. (Как видно из кода, все изображения сохраняются на SD-карте.)

У меня есть следующие вопросы:

  • Почему цвет светло-синего шарика оказался светло-желтым в кадре rgba, снятом камерой (показано на рисунке 2).

  • Я создал boundingRect вокруг самой большой синей капли, но потом ROI при выполнении rgbaFrame.submat(detectedBlobRoi), Но вы можете видеть на последнем изображении, оно выглядит как пара серых пикселей. Я ожидал, что синяя сфера отделена от остальной части изображения.

Что я пропускаю или делаю неправильно?

КОД:

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
     }
  1. Исходное изображение на компьютере. Это было сделано путем размещения камеры телефона перед экраном ноутбука.

  1. rgbaFrame.jpg

  1. hsvImage.jpg

  1. dilatedImage.jpg

  1. maskedMat.jpg

  1. detectedBlobRgba.jpg


РЕДАКТИРОВАТЬ:

Я просто использовал Core.inRange(hsvImage, new Scalar(0,50,40), new Scalar(10,255,255), maskedImage);//3, 217, 225 --- 6, 85.09, 88.24 ...... 3 219 255 и я сделал скриншот с сайта colorizer.org, предоставив ему собственные значения HSV для красного цвета, то есть для красного OpenCV Scalar(3, 217, 255) (который попадает в диапазон, заданный в данном inRange функция, я масштабировал значения канала до масштаба colorizer.org, который равен H=0-360, S=0-100, V=0-100, путем умножения значения H на 2 и деления значений S и V на 255 и умножение на 100. Это дало мне 6, 85.09, 88.24 который я установил на сайте, и сделал скриншот (первый на следующих изображениях).

  1. На оригинальном скриншоте я запечатлел этот кадр.

  1. rgbaFrame.jpg

  1. hsvImage.jpg

  1. maskedImage.jpg

  1. dilatedMat.jpg

  1. detectedBlobRgba.jpg


ВАЖНЫЙ:

Данный метод фактически вызывается в моем тестовом приложении, когда я прикасаюсь к rgbaFrame (т.е. он вызывается внутри onTouch метод). Я также использую следующий код для печати на TextView Hue, Saturation, а также Value значения цветной капли, к которой я прикоснулся. Когда я запустил это приложение, я коснулся красной капли и получил следующие значения: Hue:3, Saturation:219, Value:255 ,

public boolean onTouch(View v, MotionEvent motionEvent) { detectColoredBlob(); int cols = rgbaFrame.cols(); int row = 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 

}

2 ответа

Решение

Существует несколько ловушек при преобразовании изображения в цветовое пространство HSV и использовании цветового пространства HSV.

  1. OpenCV использует сжатый диапазон оттенков, потому что оригинальный диапазон оттенков варьируется от 0 до 360, что означает, что значения не могут поместиться в 1 байт (значения от 0 до 255), в то время как каналы насыщения и значений точно покрыты 1 байтом. Таким образом, OpenCV использует значения оттенка, разделенные на 2. Таким образом, канал оттенка будет покрываться матричными записями от 0 до 180. В этом случае ваш диапазон оттенка от 170 до 270 должен быть разделен на 2 = диапазон от 65 до 135 в OpenCV.

  2. Оттенок говорит вам о цветовом тоне, но насыщенность и значение по-прежнему важны для снижения шума, поэтому установите для своего порога минимальное значение насыщенности и значение тоже

  3. очень важно: OpenCV использует упорядочение памяти BGR для рендеринга и сохранения изображений. Это означает, что если ваше изображение имеет порядок RGB(a) и вы сохраняете его без преобразования цветов, вы меняете каналы R и B, поэтому предполагается, что красный цвет станет синим и т. Д. К сожалению, обычно вы не можете прочитать данные самого изображения, будь то это RGB или BGR заказ, так что вы должны попытаться выяснить это из источника изображения. OpenCV позволяет нескольким флагам конвертировать из RGB(A) в HSV и / или из BGR(A) в HSV, и / или из RGB в BGR и т. Д., Так что это не проблема, если вы знаете, какой формат памяти вашего изображения использует. Однако для отображения и сохранения всегда требуется упорядочение BGR, поэтому, если вы хотите отобразить или сохранить изображение, преобразуйте его в BGR! Однако значения HSV будут одинаковыми, независимо от того, конвертируете ли вы изображение BGR с помощью BGR2HSV или конвертируете изображение RGB с помощью RGB2HSV. Но он будет иметь неправильные значения, если вы преобразуете изображение BGR с помощью RGB2HSV или изображение RGB с помощью BGR2HSV... Я не уверен на 100% в API Java / Python / Android openCV, но ваше изображение действительно выглядит как каналы 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);

Вероятно, диапазон, который вы используете, не подходит для синего цвета. В OpenCV диапазон оттенков составляет 0-180, а вы дали 170-270. Найдите правильное значение оттенка для синего и используйте в inRange.

  1. http://answers.opencv.org/question/30547/need-to-know-the-hsv-value/
  2. http://answers.opencv.org/question/28899/correct-hsv-inrange-values-for-red-objects/

Вы можете сослаться на ответ здесь для выбора правильного значения hsv.

Ниже приведен код для сегментирования красного цвета, проверьте его с помощью своего кода и убедитесь, что он сегментирует красный объект.

    Imgproc.cvtColor(rgbaFrame, hsv, Imgproc.COLOR_RGB2HSV,4); // Convert to hsv for color segmentation.      
    Core.inRange(hsv,new Scalar(0,50,40,0), new Scalar(10,255,255,0),thr);//upper red range of hue cylinder 
Другие вопросы по тегам