3

I would like to use find the cabbage in the image provided. I already have an implementation with a previous question using colour thresholding, however it required me to manually enter HSV or RGB values, i require an adaptive way of thesholding and thought of using canny edge to find edges and then create a mask.

Below is the implentation from colour thesholding which is the desired output from canny.

Sample image

Masked

Using find contours

# Import the necessary packages
import numpy as np
import argparse
import cv2
import glob

def auto_canny(image, sigma=0.33):
    # compute the median of the single channel pixel intensities
    v = np.median(image)

    # apply automatic Canny edge detection using the computed median
    lower = int(max(0, (1.0 - sigma) * v))
    upper = int(min(255, (1.0 + sigma) * v))
    edged = cv2.Canny(image, lower, upper)

    # return the edged image
    return edged

# Construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True,
    help = "Path to the image")
args = vars(ap.parse_args())
image = cv2.imread(args["image"])
cv2.imshow("Image", image)


gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (3, 3), 0)

auto = auto_canny(blurred)

cv2.imshow("Image", auto)
cv2.imwrite("newimage1.jpg", auto)



(_, cnts, _) = cv2.findContours(auto.copy(), cv2.RETR_EXTERNAL,
        cv2.CHAIN_APPROX_SIMPLE)

if len(cnts) > 0:
    # sort the contours and find the largest one -- we
    # will assume this contour correspondes to the area
    # of my phone
    cnt = sorted(cnts, key = cv2.contourArea, reverse = True)[0]
    cv2.drawContours(image, [cnt], -1, (0, 255, 0), 2)


cv2.imshow("Tracking", image)
cv2.imwrite("newimage2.jpg", image)
cv2.waitKey(0)

cv2.waitKey(0)

Results:

Canny Output Outcome

My thought process is to use canny to find edges and then use findcontours to get the biggest contour and create a mask which should be the cabbage. However, this does not seem to be working as the result from canny output has a lot of edges.

I think i should do some pre processing before applying the canny edge detection but i'm not too sure what what techniques to apply for pre processing.

EDIT:

Read through the few suggestions and tried out those that i have an idea of how to do, first i converted it to HSV and split the image into respective H, s and v. Implemented 2 methods with the results below, any suggestions on how to improve?

# Import the necessary packages
import numpy as np
import argparse
import cv2
import glob

def auto_canny(image, sigma=0.33):
    # compute the median of the single channel pixel intensities
    v = np.median(image)

    # apply automatic Canny edge detection using the computed median
    lower = int(max(0, (1.0 - sigma) * v))
    upper = int(min(255, (1.0 + sigma) * v))
    edged = cv2.Canny(image, lower, upper)

    # return the edged image
    return edged

# Construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True,
    help = "Path to the image")
args = vars(ap.parse_args())
image = cv2.imread(args["image"])
cv2.imshow("Image", image)
newImage = image.copy()

hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
blurred = cv2.GaussianBlur(hsv, (3, 3), 0)

#cv2.imshow("HSV image", blurred)

#now to seperate and only extract hue image
h,s,v = cv2.split(blurred)

cv2.imshow("H", h)
#cv2.imshow("S", s)
#cv2.imshow("V", v)

thresh = cv2.adaptiveThreshold(h, 255,
    cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 11, 4)
cv2.imshow("adaptive1", thresh)
cv2.imwrite("adaptive1.jpg", thresh)

(_, cnts, _) = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
        cv2.CHAIN_APPROX_SIMPLE)

auto = auto_canny(h)
cv2.imshow("canny", auto)
cv2.imwrite("canny1.jpg", auto)


(_, cnts2, _) = cv2.findContours(auto.copy(), cv2.RETR_EXTERNAL,
        cv2.CHAIN_APPROX_SIMPLE)

if len(cnts) > 0:
    # sort the contours and find the largest one -- we
    # will assume this contour correspondes to the area
    # of my phone
    cnt = sorted(cnts, key = cv2.contourArea, reverse = True)[0]
    cv2.drawContours(image, [cnt], -1, (0, 255, 0), 2)

cv2.imshow("adaptive2", image)
cv2.imwrite("adaptive2.jpg", image)

if len(cnts2) > 0:
    # sort the contours and find the largest one -- we
    # will assume this contour correspondes to the area
    # of my phone
    cnt = sorted(cnts2, key = cv2.contourArea, reverse = True)[0]
    cv2.drawContours(newImage, [cnt], -1, (0, 255, 0), 2)

cv2.imshow("canny2", newImage)
cv2.imwrite("canny2.jpg", newImage)

cv2.waitKey(0)

Adaptive:

adaptive

contour

Canny:

canny

contour

Jack Yeoh
  • 365
  • 1
  • 3
  • 11
  • 1
    looks like a case for grabcut algorithm – Andrey Kamaev Sep 04 '17 at 23:48
  • try using HSV color image as base like in: https://stackoverflow.com/questions/29156091/opencv-edge-border-detection-based-on-color/29162813#29162813 – Micka Sep 05 '17 at 10:00
  • @AndreyKamaev I'm assuming you're talking about something like [this](https://cvg.ethz.ch/teaching/cvl/2012/grabcut-siggraph04.pdf). How does this compare to deep learning methods? ie. [this](https://github.com/matterport/Mask_RCNN) recent work on segmentation – Nathan majicvr.com May 06 '18 at 17:14

2 Answers2

4

You could also set up a 3d histogram of your image in color space and if you know that your target is the primary object in the scene, you can algorythmically set up bounds around the primary color space (clustering) and then use that to segment. That is likely what I would go for.

Sneaky Polar Bear
  • 1,611
  • 2
  • 17
  • 29
2

Don't convert to grayscale, convert to some HSV colorspace and try and segment out the green objects.

Use adaptiveThreshold rather than canny, it does a good job of finding the best edge level locally

Martin Beckett
  • 94,801
  • 28
  • 188
  • 263