-1

I am trying to come up with a way to get rid of tinier and smaller details from a single larger contour, that I am able to find in an image.

import cv2
import numpy as np

image_vec = cv2.imread('0000000000.eigo.png', cv2.COLOR_BGR2GRAY)
g_blurred = cv2.GaussianBlur(image_vec, (5, 5), 0)

blurred_float = g_blurred.astype(np.float32) / 255.0
edgeDetector = cv2.ximgproc.createStructuredEdgeDetection("model.yml")
edges = edgeDetector.detectEdges(blurred_float) * 255.0
cv2.imwrite('edge-raw.jpg', edges)

def SaltPepperNoise(edgeImg):
    count = 0
    lastMedian = edgeImg
    median = cv2.medianBlur(edgeImg, 3)
    while not np.array_equal(lastMedian, median):
        zeroed = np.invert(np.logical_and(median, edgeImg))
        edgeImg[zeroed] = 0
        count = count + 1
        if count > 70:
            break
        lastMedian = median
        median = cv2.medianBlur(edgeImg, 3)

edges_ = np.asarray(edges, np.uint8)
SaltPepperNoise(edges_)
cv2.imwrite('edge.jpg', edges_)

#canny edge detection
edges_ = cv2.Canny(edges_, 50, 255)
edges_ = cv2.GaussianBlur(edges_, (5, 5), 0)
cv2.imwrite('edgecanny.png', edges_)

def findSignificantContour(edgeImg):
    contours, hierarchy = cv2.findContours(
        edgeImg,
        cv2.RETR_TREE,
        cv2.CHAIN_APPROX_SIMPLE
    )
        # Find level 1 contours
    level1Meta = []
    for contourIndex, tupl in enumerate(hierarchy[0]):
        # Filter the ones without parent
        if tupl[3] == -1:
            tupl = np.insert(tupl.copy(), 0, [contourIndex])
            level1Meta.append(tupl)
    contoursWithArea = []
    for tupl in level1Meta:
        contourIndex = tupl[0]
        contour = contours[contourIndex]
        area = cv2.contourArea(contour)
        contoursWithArea.append([contour, area, contourIndex])
    contoursWithArea.sort(key=lambda meta: meta[1], reverse=True)
    largestContour = contoursWithArea[0][0]
    return largestContour

contour = findSignificantContour(edges_)
contourImg = np.copy(image_vec)
cv2.drawContours(contourImg, [contour], 0, (0, 255, 0), 2, cv2.LINE_AA, maxLevel=1)
cv2.imwrite('contour.jpg', contourImg)

hh, ww = image_vec.shape[:2]

mask = np.zeros((hh,ww), dtype=np.uint8)
cv2.drawContours(mask, [contour], 0, (255,255,255), cv2.FILLED)

# invert mask so shapes are white on black background
mask_inv = 255 - mask

# create new (blue) background
bckgnd = np.full_like(image_vec, (255,0,0))

# apply mask to image
image_masked = cv2.bitwise_and(image_vec, image_vec, mask=mask)

# apply inverse mask to background
bckgnd_masked = cv2.bitwise_and(bckgnd, bckgnd, mask=mask_inv)

# add together
result = cv2.add(image_masked, bckgnd_masked)

cv2.imwrite("imwrite.png", result)

Input Image:

enter image description here

Output Image:

enter image description here

Desired output:

enter image description here

This happens with all images.

I have tried using erosion and dilation, but that works differently with every image.

I was wondering if there is a way I can find two close pixels that are not connected, forming a branch, then connect those two pixels.

I will have a single huge contour in every image I pass, so there is no question of overlapping or smaller but useful contours.

vwertuzy
  • 171
  • 1
  • 2
  • 12
  • Comparing your output to desired output: it looks like most of what you want to remove are convex regions. Is it possible to create a mask which removes the convex portions like this: https://stackoverflow.com/questions/35226993/how-to-crop-away-convexity-defects? Are all of your target contours a distinct color? You could take your result and find the largest contour only in the "red" colorspace. – WesH Feb 24 '21 at 20:33
  • @WesH I tried to remove convex portions, but the points are really weird and completely not what we expect them to be. All of my target contours will be mostly a single colored objects. – vwertuzy Feb 25 '21 at 15:12
  • We cannot guess what you want to extract from this image. –  Feb 25 '21 at 16:47
  • @YvesDaoust The OP provided an example of what he/she wants. The last image, "Desired Output". – WesH Feb 25 '21 at 17:34
  • @vwertuzy I have no trouble extracting just the image screen using (replace ``;`` with linebreaks) : ``img = cv2.imread('in.png', cv2.COLOR_BGR2GRAY); out = np.zeros_like(img); reds = image_vec[:, :, 2]; _, reds = cv2.threshold(reds, 240, 255, cv2.THRESH_TOZERO); cont, _ = cv2.findContours(reds, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE); cv2.drawContours(out, cont, 0, (0, 255, 0), 2, cv2.LINE_AA); cv2.imshow("result", out); cv2.waitKey(0)`` – WesH Feb 25 '21 at 17:47
  • That does work with this image, but I am looking for a solution to get rid of the branch-like unwanted areas. The other images I am working with are much more complex and I need the current routine to filter out complex stuff. – vwertuzy Feb 25 '21 at 17:52
  • @WesH: this is not explanatory enough. –  Feb 25 '21 at 19:15
  • @vwertuzy Ok, that's why I put this in comments rather than as an answer. – WesH Feb 25 '21 at 19:45

1 Answers1

0

I was able to get rid of 'branch like' tiny and unwanted unwanted areas in a following way:(in reference to the code provided in question)

-All stuff before creating 'findSignificantContour()' remains the same

-After creating 'findSignificantContour()':

# Call findSignificantContour() 
# Create a binary image with contour filled as white and rest black
# Perform erosion on the binary image (will mostly get rid of tinier areas without messing up the actual target)
# make a call to 'findSignificantContour()' and get final contour
# Done! Mask the contour however you want 
vwertuzy
  • 171
  • 1
  • 2
  • 12