Luud van Keulen
Luud van Keulen

Reputation: 1254

Find dominant color on contour opencv

I am trying to find the dominant color inside a contour (black or white). I am using OpenCV to read an image and extract white on black images. This is what I got so far: enter image description here

The green outline is the contour, the blue lines the bounding box. So I this instance I am trying to extract the numbers 87575220 but as you can see it also recognizes some random artifacts and for instance the letter G. I think the solution would be to find the dominant colour inside of the contours and that colour should be close to white. I don't have any idea how to do this though.

This the code I have at the moment:

import argparse
import cv2
import imutils
import numpy as np

parser = argparse.ArgumentParser()
parser.add_argument("--image", "-i", required=True, help="Image to detect blobs from")
args = vars(parser.parse_args())

image = cv2.imread(args["image"])
image = imutils.resize(image, width=1200)
grey = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
(minVal, maxVal, minLoc, maxLoc) = cv2.minMaxLoc(grey)
maxval_10 = maxVal * 0.5
ret, threshold = cv2.threshold(grey, maxval_10, 255, cv2.THRESH_BINARY)
canny = cv2.Canny(grey, 200, 250)
lines = cv2.HoughLines(canny, 1, np.pi / 180, 140)

print(maxVal)

theta_min = 60 * np.pi / 180.
theta_max = 120 * np.pi / 180.0
theta_avr = 0
theta_deg = 0
filteredLines = []
for rho, theta in lines[0]:
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a * rho
    y0 = b * rho
    x1 = int(x0 + 1000 * (-b))
    y1 = int(y0 + 1000 * (a))
    x2 = int(x0 - 1000 * (-b))
    y2 = int(y0 - 1000 * (a))

    cv2.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2)

    if theta_min <= theta <= theta_max:
        filteredLines.append(theta)
        theta_avr += theta

if len(filteredLines) > 0:
    theta_avr /= len(filteredLines)
    theta_deg = (theta_avr / np.pi * 180) - 90
else:
    print("Failed to detect skew")

image = imutils.rotate(image, theta_deg)
canny = imutils.rotate(canny, theta_deg)

im2, contours, hierarchy = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# cv2.drawContours(image, contours, -1, (0, 255, 0), 1)
cv2.imshow('Contours', im2)

boundingBoxes = []
filteredContours = []

for cnt in contours:
    (x, y, w, h) = cv2.boundingRect(cnt)
    if (h > 20 and h < 90 and w > 5 and w < h):
        if cv2.contourArea(cnt, True) <= 0:
            boundingBoxes.append((x, y, w, h))
            filteredContours.append(cnt)

for x, y, w, h in boundingBoxes:
    cv2.rectangle(image, (x, y), (x + w, y + h), (255, 0, 0), 2)

cv2.drawContours(image, filteredContours, -1, (0, 255, 0), 1)

cv2.imshow('Image', image)
cv2.imshow('Edges', canny)
cv2.imshow('Threshold', threshold)

cv2.waitKey(0)
cv2.destroyAllWindows()

This is the original picture: enter image description here

Upvotes: 0

Views: 3510

Answers (3)

kavko
kavko

Reputation: 2831

I would try to make a ROI before I start searching for numbers. You have not give the original image so this example is made with the image you posted (with boxes and contours allready drawn). Should aslo work with the original though. Steps are written in the example code. Hope it helps. Cheers!

Example code:

import cv2
import numpy as np

# Read the image and make a copy then create a blank mask
img = cv2.imread('dominant.jpg')
img2 = img.copy()
h,w = img.shape[:2]
mask = np.zeros((h,w), np.uint8)

# Transform to gray colorspace and perform histogram equalization
gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
equ = cv2.equalizeHist(gray)

# Transform all pixels above thershold to white
black = np.where(equ>10)
img2[black[0], black[1], :] = [255, 255, 255]

# Transform to gray colorspace and make a thershold then dilate the thershold
gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
kernel = np.ones((15,15),np.uint8)
dilation = cv2.dilate(thresh,kernel,iterations = 1)

# Search for contours and select the biggest one and draw it on mask
_, contours, hierarchy = cv2.findContours(dilation,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)
cv2.drawContours(mask, [cnt], 0, 255, -1)

# Perform a bitwise operation
res = cv2.bitwise_and(img, img, mask=mask)

# Display the ROI
cv2.imshow('img', res)

Result:

enter image description here

Upvotes: 3

m33n
m33n

Reputation: 1751

Colors and mean do not match well due to color space properties. I would create an histogram and select the most frequent one (some color down sampling could be applied too)

Upvotes: 0

mspiller
mspiller

Reputation: 3839

You could create a mask out of each contour:

mask = np.zeros(image.shape, dtype="uint8")
cv2.drawContours(mask, [cnt], -1, 255, -1)

and then calculate the mean value of all pixels inside of the mask:

mean = cv2.mean(image, mask=mask)

and then check whether mean is close enough to white

Upvotes: 2

Related Questions