Remove shadow from image with OpenCV - python

I am trying to remove the shadow of the pipe in the following image.
I use the following code to isolate the pipe with the shadow but I cannot find a way to remove the shadow.
img = cv2.imread("myimage.png",cv2.IMREAD_UNCHANGED)
blurred = cv2.GaussianBlur(img, (5, 5), 0)
gray = cv2.cvtColor(blurred, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY_INV)[1]
cnts, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
img_X = img.shape[1] / 2
img_Y = img.shape[0]
img_cnts = None
img_distance = None
for c in cnts:
area = cv2.contourArea(c)
if area < 500:
continue
M = cv2.moments(c)
cnt_X = int(M["m10"] / M["m00"])
cnt_Y = int(M["m01"] / M["m00"])
cnt_distance = math.sqrt(sum((px - qx) ** 2.0 for px, qx in zip([cnt_X, cnt_Y], [img_X, img_Y])))
if img_distance == None or img_distance > cnt_distance:
img_cnts = c
img_distance = cnt_distance
mask = np.zeros_like(img) # Create mask where white is what we want, black otherwise
cv2.drawContours(mask, [img_cnts], -1, (255,255,255), -1) # Draw filled contour in mask
out = np.zeros_like(img) # Extract out the object and place into output image
out[mask == 255] = img[mask == 255]
This is the result of the previous code that is pretty close to what I need.
I tried to use cv2.adaptiveThreshold and cv2.createBackgroundSubtractorMOG without luck.

Well, if you apply adaptive-threshold along with bitwise-not operation result will be:
Of course, different blockSize and C parameters you will get different results.
Where: (source)
blockSize: determines the size of the neighbourhood area
C: constant that is subtracted from the mean or weighted sum of the neighbourhood pixels.
Code:
import cv2
img = cv2.imread("pdUqL.png")
gry = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thr = cv2.adaptiveThreshold(gry, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY_INV, blockSize=33, C=31)
bnt = cv2.bitwise_not(thr)
cv2.imshow("out", bnt)
cv2.waitKey(0)

there is no good way to remove the shadow. you need to fix your lighting. use "softboxes" (indirect lighting), multiple lights from multiple angles, and whenever you have mirror surfaces, arrange lights so they don't reflect off that into the camera.

Related

Removing blood vessels - denoising the vessels into the background colour

Problem statement:
fundus image, i need to suppress the blood vessels so that they do not appear on the image for the classifier being used.
There were a few thoughts; but easiest approach was to segment out a rough outline of the vasculature. This is achieved.
Next was to get the colour for the surround area and use it to blend it to the white area found; then merge it back to the original image.
Any suggestions for identifying the colour and blending it in. then merging it back to the original image so that the vessels are not visible. One thought was to do a contour of the each white area and find the corresponding colour outside and use it to blend it back.
Any alternate approach is also welcome.
eyepac image - vessels are visible; want to make it the colour of its surrounding so that they are not visible
Here is the code:
def apply_threshold_with_denoising(image):
image = cv2.adaptiveThreshold(image, 250, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 115, 1)
image = cv2.fastNlMeansDenoising(image, 1.5, 5, 5)
return image
def delete_small_components(image, size):
_, blackAndWhite = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY_INV)
nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats(blackAndWhite, None, None, None, 8, cv2.CV_32S)
sizes = stats[1:, -1] # get CC_STAT_AREA component
image = np.zeros(labels.shape, np.uint8)
for i in range(0, nlabels - 1):
if sizes[i] >= 150: # filter small dotted regions
image[labels == i + 1] = 255
return cv2.bitwise_not(image)
def kernel(num1, num2):
return np.ones((num1, num2), np.uint8)
def resize(img):
scale_percent = 50 # percent of original size
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
dim = (width, height)
# resize image
return cv2.resize(img, dim, interpolation=cv2.INTER_AREA)
def get_large_vessels(image):
struct_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 4))
opening = cv2.morphologyEx(image, cv2.MORPH_OPEN, struct_kernel, iterations=1)
cnts = cv2.findContours(opening, 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)
if area < 250:
cv2.drawContours(opening, [c], -1, (0, 0, 0), -1)
return opening
def get_small_vessels(both, large):
large = cv2.dilate(large, kernel(3, 3), iterations=5)
subtract = cv2.subtract(both, large)
return subtract
def remove_background(image, mask):
image = cv2.bitwise_and(cv2.bitwise_not(image), cv2.bitwise_not(image), mask=mask)
image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel(2, 2))
return image
img = cv2.imread('2001_left.jpeg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
l_b = np.array([0, 0, 30])
u_b = np.array([255, 255, 255])
mask = cv2.inRange(hsv, l_b, u_b)
mask = cv2.erode(mask, kernel(2, 2), iterations=5)
grayscale = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
threshold = apply_threshold_with_denoising(grayscale)
kernel22 = cv2.dilate(threshold, kernel(2, 2), iterations=2)
remove_small1kernel22 = delete_small_components(kernel22, 5)
remove_small1kernel22)
dilation = cv2.dilate(threshold, kernel(2, 1), iterations=2)
remove_small1 = delete_small_components(dilation, 150)
dilation = cv2.dilate(threshold, kernel(1, 2), iterations=2)
remove_small2 = delete_small_components(dilation, 150)
merge = cv2.addWeighted(remove_small1, 0.5, remove_small2, 0.5, 0)
threshold_merge = apply_threshold_with_denoising(merge)
remove_small3 = delete_small_components(threshold_merge, 150)
large_vessels = get_large_vessels(remove_background(remove_small3, mask))
cv2.imshow('Large blood vasculature', large_vessels)
small_vessels = get_small_vessels(remove_background(remove_small3, mask), large_vessels)
cv2.imshow('small vasculature', small_vessels)
I tried the above code.
It produces the vessels - small and large.
I need a way to use this map to recolour using a mean colour value outside each contour such that it blends in when put back.
Is there a way to just identify the segment - colour it and then merge back.

How to remove a contoured area from an image?

I have an image with a white background and some colored blocks. I have created multiple filters to find the colored blocks and get the contours for another purpose and I am able to draw the contours around the colored blocks.
These blocks are connected by some black lines which I would like to get the contours of. I was using the original contours to also get the lines but I was advised not to do that and instead remove the colored blocks from the image so that I would only remain with the black lines in the image making it easier to contour.
I have created a mask that would draw over the contoured block but when I display the final image the contoured blocks are black instead of white.
Is there any way to make the blocks white similar to the background so that I could remain with only the black lines?
From the images, you can see that the mask covers the image and the black line remains. However, I can't figure out how to make the blocks white instead of black so that only the line remains.
from ctypes import sizeof
from doctest import master
from cv2 import approxPolyDP, contourArea, cvtColor, inRange
import numpy as np
import cv2
kernel = (5, 5)
srcImg = cv2.imread('BluePurpleConnected.png', cv2.IMREAD_COLOR)
blur = cv2.GaussianBlur(srcImg, kernel, 0)
hsv = cv2.cvtColor(blur, cv2.COLOR_BGR2HSV)
grayImg = cv2.cvtColor(srcImg, cv2.COLOR_BGR2GRAY)
rows = int(grayImg.shape[0]/2)
cols = int(grayImg.shape[1]/2)
ker = np.ones((0, 0), 'uint8')
graySize = grayImg.shape
sig = 0.33
cPix = 200
bPix = 255
lPurp = np.array([130, 20, 10])
uPurp = np.array([145, 255, 255])
lBlue = np.array([94, 80, 2])
uBlue = np.array([126, 255, 255])
colArray = np.array([lPurp, uPurp, lBlue, uBlue])
masterStruc = []
maskArray = []
mask = np.ones(srcImg.shape[:2], dtype="uint8") * 255
x = 0
while x <= 1:
imgMask = cv2.inRange(hsv, colArray[2*x], colArray[2*x+1])
maskArray.append(imgMask)
v = np.median(imgMask)
lower = int(max(0, (1.0 - sig) * v))
upper = int(min(255, (1.0 + sig) * v))
bwImg = cv2.Canny(imgMask, lower, upper)
nbwImg = cv2.dilate(bwImg, kernel, iterations=1)
cImg = nbwImg
ret, threshold = cv2.threshold(cImg, cPix, bPix, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(threshold, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnt = 0
for cnt in contours:
curve = approxPolyDP(cnt, 0.02 * cv2.arcLength(cnt, True), True)
vert = len(curve)
area = cv2.contourArea(curve)
if(area > 1000):
if(vert == 4):
masterStruc.append([x, curve, area])
cv2.drawContours(mask, [curve], -1, 0, -1)
x = x + 1
srcImg = cv2.bitwise_and(srcImg, srcImg, mask=mask)
cv2.imshow('Mask', mask)
cv2.imshow('Image', srcImg)
if cv2.waitKey(0) & 0xFF == ord('q'):
cv2.destroyAllWindows()

I'm getting an error while detecting bottle fill level with OpenCV

I am trying to detect the filling levels of bottles moving on a conveyor belt with opencv. Since the bottles are colored, I give a white light from the back and determine the liquid contours and measure. I'm getting an error in a certain part of the code. Mistake;
(contours, areas) = zip(*sorted(zip(contours, areas), key = lambda a:a[1])) ValueError: not enouhg values to unpack (expected 2, got 0)
For ex photograph
When I increase the threshold value in the code, the program runs. But I want this value to remain constant. The reason for this is related to the quality of the contours. Thank you from now.
Source Code:
_, frame = cap.read()
# hsv_frame = cv2.cvtColor(frame, cv2.COLOR.BGR2HSV)
bottle_gray = cv2.split(frame)[0]
bottle_gray = cv2.GaussianBlur(bottle_gray, (7,7), 0)
(T, bottle_threshold) = cv2.threshold(bottle_gray, 49.5, 250, cv2.THRESH_BINARY_INV)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
bottle_open = cv2.morphologyEx(bottle_threshold, cv2.MORPH_CLOSE, kernel)
contours = cv2.findContours(bottle_open.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = imutils.grab_contours(contours)
bottle_clone = frame.copy()
cv2.drawContours(bottle_clone, contours, 0, (255,0,0), 2)
areas = [cv2.contourArea(contour) for contour in contours]
(contours, areas) = zip(*sorted(zip(contours,areas),key = lambda a:a[1]))```
import sys
import cv2
import numpy as np
# Fix HSV color range
def fixHSVRange(h, s, v):
return (180 * h / 360, 255 * s / 100, 255 * v / 100)
# Load image
dir = sys.path[0]
im = cv2.imread(dir+'/im.png')
# Convert image to HSV and find empty bottle range
hsv = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)
msk = cv2.inRange(hsv, fixHSVRange(0, 50, 60), fixHSVRange(5, 100, 100))
# Smooth noise
msk = cv2.medianBlur(msk, 21)
# Invert colors
msk = ~msk
# Find empty space bounderies
cnts, _ = cv2.findContours(msk, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
x, y, w, h = cv2.boundingRect(cnts[1])
# Draw level line
msk = cv2.cvtColor(msk, cv2.COLOR_GRAY2BGR)
cv2.line(msk, (0, y+h), (im.shape[1], y+h), (0, 255, 0), 2, cv2.LINE_AA)
# Save output
cv2.imwrite(dir+'/im_msk.png', np.hstack((im, msk)))

Draw contours around images of the same color with openCV python

I have this image with 3 channels RGB (a result of a VARI Index computation) and I would like to draw bounding boxes (rectangles) around the plants, represented in green here. What is the best and easiest way to do it with OpenCV / python?
I guess it's an easy problem for OpenCV experts, but I could not find good tutorials online to do this for multiple objects.
The closest tutorial I found was: determining-object-color-with-opencv
The assumptions for the bounding boxes should/could be:
green is the dominant color.
it should be more than X pixels.
Thanks in advance!
Just answering my own question after stumbling upon this resource: https://docs.opencv.org/3.4/da/d0c/tutorial_bounding_rects_circles.html
May not be the best answer but it somehow solves my problem!
import cv2
import numpy as np
image = cv2.imread('vari3.png')
# https://www.pyimagesearch.com/2016/02/15/determining-object-color-with-opencv/
# https://docs.opencv.org/3.4/da/d0c/tutorial_bounding_rects_circles.html
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)
# mask: green is dominant.
thresh = np.array((image.argmax(axis=-1) == 1) * 255, dtype=np.uint8)
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
contours_poly = [None] * len(cnts)
boundRect = [None] * len(cnts)
for i, c in enumerate(cnts):
contours_poly[i] = cv2.approxPolyDP(c, 3, True)
boundRect[i] = cv2.boundingRect(contours_poly[i])
for i in range(len(cnts)):
# cv2.drawContours(image, contours_poly, i, (0, 255, 0), thickness=2)
pt1 = (int(boundRect[i][0]), int(boundRect[i][1]))
pt2 = (int(boundRect[i][0] + boundRect[i][2]), int(boundRect[i][1] + boundRect[i][3]))
if np.sqrt((pt2[1] - pt1[1]) * (pt2[0] - pt1[0])) < 30:
continue
cv2.rectangle(image, pt1, pt2, (0, 0, 0), 2)
cv2.imwrite('result.png', image)
cv2.imshow("Image", image)
cv2.waitKey(0)
You need to do HSV filtering
Change image colors from BGR to HSV (Hue Saturation Value).
Filter a certain range of saturation and hue that matches green by
thresholding.
Refer to this page for code to do the first 2
https://pythonprogramming.net/color-filter-python-opencv-tutorial/
Do some morphological operations like Erosion, Dilation, Opening,
Closing to remove the small bits of green that don't represent trees
and to connect the trees that look broken together.
Refer to https://docs.opencv.org/master/d9/d61/tutorial_py_morphological_ops.html
Detect the contours then draw the rectangles
import cv2
import numpy as np
img = cv2.imread('8FGo1.jpg',1)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_red = np.array([45,100,50])
upper_red = np.array([75,255,255])
mask = cv2.inRange(hsv, lower_red, upper_red)
kernel = np.ones((5,5),np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
x,y,w,h = cv2.boundingRect(contour)
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imshow('img',img)
#cv2.imshow('mask',mask)
Output

detect rectangle in image and crop

I have lots of scanned images of handwritten digit inside a rectangle(small one).
Please help me to crop each image containing digits and save them by giving the same name to each row.
import cv2
img = cv2.imread('Data\Scan_20170612_4.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.bilateralFilter(gray, 11, 17, 17)
edged = cv2.Canny(gray, 30, 200)
_, contours, hierarchy = cv2.findContours(edged, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
i = 0
for c in contours:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.09 * peri, True)
if len(approx) == 4:
screenCnt = approx
cv2.drawContours(img, [screenCnt], -1, (0, 255, 0), 3)
cv2.imwrite('cropped\\' + str(i) + '_img.jpg', img)
i += 1
Here is My Version
import cv2
import numpy as np
fileName = ['9','8','7','6','5','4','3','2','1','0']
img = cv2.imread('Data\Scan_20170612_17.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.bilateralFilter(gray, 11, 17, 17)
kernel = np.ones((5,5),np.uint8)
erosion = cv2.erode(gray,kernel,iterations = 2)
kernel = np.ones((4,4),np.uint8)
dilation = cv2.dilate(erosion,kernel,iterations = 2)
edged = cv2.Canny(dilation, 30, 200)
_, contours, hierarchy = cv2.findContours(edged, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
rects = [cv2.boundingRect(cnt) for cnt in contours]
rects = sorted(rects,key=lambda x:x[1],reverse=True)
i = -1
j = 1
y_old = 5000
x_old = 5000
for rect in rects:
x,y,w,h = rect
area = w * h
if area > 47000 and area < 70000:
if (y_old - y) > 200:
i += 1
y_old = y
if abs(x_old - x) > 300:
x_old = x
x,y,w,h = rect
out = img[y+10:y+h-10,x+10:x+w-10]
cv2.imwrite('cropped\\' + fileName[i] + '_' + str(j) + '.jpg', out)
j+=1
That's an easy thing if u try. Here's my output- (The image and its one small bit)
What i did?
Resized the image first because it was too big in my screen
Erode, Dilate to remove small dots and thicken the lines
Threshold the image
Flood fill, beginning at the right point
Invert the flood fill
Find contours and draw one at a time which are in range of approximately the
area on the rectangle. For my resized (500x500) image i put Area of
contour in range 500 to 2500 (trial and error anyway).
Find bounding rectangle and crop that mask from main image.
Then save that piece with proper name- which i didn't do.
Maybe, there's a simpler way, but i liked this. Not putting the code because
i made it all clumsy. Will put if u still need it.
Here's how the mask looks when you find contours each at a time
code:
import cv2;
import numpy as np;
# Run the code with the image name, keep pressing space bar
# Change the kernel, iterations, Contour Area, position accordingly
# These values work for your present image
img = cv2.imread("your_image.jpg", 0);
h, w = img.shape[:2]
kernel = np.ones((15,15),np.uint8)
e = cv2.erode(img,kernel,iterations = 2)
d = cv2.dilate(e,kernel,iterations = 1)
ret, th = cv2.threshold(d, 150, 255, cv2.THRESH_BINARY_INV)
mask = np.zeros((h+2, w+2), np.uint8)
cv2.floodFill(th, mask, (200,200), 255); # position = (200,200)
out = cv2.bitwise_not(th)
out= cv2.dilate(out,kernel,iterations = 3)
cnt, h = cv2.findContours(out,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for i in range(len(cnt)):
area = cv2.contourArea(cnt[i])
if(area>10000 and area<100000):
mask = np.zeros_like(img)
cv2.drawContours(mask, cnt, i, 255, -1)
x,y,w,h = cv2.boundingRect(cnt[i])
crop= img[ y:h+y,x:w+x]
cv2.imshow("snip",crop )
if(cv2.waitKey(0))==27:break
cv2.destroyAllWindows()
_, contours, hierarchy = cv2.findContours(edged, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
you are using cv2.RETR_LIST to find contours in the image. For your image to get better output use cv2.RETR_EXTERNAL. Before using that first remove black border line from the image.
cv2.RETR_LIST gives you list of all contours for image
cv2.RETR_EXTERNAL gives you only external or outer contours, not internal contours
change line to
_, contours, hierarchy = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
Contours Hierarchy

Categories

Resources