Цветовая градация дорожных знаков с красной окантовкой

Я хочу обнаруживать все дорожные знаки с красной окантовкой (треугольные и круглые). Алгоритм должен быть эффективным и надежным, чтобы работать в реальных ситуациях, поэтому я решил использовать пространство HSV, поскольку оно инвариантно к свету.

Я столкнулся с этим вопрос обнаружения красных объектов, и ответ состоял в том, чтобы использовать эти диапазоны значений для HSV: Код находится на C++:

inRange(hsv, Scalar(0, 70, 50), Scalar(10, 255, 255), mask1);
inRange(hsv, Scalar(170, 70, 50), Scalar(180, 255, 255), mask2);

Mat1b mask = mask1 | mask2;

Поскольку я использую Java OpenCV, я попробовал что, но обнаружил, что побитовую операцию OR выполнить невозможно.

Поэтому я попытался реализовать это вручную вместо использования OpenCV. Я также пробовал те же диапазоны значений красного цвета, которые указаны, и, к сожалению, результаты были ужасными:

Вот мой код

Mat hsv = new Mat();
Mat rgb = Highgui.imread(scene, Highgui.CV_LOAD_IMAGE_COLOR);
Imgproc.cvtColor(rgb, hsv, Imgproc.COLOR_RGB2HSV);
Mat thresh = new Mat(hsv.size(), CvType.CV_8UC1);


for(int x=0;x<hsv.rows();x++){
    for(int y=0;y<hsv.cols();y++)
    {
        double[] data = hsv.get(x,y);

        double H = data[0];
        double S = data[1];
        double V = data[2];
        if ((( 0.0>=H && H<=10.0) && (70.0>=S && S<=255.0) && (50.0>=V && V<=255.0)) || (( 170.0>=H && H<=180.0) && (70.0>=S && S<=255.0) && (50.0>=V && V<=255.0)) ) {

            thresh.put(x,y, 255);
        }
        else
        {
            thresh.put(x,y, 0);
        }
    }
}

Вот результаты до и после определения порога

Цветовая градация дорожных знаков с красной окантовкой

Цветовая градация дорожных знаков с красной окантовкой

Может ли кто-нибудь дать мне правильные ценности?

Mat rgb = Highgui.imread(scene, Highgui.CV_LOAD_IMAGE_COLOR); Да, это не RGB, это BGR.
Dan Mašek 01.05.2018 14:29

Боже мой, это работает :)), TBH, это настолько глупо, что многие люди не заметили BGR и RGB. Также я думал, что это одно и то же. не могли бы вы ответить на этот вопрос, чтобы я мог с ним согласиться, также укажите, есть ли между ними серьезные различия.

J.doe 01.05.2018 15:06

Итак, запустил JDK (лет 15 или около того с тех пор, как я последний раз писал Java) и протестировал код - кажется, можно использовать и bitwise_or. | Что ж, я прочитал много вопросов о opencv, и это одна из распространенных ошибок, которые я заметил, так что это одна из первых вещей, на которые я обращаю внимание.

Dan Mašek 01.05.2018 15:44

Спасибо :)))

J.doe 01.05.2018 16:01

кстати, цветовое пространство HSV НЕ инвариантно к свету! Очевидно, что если у вас есть цветной свет, цвета и, следовательно, ваше пороговое поведение могут измениться!

Micka 02.05.2018 17:00
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
5
128
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Решающая ошибка в самом начале:

Mat rgb = Highgui.imread(scene, Highgui.CV_LOAD_IMAGE_COLOR);
Imgproc.cvtColor(rgb, hsv, Imgproc.COLOR_RGB2HSV);

Справочник по OpenCV C++ API, как правило, является наиболее полным и подробным, поэтому никогда не помешает сослаться на него. Если вы посмотрите на cv::imread, вы заметите следующее примечание:

In the case of color images, the decoded images will have the channels stored in B G R order.

Однако в вашем коде вы обрабатываете изображение как RGB, то есть меняете местами синий и красный. Это фатально для вашего алгоритма - вы ищете красные вещи, но все, что было красным, на самом деле синее.

Исправление простое - переименуйте rgb в bgr (чтобы имена переменных не вводили в заблуждение) и измените код преобразования на Imgproc.COLOR_BGR2HSV.

Я считаю, что ваши предыдущие проблемы с bitwise_or были просто еще одним симптомом этой ошибки. (Я действительно не вижу причины, по которой это не сработает).

См. Следующий пример (с использованием OpenCV 3.4.0):

import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.core.Core;

public class test
{
    public static void main(String[] args)
    {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

        Mat image = Imgcodecs.imread("test.jpg", Imgcodecs.CV_LOAD_IMAGE_COLOR);
        if ((image == null) || image.empty()) {
            System.out.println("Failed to load input image.");
            System.exit(-1);
        }

        Mat image_hsv = new Mat();
        Imgproc.cvtColor(image, image_hsv, Imgproc.COLOR_BGR2HSV);

        Mat mask1 = new Mat();
        Mat mask2 = new Mat();
        Core.inRange(image_hsv, new Scalar(0, 70, 50), new Scalar(10, 255, 255), mask1);
        Core.inRange(image_hsv, new Scalar(170, 70, 50), new Scalar(180, 255, 255), mask2);

        Mat mask_combined = new Mat();
        Core.bitwise_or(mask1, mask2, mask_combined);

        Mat image_masked = new Mat();
        Core.bitwise_and(image, image, image_masked, mask_combined);

        Imgcodecs.imwrite("test-mask.jpg", mask_combined);        
        Imgcodecs.imwrite("test-masked.jpg", image_masked);

        System.out.println("Done!");
    }
}

Это создает следующую комбинированную маску из вашего образца входного изображения:

Combined mask

Если мы используем эту маску на исходном изображении, мы увидим, что действительно получаем красные биты:

Masked image

Я изучил OpenCV for C++/Python, а сейчас изучаю OpenCV for Java и обнаружил, что этот вопрос - a good practice for pixel loop.

Я использую OpenCV 4.0.0-pre, поэтому некоторые функции могут быть в другом modules/packages/headers ...


Как предлагает @Dan Mašek, вы должны преобразовать образ с помощью COLOR_BGR2HSV. Кроме этого, я также обнаружил, что вы ввели неправильные диапазоны hsv.

Для этого кода на C++:

inRange(hsv, Scalar(0, 70, 50), Scalar(10, 255, 255), mask1);
inRange(hsv, Scalar(170, 70, 50), Scalar(180, 255, 255), mask2);

Mat1b mask = mask1 | mask2;

Ваше условие диапазона в Java:

(( 0.0>=H && H<=10.0) && (70.0>=S && S<=255.0) && (50.0>=V && V<=255.0)) ||
(( 170.0>=H && H<=180.0) && (70.0>=S && S<=255.0) && (50.0>=V && V<=255.0)) 

Так должно быть:

(( 0.0<=H && H<=10.0) && (70.0<=S && S<=255.0) && (50.0<=V && V<=255.0)) ||
(( 170.0<=H && H<=180.0) && (70.0<=S && S<=255.0) && (50.0<=V && V<=255.0)) 

Или это:

///!(1) Create mask by loop with condition
(( 0.0<=H && H<=10.0) || ( 170.0<=H && H<=180.0)) && (70.0<=S && S<=255.0) && (50.0<=V && V<=255.0)

Или это:

///!(2) Create mask by calling API
Mat mask1 = new Mat();
Mat mask2 = new Mat();
Core.inRange(hsv, new Scalar(0, 70, 50), new Scalar(10, 255, 255), mask1);
Core.inRange(hsv, new Scalar(170, 70, 50), new Scalar(180, 255, 255), mask2);

Mat mask= new Mat();
Core.bitwise_or(mask1, mask2, mask);

В Java:

//! 2018.05.08 18:50:59 CST
//! 2018.05.08 20:53:48 CST
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.core.*;
import java.util.*;

public class test
{
    public static void main(String[] args)
    {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        Mat img = Imgcodecs.imread("test.jpg", Imgcodecs.CV_LOAD_IMAGE_COLOR);
        if ((img == null) || img.empty()) {
            System.out.println("Failed to load input img.");
            System.exit(-1);

        }
        Mat hsv = new Mat();
        Imgproc.cvtColor(img, hsv, Imgproc.COLOR_BGR2HSV);

        ///! (1) Create the mask by loop
        Mat mask = new Mat(hsv.size(), CvType.CV_8UC1, new Scalar(0,0,0));
        for(int i=0;i<hsv.rows();++i){
            for(int j=0;j<hsv.cols();++j){
                double[] data = hsv.get(i,j);
                double H = data[0];
                double S = data[1];
                double V = data[2];
                //mask[np.where(((h<10) | ((h>=170) & (h<=180)) ) & ((s>=70) & (s<=255)) & ((v>=50) & (v<=255)) )] = 255
                if ((( 0.0<=H && H<=10.0) || ( 170.0<=H && H<=180.0)) && (70.0<=S && S<=255.0) && (50.0<=V && V<=255.0)) {
                    mask.put(i,j, 255);
                }
            }
        }

        ///!(2) Create mask by calling API
        Mat mask1 = new Mat();
        Mat mask2 = new Mat();
        Core.inRange(hsv, new Scalar(0, 70, 50), new Scalar(10, 255, 255), mask1);
        Core.inRange(hsv, new Scalar(170, 70, 50), new Scalar(180, 255, 255), mask2);

        Mat mask_combined = new Mat();
        Core.bitwise_or(mask1, mask2, mask_combined);

        ///! Get the masked
        Mat masked = new Mat();
        Core.bitwise_and(img, img, masked, mask);
        //Core.bitwise_and(img, img, masked, mask_combined);

        Imgcodecs.imwrite("test_mask.jpg", mask);
        Imgcodecs.imwrite("test_masked.jpg", masked);
    }
}

В Python это можно записать как:

import cv2
import numpy as np

img = cv2.imread("test.jpg")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h,s,v = cv2.split(hsv)

## (1) by hand 
mask = np.zeros_like(s)
mask[np.where(((h<10) | ((h>=170) & (h<=180)) ) & ((s>=70) & (s<=255)) & ((v>=50) & (v<=255)) )] = 255

## (2) call api 
mask1 = cv2.inRange(hsv, (0, 70, 50), (10, 255, 255))
mask2 = cv2.inRange(hsv, (170, 70, 50), (180, 255, 255))
mask = cv2.bitwise_or(mask1, mask2)

masked = cv2.bitwise_and(img, img, mask=mask)

Вот мой результат:

Другие вопросы по теме