Home > OS >  Is there any way to gain a smoother shape in binary image?
Is there any way to gain a smoother shape in binary image?

Time:10-15

I want to detect shapes like triangles, circles and rectangles in binary image after color segmentation but sometimes shapes are very ripped and jugged like in this picture. Is there any way to get a better shape?

Maybe that is something wrong with my color masks.

    def redColorDetection(self, img):

    self.low_red = np.array([160, 50, 5])
    self.high_red = np.array([180, 255, 255])

    red_mask = cv2.inRange(self.hsv, self.low_red, self.high_red)

    return  red_mask

enter image description here

enter image description here

CodePudding user response:

Red color is split in the cicular region of the HSV color space, so there is red part in 0 x and a red part in 360-x (180-x/2 in opencv because the 360° are divided by 2 there to fit into 8 bit).

For example 0 15 and 180-15 ( /-10 is also ok) in opencv give good results in your example:

int main()
{
    cv::Mat img = cv::imread("C:/data/StackOverflow/redSign.jpg");
    cv::Mat hsv;
    cv::cvtColor(img, hsv, cv::COLOR_BGR2HSV);

    cv::Scalar low_red1(165, 50, 5);
    cv::Scalar high_red1(180, 255, 255);

    cv::Scalar low_red2(0, 50, 5);
    cv::Scalar high_red2(15, 255, 255);

    cv::Mat mask1;
    cv::Mat mask2;
    cv::inRange(hsv, low_red1, high_red1, mask1);
    cv::inRange(hsv, low_red2, high_red2, mask2);

    cv::imshow("mask1", mask1);
    cv::imshow("mask2", mask2);

    cv::imshow("mask1 mask2", mask1   mask2);

    cv::waitKey(0);

}

Giving this result:

Mask1: enter image description here

Mask2: enter image description here

Mask total: enter image description here

If you want to segment red with a single inRange call you can shift the hue channel, but that's likely not more efficient, but might make it easier to write some kind of color segmentation general code: Have a look at the "shiftedH" part of: enter image description here

import cv2
import numpy as np

# load image
img = cv2.imread('sign.jpg')

# convert to LAB
lab = cv2.cvtColor(img,cv2.COLOR_BGR2LAB)

# extract A channel
# The A axis is relative to the green–red opponent colors
# See https://en.wikipedia.org/wiki/CIELAB_color_space
A = lab[:,:,1]

# threshold A channel (choose one or the other depending upon what result you want)
thresh1 = cv2.threshold(A, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU)[1]
thresh2 = cv2.threshold(A, 150, 255, cv2.THRESH_BINARY)[1]

# save results
cv2.imwrite('sign_A_channel_lab.jpg', A)
cv2.imwrite('sign_threshold1.jpg', thresh1)
cv2.imwrite('sign_threshold2.jpg', thresh2)

# show results
cv2.imshow('A',A)
cv2.imshow('thresh1', thresh1)
cv2.imshow('thresh2', thresh2)
cv2.waitKey(0)
cv2.destroyAllWindows()

A channel from LAB:

enter image description here

Otsu Threshold:

enter image description here

Alternate Simple Threshold:

enter image description here

  • Related