简体   繁体   中英

Detection of red circle-alike | openCV | Java

I am writing a program which need to detect red circle-alikes from this picture.

在此处输入图片说明

I have tried canny edge detection and find contours but none of them find this red "circles". I also tried to convert this to hsv and detect this by color but I couldn't determine good range for this color, maybe background color confuses it?

I put here a piece of my code with my final attempt..

Mat image = new Mat();
image = Imgcodecs.imread("image.jpg");
Mat hsvImage = new Mat();
Mat grayscaleImage = new Mat();
Mat binaryImage = new Mat();
Imgproc.blur(image, image, new Size(1, 1));  
Imgproc.cvtColor(image, hsvImage, Imgproc.COLOR_BGR2HSV);           
Imgproc.cvtColor(image, grayscaleImage, Imgproc.COLOR_BGR2GRAY);            
Imgproc.equalizeHist(grayscaleImage, grayscaleImage);
Imgproc.Canny(grayscaleImage, grayscaleImage, 50, 150, 3,false);

List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
        Imgproc.findContours(grayscaleImage.clone(), contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);


        for (int id=0;id<contours.size();id++){
            MatOfPoint2f mop2f = new MatOfPoint2f();
            contours.get(id).convertTo(mop2f,CvType.CV_32F);
            RotatedRect rectangle = Imgproc.minAreaRect(mop2f);
            if (rectangle.boundingRect().width>80)
            Imgproc.drawContours(image,contours,id,new Scalar(0,255,0));

        }

If you want to process that marked image, you really might want to detect colors. Typically this is done in HSV color-space.

Here is some C++ code to detect "red" color. The result isn't good enough to use findContours yet, but maybe after some dilation. Maybe you can convert the code to Java.

If you want to detect different color, change the line redMask = thresholdHue(hsv, 0, 20, 50, 50); to mask = thresholdHue(hsv, yourWantedHueColorValue, 20, 50, 50);`

// for example to shift a circluar hue-channel
cv::Mat shiftChannel(cv::Mat H, int shift, int maxVal = 180)
{
    // CV_8UC1 only!
    cv::Mat shiftedH = H.clone();
    //int shift = 25; // in openCV hue values go from 0 to 180 (so have to be doubled to get to 0 .. 360) because of byte range from 0 to 255
    for (int j = 0; j < shiftedH.rows; ++j)
    for (int i = 0; i < shiftedH.cols; ++i)
    {
        shiftedH.at<unsigned char>(j, i) = (shiftedH.at<unsigned char>(j, i) + shift) % maxVal;
    }

    return shiftedH;
}

cv::Mat thresholdHue(cv::Mat hsvImage, int hueVal, int range = 30, int minSat = 50, int minValue = 50)
{
    // hsvImage must be CV_8UC3 HSV image.
    // hue val and range are in openCV's hue range (0 .. 180)
    // range shouldnt be bigger than 90, because that's max (all colors), after shifting the hue channel.

    // this function will
    //    1. shift the hue channel, so that even colors near the border (red color!) will be detectable with same code.
    //    2. threshold the hue channel around the value 90 +/- range

    cv::Mat mask; // return-value

    std::vector<cv::Mat> channels;
    cv::split(hsvImage, channels);

    int targetHueVal = 180 / 2; // we'll shift the hue-space so that the target val will always be 90 afterwards, no matter which hue value was chosen. This can be important if 
    int shift = targetHueVal - hueVal;
    if (shift < 0) shift += 180;

    cv::Mat shiftedHue = shiftChannel(channels[0], shift, 180);

    // merge the channels back to hsv image
    std::vector<cv::Mat> newChannels;
    newChannels.push_back(shiftedHue);
    newChannels.push_back(channels[1]);
    newChannels.push_back(channels[2]);
    cv::Mat shiftedHSV;
    cv::merge(newChannels, shiftedHSV);

    // threshold
    cv::inRange(shiftedHSV, cv::Vec3b(targetHueVal - range, minSat, minValue), cv::Vec3b(targetHueVal + range, 255, 255), mask);

    return mask;
}


int main(int argc, char* argv[])
{
    cv::Mat input = cv::imread("C:/StackOverflow/Input/redCircleLikeContours.jpg");


    cv::Mat redMask; 

    cv::Mat hsv;
    cv::cvtColor(input, hsv, CV_BGR2HSV);

    redMask = thresholdHue(hsv, 0, 20, 50, 50);

    cv::imshow("red", redMask);

    cv::imshow("input", input);
    cv::imwrite("C:/StackOverflow/Output/redCircleLikeContoursMask.png", redMask);

    cv::waitKey(0);
    return 0;
}

Here's the result:

在此处输入图片说明

Here is my code if somebody would want to look :)

public static void main (String args[]){

        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);               

        Mat image = new Mat();
        image = Imgcodecs.imread("imageorg.jpg");

        if ( image == null) System.out.println("Image is fine");
        else System.out.println("Wrong path to image");


        Mat hsvImage = new Mat();
        Imgproc.blur(image, image, new Size(3,3));  
        Imgproc.cvtColor(image, hsvImage, Imgproc.COLOR_BGR2HSV);           

        Mat redMask = new Mat();
        redMask = thresholdHue(hsvImage,0,20,50,50);

        Mat kernel = new Mat();
        kernel = Imgproc.getStructuringElement(Imgproc.MORPH_DILATE, new Size(2,2));        

        Mat dilateMat = new Mat();
        Imgproc.dilate(redMask, dilateMat, kernel);                         

        Imgcodecs.imwrite("redCircleLikeContours.png", redMask);                


        List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
        Imgproc.findContours(dilateMat.clone(), contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);           


        List<MatOfPoint> removedContoursList = new ArrayList<MatOfPoint>();         

        for (int id=0;id<contours.size();id++){                     
            MatOfPoint2f mop2f = new MatOfPoint2f();
            contours.get(id).convertTo(mop2f,CvType.CV_32F);
            RotatedRect rectangle = Imgproc.minAreaRect(mop2f);
            if (rectangle.boundingRect().height<10){
                removedContoursList.add(contours.get(id));
                System.out.println("removing: "+rectangle.boundingRect());
                contours.remove(id);
                id--;
            }
        }

    }


    public static Mat thresholdHue(Mat hsvImage, int hueVal, int range, int minSat, int minValue)
    {   
        Mat mask = new Mat(); 

        List<Mat> channels = new ArrayList<Mat>();
        Core.split(hsvImage, channels);

        int targetHueVal = 180 / 2; 
        int shift = targetHueVal - hueVal;
        if (shift < 0) shift += 180;

        Mat shiftedHue = shiftChannel(channels.get(0), shift, 180);


        List<Mat> newChannels = new ArrayList<Mat>();

        newChannels.add(shiftedHue);
        newChannels.add(channels.get(1));
        newChannels.add(channels.get(2));
        Mat shiftedHSV = new Mat();
        Core.merge(newChannels, shiftedHSV);


        Core.inRange(shiftedHSV, new Scalar(targetHueVal - range, minSat, minValue), new Scalar(targetHueVal + range, 255, 255), mask);

        return mask;
    }


    private static Mat shiftChannel(Mat H, int shift, int maxVal)
    {

        Mat shiftedH = H.clone();
        for (int j = 0; j < shiftedH.rows(); ++j)
        for (int i = 0; i < shiftedH.cols(); ++i)
        {
            shiftedH.put(j, i,(shiftedH.get(j,i)[0] + shift) % maxVal);
        }

        return shiftedH;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM