Extract polygons by color from mask image - python

I would like to extract the Polygon Shape by color, if it's possible the corner coordinates,
Currently i'm trying with open-cv find contourns, but it's very precise i'm getting a lot of coordinates. what i want it's just the corner points to make the polygon by color.
img = cv2.imread('mask_ZT76_39_A_2_8.png', 0)
img = cv2.medianBlur(img, 5)
thresh = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv2.THRESH_BINARY,11,2)
image, contours, hierarchy = cv2.findContours(thresh.copy(),cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(image, contours, -1, (0,255,0), 3)
approx_=[]
for i in range(len(contours[0])):
epsilon = cv2.arcLength(contours[0][i],True)
approx = cv2.approxPolyDP(contours[0][i],epsilon,True)
Thanks.

Related

Rounded rectangle perspective transform

I have an image where I'm trying to export and do transform as rectangle (no matter in base photo). I have done with finding largest contour in image using mask (trained in pytorch). In variable pr_mask I have stored mask from network. After that from that mask I can get extract contours of my image. So from this image:
I can extract contours like this:
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray,(5,5),5)
canny = cv2.Canny(blur, 30, 80, 3)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
flag, thresh = cv2.threshold(canny, 80, 255, cv2.THRESH_BINARY)
kernel = np.ones((10,10),np.uint8)
dilation = cv2.dilate(canny,kernel,iterations = 1)
ret,thresh = cv2.threshold(dilation, 200, 255, 0)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
After that I fetch largest contour and then trying to extract contours:
approx = np.ones((10,10),np.uint8)
d = 0
while (len(approx)>4):
d=d+1
approx = cv2.approxPolyDP(largestcontour,d, True)
cv2.drawContours(imagebase, [largestcontour], -1, (0,255,0), 3)
hull = []
hull.append(cv2.convexHull(largestcontour, False))
cv2.drawContours(imagebase, hull, -1, (0,0,255), 3)
After that I get tthis result:
Is it possible to extract square (rectangle) which will transform my image to exact square without rounded corners and to fit whole image?
I'm trying to find a way how to solve this "perspective" issue and align element to specific position. My image is taken by cameras, so it can be in different positions, where boundingRectangle will not work ... any suggestions?

Python image recognition going around objects to create quadrilateral polygons

I have an image and I want the image recognition to pick up quadrilaterals/trapezoid-like polygons from the image. What I want the bounding box to do is to create boxes around areas.
Original image
Contours drawn on image
Problem area
What I want it to look like at the top
From the image, it goes around the specific objects in the picture but I want it to skip those and just create boxes instead. Around the bottom of the image, it is doing what I want it to do, create boxes around the 'blocks' in the image.
import cv2
import numpy as np
# read image
img = cv2.imread("map3.png")
imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
_,thresh = cv2.threshold(imgray, 110, 255,
cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
area = cv2.contourArea(contour)
if area > 500:
approx = cv2.approxPolyDP(contour, .002*cv2.arcLength(contour, True), True)
if len(approx) >= 4:
cv2.drawContours(img, [approx], -1, (0, 0, 255), 2)
cv2.imshow("IMAGE", img)
cv2.waitKey(0)

How to perform image segmentation of apples using Python OpenCV?

I have pictures of apple slices that have been soaked in an iodine solution. The goal is to segment the apples into individual regions of interest and evaluate the starch level of each one. This is for a school project so my goal is to test different methods of segmentation and objectively find the best solution whether it be a single technique or a combination of multiple techniques.
The problem is that so far I have only come close on one method. That method is using HoughCircles. I had originally planned to use the Watershed method, Morphological operations, or simple thresholding. This plan failed when I couldn't modify any of them to work.
The original images look similar to this, with varying shades of darkness of the apple
I've tried removing the background tray using cv2.inRange with HSV values, but it doesn't work well with darker apples.
This is what the HoughCircles produced on the original image with a grayscale and median blur applied, also with an attempted mask of the tray.
Any advice or direction on where to look next would be greatly appreciated. I can supply the code I'm using if that will help.
Thank you!
EDIT 1 : Adding some code and clarifying the question
Thank you for the responses. My real question is are there any other methods of segmentation that this scenario lends itself well to? I would like to try a couple different methods and compare results on a large set of photos. My next in line to try is using k-means segmentation. Also I'll add some code below to show what I've tried so far.
HSV COLOR FILTERING
import cv2
import numpy as np
# Load image
image = cv2.imread('ApplePic.jpg')
# Set minimum and max HSV values to display
lower = np.array([0, 0, 0])
upper = np.array([105, 200, 255])
# Create HSV Image and threshold into a range.
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower, upper)
maskedImage = cv2.bitwise_and(image, image, mask=mask)
# Show Image
cv2.imshow('HSV Mask', image)
cv2.waitKey(0)
HoughCircles
# import the necessary packages
import numpy as np
import argparse
import cv2
import os
directory = os.fsencode('Photos\\Sample N 100')
for file in os.listdir(directory):
filename = os.fsdecode(file)
if filename.endswith('.jpg'):
# Load the image
image = cv2.imread('Photos\\Sample N 100\\' + filename)
# Calculate scale
scale_factor = 800 / image.shape[0]
width = int(image.shape[1] * scale_factor)
height = 800
dimension = (width, height)
min_radius = int((width / 10) * .8)
max_radius = int((width / 10) * 1.2)
# Resize image
image = cv2.resize(image, dimension, interpolation=cv2.INTER_AREA)
# Copy Image
output = image.copy()
# Grayscale Image
gray = cv2.medianBlur(cv2.cvtColor(image, cv2.COLOR_BGR2GRAY), 5)
# Detect circles in image
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, min_radius * 2, 4, 60, 20, min_radius, max_radius)
# ensure at least some circles were found
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
# loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circles:
# draw the circle in the output image, then draw a rectangle
# corresponding to the center of the circle
cv2.circle(output, (x, y), r, (0, 255, 0), 4)
cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
cv2.putText(output, '(' + str(x) + ',' + str(y) + ',' + str(r) + ')', (x, y),
cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, 255)
# show the output image
cv2.imshow("output", np.hstack([image, output, maskedImage]))
cv2.waitKey(0)
continue
else:
continue
An alternative approach to segmenting the apples is to perform Kmeans color segmentation before thresholding then using contour filtering to isolate the apple objects:
Apply Kmeans color segmentation. We load the image, resize smaller using imutils.resize then apply Kmeans color segmentation. Depending on the number of clusters, we can segment the image into the desired number of colors.
Obtain binary image. Next we convert to grayscale, Gaussian blur and Otsu's threshold.
Filter using contour approximation. We filter out non-circle contours and small noise.
Morphological operations. We perform a morph close to fill adjacent contours
Draw minimum enclosing circles using contour area as filter. We find contours and draw the approximated circles. For this we use two sections, one where there was a good threshold and another where we approximate the radius.
Kmeans color quantization with clusters=3 and binary image
Morph close and result
The "good" contours that had the radius automatically calculated using cv2.minEnclosingCircle is highlighted in green while the approximated contours are highlighted in teal. These approximated contours were not segmented well from the thresholding process so we average the "good" contours radius and use that to draw the circle.
Code
import cv2
import numpy as np
import imutils
# Kmeans color segmentation
def kmeans_color_quantization(image, clusters=8, rounds=1):
h, w = image.shape[:2]
samples = np.zeros([h*w,3], dtype=np.float32)
count = 0
for x in range(h):
for y in range(w):
samples[count] = image[x][y]
count += 1
compactness, labels, centers = cv2.kmeans(samples,
clusters,
None,
(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10000, 0.0001),
rounds,
cv2.KMEANS_RANDOM_CENTERS)
centers = np.uint8(centers)
res = centers[labels.flatten()]
return res.reshape((image.shape))
# Load image, resize smaller, perform kmeans, grayscale
# Apply Gaussian blur, Otsu's threshold
image = cv2.imread('1.jpg')
image = imutils.resize(image, width=600)
kmeans = kmeans_color_quantization(image, clusters=3)
gray = cv2.cvtColor(kmeans, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (9,9), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
# Filter out contours not circle
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.04 * peri, True)
if len(approx) < 4:
cv2.drawContours(thresh, [c], -1, 0, -1)
# Morph close
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=3)
# Find contours and draw minimum enclosing circles
# using contour area as filter
approximated_radius = 63
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
area = cv2.contourArea(c)
x,y,w,h = cv2.boundingRect(c)
# Large circles
if area > 6000 and area < 15000:
((x, y), r) = cv2.minEnclosingCircle(c)
cv2.circle(image, (int(x), int(y)), int(r), (36, 255, 12), 2)
# Small circles
elif area > 1000 and area < 6000:
((x, y), r) = cv2.minEnclosingCircle(c)
cv2.circle(image, (int(x), int(y)), approximated_radius, (200, 255, 12), 2)
cv2.imshow('kmeans', kmeans)
cv2.imshow('thresh', thresh)
cv2.imshow('close', close)
cv2.imshow('image', image)
cv2.waitKey()

drawcontours on different position

I would like to draw contours in the middle of a blank image. I don't know how to set the contour location to be drawn. this is the line I use.
cv2.drawContours(bimg, c, -1, 255, 1)
bimg is the blank image, c is the contour I've extracted from an image. I believe I can move the contour by manipulating c, but I don't understand how c is written actually
You can look at the official documentation of opencv for contours. This code can be used to find contours of an image threshold and draw them on a white background with red color.
img = cv2.imread('image_name.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_,thresh1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
_, cnts, _ = cv2.findContours(mask,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
bgr = np.ones((img.shape[0], img.shape[1]), dtype= 'uint8')*255 #this creates a white background of the same size of input shape
cv2.drawContours(bgr, cnts, -1, (0,0,255), 1)

Highlight all possible circles ( Bubble sheet choices ) in opencv

I am working on automatically correcting a bubble-sheet tests that are scanned.
Currently, I can extract the solutions part of the sheet and fix its rotation.
So I have this image.
The output image with detected contours
Running the following code yields in the output image
def get_answers(image):
display_normal("Just image",image)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurry = cv2.GaussianBlur(gray, (3, 3), 1)
thresh = cv2.threshold(blurry, 225, 255,
cv2.THRESH_BINARY_INV)[1]
display_normal("Binary", thresh)
# find contours in the thresholded image, then initialize
# the list of contours that correspond to questions
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[1]
questionCnts = []
# loop over the contours
for c in cnts:
# compute the bounding box of the contour, then use the
# bounding box to derive the aspect ratio
(x, y, w, h) = cv2.boundingRect(c)
ar = w / float(h)
# in order to label the contour as a question, region
# should be sufficiently wide, sufficiently tall, and
# have an aspect ratio approximately equal to 1
if w >= 18 and h >= 18 and 0.9 <= ar and ar <= 1.2:
questionCnts.append(c)
cv2.drawContours(image, questionCnts, -1, (255, 0, 0), 1)
display_normal("Image with contours",image.copy())
if(questionCnts < 45*4):
raise Exception("Didn't found all possible answers")
Here is the problem : I convert the input image to binary and try to find contours that looks like a circle, but I can't find the whole possible 45*4 choices.. I fail to detect some of these circles..
So is there any better idea/algorithm to do this specific task ?
You could have tried using adaptive threshold:
adapt_thresh = cv2.adaptiveThreshold(equ, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)
cv2.imshow('adapt_thresh.jpg', adapt_thresh)
(I resized the original image to keep it smaller)
UPDATE:
Another approach that I just performed.......
I equalized the gray scale image using histogram equalization:
equalized_img = cv2.equalizeHist(gray)
cv2.imshow('Equalized Image.jpg', equalized_img )
I then obtained the median of the equalized image using np.median(equalized_img) and applied a binary threshold by selecting all pixel values below [0.6 * median]
ret, thresh = cv2.threshold(equalized_img, lower, 255, 1)
cv2.imwrite("Final Image.jpg", thresh)
Now you can go ahead and find your desired contours on this image.
Hope it helps .. :)

Categories