I'm trying to segment each charm item in this collective image.
The shapes are irregular and inconsistent:
https://i.imgur.com/sf8nOau.jpg
I have another image where there is some consistency with the top row of items, but ideally I'd be able to process and brake up all items in one go:
http://i.imgur.com/WiiYBay.jpg
I have no experience with opencv so I'm just looking for best possible tool or approach to take. I've read about background subtraction as well as color clustering, but I'm not sure about those either.
Any ideas on how to best approach this? Thanks.
Code
import cv2
import numpy as np
im=cv2.imread('so1.jpg')
gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
kernel = np.ones((3,3),np.uint8)
res = cv2.dilate(thresh,kernel,iterations = 1)
res = cv2.erode(res,kernel,iterations = 1)
res = cv2.dilate(res,kernel,iterations = 1)
cv2.imshow('thresh',res)
_,contours, hierarchy = cv2.findContours(res.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(im,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imshow('image',im)
cv2.waitKey(0)
cv2.destroyAllWindows()
Output
Now having the contours you can crop them out
Code Update
import cv2
import numpy as np
im=cv2.imread('so.jpg')
gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
kernel = np.ones((3,3),np.uint8)
res = cv2.dilate(thresh,kernel,iterations = 1)
res = cv2.erode(res,kernel,iterations = 1)
res = cv2.dilate(res,kernel,iterations = 8)
cv2.imshow('thresh',res)
_,contours, hierarchy = cv2.findContours(res.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
count=0
for cnt in contours:
blank=np.zeros(im.shape,dtype=np.uint8)
x,y,w,h = cv2.boundingRect(cnt)
epsilon = 0.001*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)
cv2.fillConvexPoly(blank,approx,(255, 255, 255))
masked_image = cv2.bitwise_and(im, blank)
cv2.imwrite('results_so/im'+str(count)+'.jpg',masked_image[y:y+h,x:x+w])
count+=1
cv2.imshow('image',im)
cv2.waitKey(0)
cv2.destroyAllWindows()
results
good one
bad one
Some small noises are also detected as objects you can eliminate those by only taking contours whose area is greater than a certain value .
Related
I was given a school project for recognizing various kinds of CAPTCHA, and I had some difficulties with its implementation.
Images of this type will be fed into input ,,.
I handle them with the following code:
import cv2
import pytesseract
# load image
fname = 'picture.png'
im = cv2.imread(fname,cv2.COLOR_RGB2GRAY)
pytesseract.pytesseract.tesseract_cmd = r'C:\Tesseract-OCR\tesseract.exe'
im = im[0:90, 35:150]
im = cv2.blur(im,(3,3))
im = cv2.threshold(im, 223 , 250, cv2.THRESH_BINARY)
im = im[1]
cv2.imshow('',im)
cv2.waitKey(0)
After all processing, the image looks like this: And at this point, I have a problem, how can I modify the image to good readability by the computer, so that instead of the wrong TAREQ. he would display the 7TXB6Q
I am trying to display text from an image with the pytesseract library as follows
data = pytesseract.image_to_string(im, lang='eng', config='--psm 6 --oem 3 -c tessedit_char_whitelist= ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')
print(data)
I am writing here hoping to get valuable advice (perhaps you know the most suitable way to get text from a picture or process the image pinned above). Peace for everyone)
More images
You can try finding countours and eliminating those which have small areas. This preprocessing operation should increase the success of OCR result.
Before:
import cv2 as cv
import numpy as np
# your thresholded image im
bw = cv.imread('bw.png', cv.IMREAD_GRAYSCALE)
_, cnts, _ = cv.findContours(bw, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
# remove the largest contour which is background
cnts = np.array(cnts[1:], dtype=object)
areas = np.array(list(map(cv.contourArea, cnts)))
thr = 35
thr_cnts = cnts[areas > thr]
disp_img = 255 * np.ones(bw.shape, dtype=np.uint8)
disp_img = cv.drawContours(disp_img, thr_cnts, -1, (0, 0, 0), cv.FILLED)
disp_img = cv.bitwise_or(disp_img, bw)
cv.imshow('result', disp_img)
cv.waitKey()
cv.destroyAllWindows()
cv.imwrite('result.png', disp_img)
Result:
Edit: It seems that merging the two codes did not give the same result. This is the full code from the beginning to the end.
Input:
import cv2 as cv
import numpy as np
# load image
fname = 'im.png'
im = cv.imread(fname, cv.IMREAD_GRAYSCALE)
# crop
im = im[0:90, 35:150]
# blurring is essential for denoising
im = cv.blur(im, (3,3))
thr = 219
# the binary threshold value is very important
# using 220 instead of 219 causes loss of a letter
# because it touches to the bottom edge and gets involved in the background
_, im = cv.threshold(im, thr, 255, cv.THRESH_BINARY)
cv.imshow('', im)
cv.waitKey(0)
Thresholded:
# binary image
bw = np.copy(im)
# find contours and corresponding areas
_, cnts, _ = cv.findContours(bw, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)
cnts = np.array(cnts, dtype=object)
areas = np.array(list(map(cv.contourArea, cnts)))
thr = 35
# eliminate contours that are smaller than threshold
# also remove the largest contour which is background
thr_cnts = cnts[np.logical_and(areas > thr, areas != np.max(areas))]
# draw the remaining contours
disp_img = 255 * np.ones(bw.shape, dtype=np.uint8)
disp_img = cv.drawContours(disp_img, thr_cnts, -1, (0, 0, 0), cv.FILLED)
disp_img = cv.bitwise_or(disp_img, bw)
cv.imshow('', disp_img)
cv.waitKey()
cv.destroyAllWindows()
Result:
I'm trying to get the contours of a portion of an image but I keep getting an error when I call the cv.findContour image. To find the object in the image I first try to extract the colour of the object, which is red, then I try to find the contours on the detected objects.
import cv2 as cv
import numpy as np
def main():
image_find_goal = "image.jpg"
kernel = np.ones((5,5),np.uint8)
#findGoal(image_find_goal)
img1 = cv.imread(image_find_goal)
img = cv.resize(img1,(700,700))
hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV)
#range for red color
lower_r = np.array([160,100,100])
upper_r = np.array([179,255,255])
mask = cv.inRange(hsv,lower_r,upper_r)
res = cv.bitwise_and(img,img,mask=mask)
_,thresh = cv.threshold(res,125,255,cv.THRESH_BINARY)
dilate = cv.dilate(thresh,None,iterations=1)
contours, hierarchy = cv.findContours(dilate,cv.RETR_TREE,cv.CHAIN_APPROX_NONE)
for cnt in contours:
epsilon = 0.1*cv.arcLength(cnt,True)
approx = cv.approxPolyDP(cnt,epsilon,True)
if len(approx) == 4:
cv.drawContours(img,cnt,-1,(60,255,255),4)
cv.imshow('OBSTACLES',img)
cv.waitKey(0)
cv.destroyWindow(img)
The error I get is:
contours, hierarchy =
cv.findContours(dilate,cv.RETR_TREE,cv.CHAIN_APPROX_NONE) cv2.error:
OpenCV(4.4.0)
/tmp/pip-req-build-sw_3pm_8/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'
I think this snippet solves your issue. Since you are interested in Contours I would apply the Canny Edge Detection before dilating. This move will fix also the CV Type error you are getting
import cv2 as cv
import numpy as np
image_find_goal = "image.jpg"
#findGoal(image_find_goal)
img1 = cv.imread(image_find_goal)
img = cv.resize(img1,(700,700))
hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV)
#range for red color
lower_r = np.array([160,100,100])
upper_r = np.array([179,255,255])
mask = cv.inRange(hsv,lower_r,upper_r)
res = cv.bitwise_and(img,img,mask=mask)
_,thresh = cv.threshold(res,125,255,cv.THRESH_BINARY)
# check which are the best canny threshold values for your image
imgCanny = cv.Canny(thresh, 180, 180)
dilate = cv.dilate(imgCanny, None, iterations = 1)
# cv.imshow("dilate", dilate)
# cv.waitKey()
contours, hierarchy = cv.findContours(dilate,cv.RETR_TREE,cv.CHAIN_APPROX_NONE)
for cnt in contours:
epsilon = 0.1*cv.arcLength(cnt,True)
approx = cv.approxPolyDP(cnt,epsilon,True)
if len(approx) == 4:
cv.drawContours(img,cnt,-1,(60,255,255),4)
cv.imshow('OBSTACLES',img)
cv.waitKey(0)
cv.destroyAllWindows()
I have the following image:
I used the following code to outline all the round blobs in this image using this code:
import numpy as np
import cv2
im = cv2.imread('im.jpg')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,200,255,0)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(im,contours,-1,(0,0,255),1)
#(B,G,R)
cv2.imshow('image',im)
cv2.waitKey(0)
cv2.destroyAllWindows()
And it produces this image:
Which is great for the first step. But I'm having a really hard time drawing a different colored outline for the blue colored blobs. I tried using multiple contours:
import numpy as np
import cv2
im = cv2.imread('im.jpg')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,200,255,0)
ret, thresh2 = cv2.threshold(imgray,130,255,0)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
contours2, hierarchy2 = cv2.findContours(thresh2,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(im,contours,-1,(0,0,255),1)
cv2.drawContours(im,contours2,-1,(0,255,0),1)
#(B,G,R)
cv2.imshow('image',im)
cv2.waitKey(0)
cv2.destroyAllWindows()
and the image comes out like this:
The first problem with this approach is the fact that it doesn't accurately only outline the blue blobs. Moreover, the sensitivity rating in the threshold function would have to be modified for each image depending on lighting, etc. Is there a smoother way to do this?
Based on this:
import cv2
import numpy as np
img = cv2.imread("bluepink.jpg")
imghsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_blue = np.array([110,50,50])
upper_blue = np.array([130,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), 1)
cv2.imwrite("contours_blue.png", im)
Not ideal, but there seem to be no false positives. And you can probably improve it by adding another near-black color range (as really dark color is only present inside those bluish blobs). Maybe with some additional dilate-eroding, it never hurts to dilate-erode.
I'm having some images where I want to filter out the black areas using OpenCV with python. The problem is that these images comes from satellite footage and have no clear borders.
In the images added above, the first is the original image while the second is a blurred version, i'm interested in the small dark area in the middle. Ultimately I want to have this area marked as a singe contour area.
My main problem seems to be that when I use the OpenCV findContours function, I get a lot of small contours instead of one (or two) big ones.
I'm fairly new to OpenCV so any help would be appreciated!
Here are just some rough results I have been able to obtain with a simple pipeline:
The code is fairly self-explanatory too
import cv2
import numpy as np
def nothing(x):
pass
cv2.namedWindow('image')
cv2.createTrackbar('high','image',0,255,nothing)
cv2.createTrackbar('low','image',0,255,nothing)
cv2.namedWindow('Edges')
while(1):
image = cv2.imread("PATH TO IMAGE HERE")
imgray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
high = cv2.getTrackbarPos('high', 'image')
low = cv2.getTrackbarPos('low', 'image')
edges = cv2.Canny(imgray, low, high)
kernel = np.ones((8, 8), np.uint8)
closing = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)
cv2.imshow('Edges', closing)
ret,thresh = cv2.threshold(closing,low,high,0)
im2, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
largest_area = 0
largest_contour_index = 0
counter = 0
for i in contours:
area = cv2.contourArea(i)
if (area > largest_area):
largest_area = area
largest_contour_index = counter
counter = counter + 1
cv2.drawContours(image, contours, largest_contour_index, (0,255,0), 1)
cv2.imshow('image', image)
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
The pipeline is as follows:
Read smooth image
Convert to grayscale
Apply the morphological operation closing (8x8 mask)
Find contours
Find the largest contour (area wise)
I'm trying to to get the number of correct answers given a sample of the model answer and an answer sheet example so i'm using cv2.bitwise_and function then i'm making erosion and dilation for the resulting image to count the objects that represent the correct answers but it's not working well.
Here is two examples images that i'm using:
That is the result:
so it detect 3 circles instead of 2. I tried to change the number of iterations in erosion and dilation and change the shape of the StructuringElement but still getting that wrong answers.
Here is my code:
import numpy as np
import cv2
from PIL import Image
img1 = cv2.imread("model_c3.png")
img2 = cv2.imread("ex_c3.png")
retval1, img1 = cv2.threshold(img1,225,255,cv2.THRESH_BINARY_INV)
retval2, img2 = cv2.threshold(img2,225,255,cv2.THRESH_BINARY_INV)
img2gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
img1gray = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
mask = cv2.bitwise_and(img1gray, img2gray,img2)
el = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
e = cv2.erode(mask, el, iterations=2)
d = cv2.dilate(e, el, iterations=7)
im, contours, hierarchy = cv2.findContours(
d,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE
)
centers = []
radii = []
for contour in contours:
br = cv2.boundingRect(contour)
m = cv2.moments(contour)
center = (int(m['m10'] / m['m00']), int(m['m01'] / m['m00']))
centers.append(center)
print("There are {} circles".format(len(centers)))
cv2.imshow('result.png',d)
cv2.waitKey(0)
cv2.destroyAllWindows()
I changed a few things in your code.
It doesn't make much sense to threshold the RGB image and then convert it to gray.
Instead of ANDing both images and then do morphological operations to clean the result you should simply prepare both images for a proper AND operation. Make the circles in one image bigger and in the other image smaller. That way you also compensate the small shift you have between both images (the reason for all the artifacts in your AND result).
I'm not sure if you fully understand the functions you use because you use them in a weird manner with strange parameters...
Please note that I just changed a few things in your code to make it work. This is not intended to be a perfect solution.
import numpy as np
import cv2
#from PIL import Image
img1 = cv2.imread("d://1.png")
img2 = cv2.imread("d://2.png")
img2gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
img1gray = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
retval1, img1bin = cv2.threshold(img1gray,128,255,cv2.THRESH_BINARY_INV)
retval2, img2bin = cv2.threshold(img2gray,128,255,cv2.THRESH_BINARY_INV)
el = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
e = cv2.erode(img1bin, el, iterations=2)
cv2.imshow("e", e)
d = cv2.dilate(img2bin, el, iterations=7)
result = cv2.bitwise_and(e, d)
cv2.imshow("d", d)
im, contours, hierarchy = cv2.findContours(
result,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE
)
centers = []
radii = []
for contour in contours:
br = cv2.boundingRect(contour)
m = cv2.moments(contour)
center = (int(m['m10'] / m['m00']), int(m['m01'] / m['m00']))
centers.append(center)
print("There are {} circles".format(len(centers)))
cv2.imshow('result',result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Its seems the false positive blob is smaller than the other blobs. Filter the contours by area...
area = cv2.contourArea(contour)
http://docs.opencv.org/trunk/dd/d49/tutorial_py_contour_features.html