colour contour on grayscale image - python

I made a simple code to find the contour of an object in an image.
img = cv2.imread(file_path, cv2.IMREAD_GRAYSCALE)
blur = cv2.GaussianBlur(img_enhanced,(25,25),0) # apply blur for contour
ret, binary = cv2.threshold(blur,1,255,cv2.THRESH_BINARY) # apply threshold to blur image
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # find countour
obj_index = contours.index(max(contours, key=len)) # find index of largest object
contour_img = cv2.drawContours(img, contours, obj_index, (0,255,0), 3) # draw coutour on original image
plt.imshow(contour_img)
plt.show()
The original image is already grayscale but anyway I applied cv2.IMREAD_GRAYSCALE when I import the image.
And I thought if I apply the grayscale image when I 'Draw' contour, with below syntax,
contour_img = cv2.drawContours(img, contours, obj_index, (0,255,0), 3)
I can have a grayscale image with colored contour, but it shows
weird colored image.
How can I draw a colored contour line on the grayscale image?
Thanks in advance
image sample: black background and white object

Based on my comment:
Try something like this and tell me if it works or not
Edit: I have changed the threshold range. cv2.threshold(blur,25,255,cv2.THRESH_BINARY)
img = cv2.imread(file_path, 1)
print(img.shape) # this should give you (img_h, img_w, 3)
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(img2,(25,25),0) # apply blur for contour
ret, binary = cv2.threshold(blur,25,255,cv2.THRESH_BINARY) # apply threshold to blur image
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # find countour
obj_index = contours.index(max(contours, key=len)) # find index of largest object
contour_img = cv2.drawContours(img, contours, obj_index, (0,255,0), 3) # draw coutour on original image
plt.imshow(contour_img, cmap='gray')
plt.show()
plt.hist(blur.ravel(), bins=50)

Related

How to remove bell curved shape from image using opencv

I hope you are doing well. I want to remove the bell-curved shape in the image. I have used OpenCV. I have implemented the following code which detects the curve shape, Now How can remove that curve shape and save the new image in the folder.
Input Image 1
I want to remove the area shown in the image below
Area i want to remove
import cv2
import numpy as np
# load image as grayscale
cell1 = cv2.imread("/content/savedImage.jpg",0)
# threshold image
ret,thresh_binary = cv2.threshold(cell1,107,255,cv2.THRESH_BINARY)
# findcontours
contours, hierarchy = cv2.findContours(image =thresh_binary , mode = cv2.RETR_TREE,method = cv2.CHAIN_APPROX_SIMPLE)
# create an empty mask
mask = np.zeros(cell1.shape[:2],dtype=np.uint8)
# loop through the contours
for i,cnt in enumerate(contours):
# if the contour has no other contours inside of it
if hierarchy[0][i][2] == -1 :
# if the size of the contour is greater than a threshold
if cv2.contourArea(cnt) > 10000:
cv2.drawContours(mask,[cnt], 0, (255), -1)
fig, ax = plt.subplots(1,2)
ax[0].imshow(cell1,'gray');
ax[1].imshow(mask,'gray');
Ouput Image after the above Code
How can I process to remove that curve shape?
Here is one way to do that in Python/OpenCV.
Get the external contours. Then find the one that has the largest w/h aspect ratio. Draw that contour filled on a black background as a mask. Dilate it a little. Then invert it and use it to blacken out that region.
Input:
import cv2
import numpy as np
# read image
img = cv2.imread('the_image.jpg')
ht, wd = img.shape[:2]
# convert to grayscale
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# threshold
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# get external contours
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
max_aspect=0
for cntr in contours:
x,y,w,h = cv2.boundingRect(cntr)
aspect = w/h
if aspect > max_aspect:
max_aspect = aspect
max_contour = cntr
# create mask from max_contour
mask = np.zeros((ht,wd), dtype=np.uint8)
mask = cv2.drawContours(mask, [max_contour], 0, (255), -1)
# dilate mask
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
mask = cv2.morphologyEx(mask, cv2.MORPH_DILATE, kernel)
# invert mask
mask = 255 - mask
# mask out region in input
result = img.copy()
result = cv2.bitwise_and(result, result, mask=mask)
# save resulting image
cv2.imwrite('the_image_masked.png',result)
# show thresh and result
cv2.imshow("mask", mask)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:

How to detect a rectangle block from an image and change it to white color?

How can I remove a black color rectangle from an image like this below,
I want to detect that huge black color rectangle box and change it to white color? Its my next pre-processing stage.
Any help is appreciated
Here is one way to do that in Python/OpenCV.
Read the input
Convert to gray
Threshold to binary and invert
Get the largest external contour
Get the bounding box for that contour
Fill that region of the input with white
Save the result
Input:
import cv2
import numpy as np
# read image
img = cv2.imread('black_rectangle.png')
ht, wd = img.shape[:2]
print(img.shape)
# convert to grayscale
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# threshold
thresh = cv2.threshold(gray,128,255,cv2.THRESH_BINARY)[1]
# invert
thresh = 255 - thresh
# get largest external contour
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)
# get bounding box of largest contour
x,y,w,h = cv2.boundingRect(big_contour)
# make that region white in the input image
result = img.copy()
result[y:y+h, x:x+w] = (255,255,255)
# show thresh and result
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
# save resulting image
cv2.imwrite('black_rectangle_2white.png',result)
Result:

OpenCV Smoother contour

I am trying to solve the issue of creating paths around logos with OpenCV.
I have attached two images, tekst.png and tekst2.png. I have also attached an image comparison.png that shows the wanted result (created manually) and the result I currently am getting with my program.
If anyone has any tips for me, I'd appreciate it a lot!
Short description of wanted solution:
Returns one outer contour that is as close as possible to the logo.
I can use the contour mentioned in the last sentence to scale it up to make padding in between the logo and the contour.
Some kind of algorithm to smooth out the finished contour
The code I currently have:
def current_milli_time():
return round(time.time() * 1000)
def time_calculation_start():
timing.append(current_milli_time())
def time_calculation_end(string):
timing.append(current_milli_time())
print(str(string) + ": ", timing[1] - timing[0], "ms")
timing.clear()
def render_png(filename):
print(filename)
time_calculation_start()
original_image = cv2.imread(str(filename), cv2.IMREAD_UNCHANGED)
copy = original_image.copy() # Saved for imagecreation
time_calculation_end("Setup")
time_calculation_start()
if(original_image.shape[2] == 4):
b,g,r,mask = cv2.split(original_image)
time_calculation_end("Mask")
# Reduce outer turdss
time_calculation_start()
kernel = np.ones((3,3), np.uint8)
dilation = cv2.dilate(mask,kernel,iterations = 2)
dilation = cv2.erode(dilation,kernel,iterations = 1)
time_calculation_end("Dialtion")
time_calculation_start()
gaublur = cv2.GaussianBlur(dilation,(16,16),0)
time_calculation_end("Gaussian blur")
#Find contours
time_calculation_start()
contours, hierarchy = cv2.findContours(gaublur, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
time_calculation_end("Find contours")
print("\tContour layers: ", len(contours))
# Draw contours
time_calculation_start()
cv2.drawContours(copy, contours, -1, (0, 255, 0, 255),1)
time_calculation_end("Draw contours")
print("\n")
cv2.imwrite(str(render_path) + str(filename), copy)
Here is one way to do that in Python/OpenCV. Note that I reduced the size of your input.
Read the input
Extract the BGR channels
Extract the alpha channel
Get the largest contour from the alpha channel to remove small regions
Reduce the number of vertices to make it smoother
Draw a white filled contour on black background
Dilate the contour image
Make an edge image and thicken it
Make a white background image
Invert the dilated contour and blur it for the shadow
Overlay the blurred dilated area on the background
Overlay the dilated white region
Overlay the bgr image
Overlay the edge
Save the result
Input:
import cv2
import numpy as np
# read image
img = cv2.imread('hjemsokt_small.png', cv2.IMREAD_UNCHANGED)
# extract bgr image
bgr = img[:,:,0:3]
# extract alpha channel
alpha = img[:,:,3]
# get largest contours
contours = cv2.findContours(alpha, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)
# smooth contour
peri = cv2.arcLength(big_contour, True)
big_contour = cv2.approxPolyDP(big_contour, 0.001 * peri, True)
# draw white filled contour on black background
contour_img = np.zeros_like(alpha)
cv2.drawContours(contour_img, [big_contour], 0, 255, -1)
# apply dilate to connect the white areas in the alpha channel
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (40,40))
dilate = cv2.morphologyEx(contour_img, cv2.MORPH_DILATE, kernel)
# make edge outline
edge = cv2.Canny(dilate, 0, 200)
# thicken edge
edge = cv2.GaussianBlur(edge, (0,0), sigmaX=0.3, sigmaY=0.3)
# make background
result = np.full_like(bgr, (255,255,255))
# invert dilated image and blur
dilate_inv = 255 - dilate
dilate_inv = cv2.GaussianBlur(dilate_inv, (0,0), sigmaX=21, sigmaY=21)
dilate_inv = cv2.merge([dilate_inv,dilate_inv,dilate_inv])
# overlay blurred dilated area on background
result[dilate_inv>0] = dilate_inv[dilate_inv>0]
# overlay dilated white region
result[dilate==255] = (255,255,255)
# overlay bgr image
result[contour_img==255] = bgr[contour_img==255]
# overlay edge
result[edge!=0] = (96,96,96)
# save resulting images
cv2.imwrite('hjemsokt_small_alpha.jpg',alpha)
cv2.imwrite('hjemsokt_small_contour.jpg',contour_img)
cv2.imwrite('hjemsokt_small_alpha_dilated.jpg',dilate)
cv2.imwrite('hjemsokt_small_alpha_dilated_inv.jpg',dilate_inv)
cv2.imwrite('hjemsokt_small_alpha_dilated_edge.jpg',edge)
cv2.imwrite('hjemsokt_small_result.jpg',result)
# show thresh and result
cv2.imshow("bgr", bgr)
cv2.imshow("alpha", alpha)
cv2.imshow("contour_img", contour_img)
cv2.imshow("dilate", dilate)
cv2.imshow("dilate_inv", dilate_inv)
cv2.imshow("edge", edge)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Alpha channel:
Contour Image:
Smoothed Dilated contour image:
Inverted contour blurred:
Edge image:
Result:

How to detect text on an X-Ray image with OpenCV

I want to detect text on x-ray images. The goal is to extract the oriented bounding boxes as a matrix where each row is a detected bounding box and each row contains the coordinates of all four edges i.e. [x1, x2, y1, y2]. I'm using python 3 and OpenCV 4.2.0.
Here is a sample image:
The string "test word", "a" and "b" should be detected.
I followed this OpenCV tutorial about creating rotated boxes for contours and this stackoverflow answer about detecting a text area in an image.
The resulting boundary boxes should look something like this:
I was able to detect the text, but the result included a lot of boxes without text.
Here is what I tried so far:
img = cv2.imread(file_name)
## Open the image, convert it into grayscale and blur it to get rid of the noise.
img2gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
ret, mask = cv2.threshold(img2gray, 180, 255, cv2.THRESH_BINARY)
image_final = cv2.bitwise_and(img2gray, img2gray, mask=mask)
ret, new_img = cv2.threshold(image_final, 180, 255, cv2.THRESH_BINARY) # for black text , cv.THRESH_BINARY_INV
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))
dilated = cv2.dilate(new_img, kernel, iterations=6)
canny_output = cv2.Canny(dilated, 100, 100 * 2)
cv2.imshow('Canny', canny_output)
## Finds contours and saves them to the vectors contour and hierarchy.
contours, hierarchy = cv2.findContours(canny_output, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# Find the rotated rectangles and ellipses for each contour
minRect = [None] * len(contours)
for i, c in enumerate(contours):
minRect[i] = cv2.minAreaRect(c)
# Draw contours + rotated rects + ellipses
drawing = np.zeros((canny_output.shape[0], canny_output.shape[1], 3), dtype=np.uint8)
for i, c in enumerate(contours):
color = (255, 0, 255)
# contour
cv2.drawContours(drawing, contours, i, color)
# rotated rectangle
box = cv2.boxPoints(minRect[i])
box = np.intp(box) # np.intp: Integer used for indexing (same as C ssize_t; normally either int32 or int64)
cv2.drawContours(img, [box], 0, color)
cv2.imshow('Result', img)
cv2.waitKey()
Do I need to run the results through OCR to make sure whether it is text or not? What other approaches should I try?
PS: I'm quite new to computer vision and not familiar with most concepts yet.
Here's a simple approach:
Obtain binary image. Load image, create blank mask, convert to grayscale, Gaussian blur, then Otsu's threshold
Merge text into a single contour. Since we want to extract the text as one piece, we perform morphological operations to connect individual text contours into a single contour.
Extract text. We find contours then filter using contour area with cv2.contourArea and aspect ratio using cv2.arcLength + cv2.approxPolyDP. If a contour passes the filter, we find the rotated bounding box and draw this onto our mask.
Isolate text. We perform an cv2.bitwise_and operation to extract the text.
Here's a visualization of the process. Using this screenshotted input image (since your provided input image was connected as one image):
Input image -> Binary image
Morph close -> Detected text
Isolated text
Results with the other image
Input image -> Binary image + morph close
Detected text -> Isolated text
Code
import cv2
import numpy as np
# Load image, create mask, grayscale, Gaussian blur, Otsu's threshold
image = cv2.imread('1.png')
original = image.copy()
blank = np.zeros(image.shape[:2], dtype=np.uint8)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
# Merge text into a single contour
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=3)
# Find contours
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:
# Filter using contour area and aspect ratio
x,y,w,h = cv2.boundingRect(c)
area = cv2.contourArea(c)
ar = w / float(h)
if (ar > 1.4 and ar < 4) or ar < .85 and area > 10 and area < 500:
# Find rotated bounding box
rect = cv2.minAreaRect(c)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(image,[box],0,(36,255,12),2)
cv2.drawContours(blank,[box],0,(255,255,255),-1)
# Bitwise operations to isolate text
extract = cv2.bitwise_and(thresh, blank)
extract = cv2.bitwise_and(original, original, mask=extract)
cv2.imshow('thresh', thresh)
cv2.imshow('image', image)
cv2.imshow('close', close)
cv2.imshow('extract', extract)
cv2.waitKey()
I removed the text using the following comand (after the code of above):
gray2 = cv2.cvtColor(extract, cv2.COLOR_BGR2GRAY)
blur2 = cv2.GaussianBlur(gray2, (5,5), 0)
thresh2 = cv2.threshold(blur2, 0, 255, cv2.THRESH_BINARY)[1]
test = cv2.inpaint(original, thresh2, 7, cv2.INPAINT_TELEA)

How to extract multiple objects from an image using Python OpenCV?

I am trying to extract object from an image using the color using OpenCV, I have tried by inverse thresholding and grayscale combined with cv2.findContours() but I am unable to use it recursively. Furthermore I can't figure out how to "cut out" the match from the original image and save it to a single file.
EDIT
~
import cv2
import numpy as np
# load the images
empty = cv2.imread("empty.jpg")
full = cv2.imread("test.jpg")
# save color copy for visualization
full_c = full.copy()
# convert to grayscale
empty_g = cv2.cvtColor(empty, cv2.COLOR_BGR2GRAY)
full_g = cv2.cvtColor(full, cv2.COLOR_BGR2GRAY)
empty_g = cv2.GaussianBlur(empty_g, (51, 51), 0)
full_g = cv2.GaussianBlur(full_g, (51, 51), 0)
diff = full_g - empty_g
# thresholding
diff_th =
cv2.adaptiveThreshold(full_g,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY,11,2)
# combine the difference image and the inverse threshold
zone = cv2.bitwise_and(diff, diff_th, None)
# threshold to get the mask instead of gray pixels
_, zone = cv2.threshold(bag, 100, 255, 0)
# dilate to account for the blurring in the beginning
kernel = np.ones((15, 15), np.uint8)
bag = cv2.dilate(bag, kernel, iterations=1)
# find contours, sort and draw the biggest one
contours, _ = cv2.findContours(bag, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
contours = sorted(contours, key=cv2.contourArea, reverse=True)[:3]
i = 0
while i < len(contours):
x, y, width, height = cv2.boundingRect(contours[i])
roi = full_c[y:y+height, x:x+width]
cv2.imwrite("piece"+str(i)+".png", roi)
i += 1
Where empty is just a white image size 1500 * 1000 as the one above and test is the one above.
This is what I came up with, only downside, I have a third image instead of only the 2 expected showing a shadow zone now...
Here's a simple approach:
Obtain binary image. Load the image, grayscale, Gaussian blur, Otsu's threshold, then dilate to obtain a binary black/white image.
Extract ROI. Find contours, obtain bounding boxes, extract ROI using Numpy slicing, and save each ROI
Binary image (Otsu's thresholding + dilation)
Detected ROIs highlighted in green
To extract each ROI, you can find the bounding box coordinates using cv2.boundingRect(), crop the desired region, then save the image
x,y,w,h = cv2.boundingRect(c)
ROI = original[y:y+h, x:x+w]
First object
Second object
import cv2
# Load image, grayscale, Gaussian blur, Otsu's threshold, dilate
image = cv2.imread('1.jpg')
original = image.copy()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7,7))
dilate = cv2.dilate(thresh, kernel, iterations=1)
# Find contours, obtain bounding box coordinates, and extract ROI
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
image_number = 0
for c in cnts:
x,y,w,h = cv2.boundingRect(c)
cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 2)
ROI = original[y:y+h, x:x+w]
cv2.imwrite("ROI_{}.png".format(image_number), ROI)
image_number += 1
cv2.imshow('image', image)
cv2.imshow('thresh', thresh)
cv2.imshow('dilate', dilate)
cv2.waitKey()

Categories

Resources