I'm currently working on an image where I have to find the box outer region. But I failed to find the white and black boxes regions.
input image:
https://i.imgur.com/gec9eP5.png
output image:
https://i.imgur.com/Giz1DAW.png
Update edit:
if I use HLS instead of HSV I can find 3 more box region but 2 is still missing.
here is new output:
https://i.imgur.com/eUqltKI.png
and here is my code:
import cv2
import numpy as np
img = cv2.imread("1.png")
imghsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_blue = np.array([0,50,0])
upper_blue = np.array([255,255,255])
mask_blue = cv2.inRange(imghsv, lower_blue, upper_blue)
_, contours, _ = cv2.findContours(mask_blue, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
im = np.copy(img)
cv2.drawContours(im, contours, -1, (0, 255, 0), 2)
cv2.imwrite("contours_blue.png", im)
The mask you're generating with
mask_blue = cv2.inRange(imghsv, lower_blue, upper_blue)
does not include the bottom row at all, so it's impossible to detect these outlines with
_, contours, _ = cv2.findContours(mask_blue, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
You could try to work with multiple masks / thresholds to account for the different color ranges and merge the detected contours.
Color channel threshold is not the optimal solution in cases when you are dealing with objects of many different colors (that are not known in advance) and with background color that is not necessarily distinctly different from all object colors. Combination of multiple thresholds/conditions could solve the job for this particular image but this same combination can fail for slightly different input, so I think this approach is generally not too good.
I think the problem is very elementary in nature so I would recommend sticking to a simple approach. For example, if you apply Sobel operator on your image, you get result like one below. The intensity of the result is weak on some borders, so I inverted the image colors to make it better visible.
There are tons of tutorials on Sobel operator on the web so I won't go into detail here. On your input image there is no noise, so the intensity outside and within the boxes is zero. I would therefore suggest masking-out all zero values. If you do contour detection after that, you will end up with two contours per square - one will be on the inner side of the border and one on the outer side of the border. If you only want to extract contours on the outer border, see how contour hirearchy works in the OpenCV documentation. If you want to have contour exactly on the border, help yourself with outer contour and erosion.
Related
I have a population of images of maps (4tb worth - 670K images total). The maps are made up of overlain images of different maps. The boundaries of each map signified by a red, blue, or grey boundry. The solution I'm trying to come up with a solution that answers these two questions:
Does the map contain red lines that are not on top of grey lines.
If the answer to 1 is yes, are there portions of the red square located inside of blue squares?
I aim to build either a CNN or a script that will answer these questions by:
Recognizing images with intersecting red/grey lines.
Recognizing images with red lines inside the blue lines.
My first step (and please feel free to criticize my approach) is to take a map and mask the blue lines therein.
#Inpaint - or Photoshop "Content Aware Fill"
im = cv2.imread('Sample2.png')
# Make mask of blue pixels - True where blue, False elsewhere
mask = np.all(im == (255, 0, 0), axis=-1)
# Inpaint white areas using Navier Strokes
result = cv2.inpaint(im,np.uint8(mask)*255,3,cv2.INPAINT_NS)
cv2.imwrite('result-NAVIER-STOKES.png',result)
This provides this output.
Next, I want to create an image containing a white background with the squares as black which I then hope to use to train a NN or write a script to identify if the squares intersect. In order to do that, I will need to further pre-process the image to look as close to this as possible (in cases where no grey square exists it would be only one square):
I've played around with masking to remove the noise and only isolate the grey and red rectangles but it seems like what I've written can only produce an output that isolates the red rectangle only:
import cv2
import numpy as np
import os
filename = 'output.PNG'
im = cv2.imread('output.PNG')
output_file = (os.path.splitext(filename)[0])
hsv = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)
lower = (0,240,160)
upper = (120,255,255)
thresh = cv2.inRange(hsv, lower, upper)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9))
clean = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15,15))
clean = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# get external contours
contours = cv2.findContours(clean, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
result1 = im.copy()
result2 = im.copy()
for c in contours:
cv2.drawContours(result1,[c],0,(0,0,0),2)
# get rotated rectangle from contour
rot_rect = cv2.minAreaRect(c)
box = cv2.boxPoints(rot_rect)
box = np.int0(box)
# draw rotated rectangle on copy of img
cv2.drawContours(result2,[box],0,(0,0,0),2)
How can I isolate the grey square as well as the red square? I know it's a challenge because the map in the background is similar in coloration to the grey line. Is there a way to isolate the grey square an perhaps change its color to something other than grey to apply an additional mask?
Also if there's a better way to approach this problem, would love to hear thoughts and suggestions.
I have an small project where I need to calculate the area of hair portions and tell which one covers greater area among two images. I have another code for hair extraction. However it is not also giving result as expected.You may have guessed already from image below. I will work on it later.
I am trying to calculate the area from contours which is giving me error like:
OpenCV(3.4.4) C:\projects\opencv-python\opencv\modules\imgproc\src\contours.cpp:195: error: (-210:Unsupported format or combination of formats) [Start]FindContours supports only CV_8UC1 images when mode != CV_RETR_FLOODFILL otherwise supports CV_32SC1 images only in function 'cvStartFindContours_Impl'
So, why is findContours not supporting my image?
Another approach:
I only need to find the area of hair portion. So, I thought of calculating area covered by all the white pixels and then subtract it from area of whole image too. In this case, I do not know how to calculate area covered by all white pixels. I thought this way because, hair color can vary, but background will always be white.
So, is this technique possible? Or please suggest some solution for above mentioned error?
My image:
My code:
import cv2
import numpy as np
img = cv2.imread("Hair.jpg")
_, contours, _ = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
c = max(contours, key = cv2.contourArea)
cv2.drawContours(img, [c], -1, (255,255, 255), -1)
area = cv2.contourArea(c)
print(area)
cv2.imshow("contour", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Your error already tells what is wrong, specifically this part:
FindContours supports only CV_8UC1 images when mode
This means that it has to be a greyscale image. You pass an image loaded with:
img = cv2.imread("Hair.jpg")
Which by default returns the image in CV_8UC3 or in simple words, BGR colorspace. Even if your image only has black and white. Solution, load as greyscale:
img = cv2.imread("Hair.jpg", cv2.IMREAD_GRAYSCALE)
Also, I notice that this is a .jpg file, which may introduce some artifacts that you may not like/want. To remove them, use threshold:
ret,thresh1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
I hope this helps you, if not, leave a comment
Update:
findContours function takes black as background and white as foreground. In your case is the other way around. But there is an easy way to solve this, just invert the image when it is being passed:
_, contours, _ = cv2.findContours(255-img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
255 is the max value the image can have, and this will turn black into white and white into black, giving you the correct contour.
So I'm trying to recognize an region that's already been defined by a bounding box. Example:
Some of the areas within these rectangles in these images are white and some are black, and most of them are completely different sizes. The only common characteristic between these images is the red rectangle:
Essentially what I'm trying to do is create a randomly generated meme bot that places a random source image in the region defined by these rectangles. I have tons of these images already with predefined areas with these red rectangles for use. I want to automate the process somehow, currently every resize shape and offset has to be defined for each template. So what I need to do is recognize the area within the rectangle and have it return the defined resize shape and offset needed to place the source image.
How should I go about this? Should I use something in OpenCV or am I going to have to train a CNN? Just really looking for a push in the right direction because I'm pretty lost as to the best approach to this problem.
I think OpenCV can do it. Below is a short example of the steps for what you need. Read the comments in the code for more details.
import cv2
import numpy as np
img = cv2.imread("1.jpg")
#STEP1: get only red color (or the bounding box color) in the image
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# define range of red color in HSV
lower_red = np.array([0,50,50])
upper_red = np.array([0,255,255])
# Threshold the HSV image to get only blue colors
mask = cv2.inRange(hsv, lower_red, upper_red)
red_only = cv2.bitwise_and(img,img, mask= mask)
#STEP2: find contour
gray_img = cv2.cvtColor(red_only,cv2.COLOR_BGR2GRAY)
_,thresh = cv2.threshold(gray_img,1,255,cv2.THRESH_BINARY)
_,contours,_ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#max contour in the image is the box you want
areas = [cv2.contourArea(c) for c in contours]
sorted_areas = np.sort(areas)
cnt=contours[areas.index(sorted_areas[-1])]
r = cv2.boundingRect(cnt)
cv2.rectangle(img,(r[0],r[1]),(r[0]+r[2],r[1]+r[3]),(0,255,0),3)
cv2.imshow("img",img)
cv2.imshow("red_only",red_only)
cv2.imshow("thresh",thresh)
cv2.waitKey()
cv2.destroyAllWindows()
I need to assign different colors to different biological cells in an image.
To be more specific, the image is only black and white (so a matrix of 0 and 255 only). The content of the cells (excluding boundaries) is represented by white color, whereas cell boundaries are represented by black color. Each cell is enclosed by some cell boundaries or image edges. I hope to assign different colors to different cells, such that I can immediately tell which cells I am currently at by simply looking at the value of its entry.
Edit: It is biological cells. I have found something similar online:
Source: http://brainiac2.mit.edu/isbi_challenge/
A simple approach comes to mind:
Threshold the image to binarize it, since the one you provided contains more than just 0s and 255s.
Find all the contours and organize them into a two-level hierarchy. This can be accomplished by calling cv2.threshold with the flag RETR_CCOMP:
At the top level, there are external boundaries of the components. At the second level, there are boundaries of the holes. If there is another contour inside a hole of a connected component, it is still put at the top level.
N.B.: "Components" are the white parts of image, "holes" are the black parts.
Iterate over contours. For each contour of a component (since such contours are placed at the top level of the hierarchy, they have no parent contour) draw the contour polygon filled with a random colour.
Sample Code:
import cv2
import numpy as np
img = cv2.imread('cells.png', cv2.IMREAD_GRAYSCALE)
thresh = cv2.threshold(img, thresh=128, maxval=255, type=cv2.THRESH_BINARY)[1]
_, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
output = cv2.cvtColor(thresh, cv2.COLOR_GRAY2BGR)
for i, contour in enumerate(contours):
if hierarchy[0][i][3] == -1:
colour = cv2.randu(np.zeros(3, np.uint8), 0, 256)
cv2.drawContours(output, contours, i, colour.tolist(), -1)
cv2.imwrite('cells_colour.png', output)
Result:
How can I plot/save the inner contours of an image using python opencv?
I know how can I get the largest contour, I want to save it and the inside holes, which are contours as well.
Original Image:
import numpy as np
import cv2
from matplotlib import pyplot as plt
rgb = cv2.imread('MIL_NPGERBV2.png')
grayImg = cv2.imread('MIL_NPGERBV2.png', cv2.CV_LOAD_IMAGE_GRAYSCALE)
#to apply properly contour algorithm we need convert to binary
(thresh, bwImage) = cv2.threshold(image, 128, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
img1 = rgb.copy()
img2 = rgb.copy()
contours, hierarchy = cv2.findContours(bwImage,cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
#show all contours
cv2.drawContours(img1, contours, -1, (0,255,0), 2)
out = np.hstack([img1])
cv2.imshow('Output', out)
cv2.waitKey(0)
cv2.destroyAllWindows()
OpenCV's findContours method can give you the internal contours if asked politely.
It's one of the mode options of cv2.findContours() :
CV_RETR_CCOMP retrieves all of the contours and organizes them into a
two-level hierarchy. At the top level, there are external boundaries
of the components. At the second level, there are boundaries of the
holes. If there is another contour inside a hole of a connected
component, it is still put at the top level.
So asking a nice
myContours = cv2.findContours(myImg,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
should give you a sort of nested object array, with the first item giving the useless outlines and the rest with the insides.
I'll try to update the answer to include a MCVE later on (but only if you update the question to include one too ^^)