This is the imageI am trying to give proper shape to the images in my folder but unable to get that perfect result. Following is one type of example:
Following is the coding that I have done for my folder containing this type of images:
''''code''''
import cv2
import numpy as np
import glob
path = r'C:\Users\User\Desktop\A\*.jpg'
def k_function(image,k):
z= image.reshape((-1,4))
z=np.float32(z)
criteria = (cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
ret,label,center=cv2.kmeans(z,k,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)
center = np.uint8(center)
res = center[label.flatten()]
res2 = res.reshape((image.shape))
return res2
def noise_function(image):
kernel = np.ones((2, 2), np.uint8)
closing = cv2.morphologyEx(image, cv2.MORPH_CLOSE,
kernel, iterations = 2)
bg = cv2.dilate(closing, kernel, iterations = 1)
dist_transform = cv2.distanceTransform(closing, cv2.DIST_L2, 0)
ret, fg = cv2.threshold(dist_transform, 0.02
* dist_transform.max(), 255, 0)
return fg
def filling(thresh):
im_floodfill = thresh.copy()
h, w = thresh.shape[:2]
mask = np.zeros((h+2, w+2), np.uint8)
cv2.floodFill(im_floodfill, mask,(60,60),255);
im_floodfill_inv = cv2.bitwise_not(im_floodfill)
n = thresh | im_floodfill_inv
return n
for i, img in enumerate(glob.glob(path)):
img1 = cv2.imread(img)
n = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
b= k_function(n,2)
nm, thresh1 = cv2.threshold(b, 127, 255, cv2.THRESH_BINARY_INV);
fill = filling(thresh1)
noise = noise_function(fill)
cv2.imwrite(r'C:\Users\User\Desktop\New folder\image{}.jpg'.format(i),noise)
Try using copyMakeBorder to make a border. It looks like you're trying to use floodFill and I've never figured out how that is supposed to work.
import cv2
image = cv2.imread('elbow.png')
image = cv2.copyMakeBorder(image, 10, 0, 0, 10, cv2.BORDER_CONSTANT)
cv2.imwrite('elbow_border.png', image)
elbow.png:
elbow_border.png:
I would approach it a bit differently in Python/OpenCV. I would convert to HSV and threshold the saturation channel. Then use morphology open to smooth outline.
Input (cropped from your post):
import cv2
# load image as HSV and select saturation
img = cv2.imread("finger.png")
sat = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)[:,:,1]
# threshold the saturation channel
ret, thresh = cv2.threshold(sat,25,255,0)
# apply morphology open to smooth the outline
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (19,19))
smoothed = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
# write result to disk
cv2.imwrite("finger_smoothed.png", smoothed)
cv2.imshow("SAT", sat)
cv2.imshow("THRESH", thresh)
cv2.imshow("SMOOTHED", smoothed)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
Related
I have a set of pictures (sample) of the same formatted code, I've tried every thing but nothing works well.
I tried blurring, hsv, threshing, etc.
can you help me out?
import pytesseract
import cv2
imgr = cv2.imread("a.png")
img = cv2.resize(imgr, (int(imgr.shape[1] * 3), int(imgr.shape[0] * 3)), interpolation=cv2.INTER_AREA)
img = cv2.blur(img, (7, 7))
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
cv2.imshow("", v)
cv2.waitKey(0)
p = pytesseract.image_to_string(v)
print(p)
thresh = cv2.threshold(v, 170, 255, cv2.THRESH_BINARY)[1]
cv2.imshow("", thresh)
cv2.waitKey(0)
print(pytesseract.image_to_string(thresh))
ation
below is a possible solution.
I felt that distortion was part of the problem. So I tried to "fix" that.
The result looks fine: the detection is successful.
Unfortunately, since you give only one sample, I have no way to figure out if this will work on the other ones... (probably not...) Nevertheless, you can give it a try.
Best regards,
Stéphane
Note: I use tesseract-5.0.0-alpha with the tessdata_best dataset.
Here is the output from the console:
Regression parameters for the second-degree polynomial:
[ 2.33735101e-04 -1.92211992e-01 2.43573673e+02]
=============================
Rectified image
RESULT: EG01-012R210126024
=============================
================================================
Test on the non rectified image
with the same blur, erode, threshold and
tesseract parameters
RESULT: EGO1-012R2101269
================================================
Press any key on an opened opencv window to close
And below is the program:
# Standard imports
import cv2
import numpy as np
from matplotlib import pyplot as plt
import pytesseract
pytesseract.pytesseract.tesseract_cmd = r'/usr/local/bin/tesseract'
# Read image
imgr = cv2.imread("a.png")
# Resizing, converting...
factor=3
imgr = cv2.resize(imgr, (int(imgr.shape[1]*factor ), int(imgr.shape[0]*factor)), interpolation=cv2.INTER_AREA)
# First detection in order to crop the image
# We want a detection. Not important if result is bad.
strings=pytesseract.image_to_data(imgr, lang = 'eng', config='--psm 11 --oem 3 -c tessedit_char_whitelist=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-')
strings=strings.split('\n')
for line in strings[2:]:
s=line.split('\t')
if len(s[11])>0:
xmin=int(s[6])
break
## We crop the image to keep the interesting part...
imgr=imgr[:,np.max([0,xmin-imgr.shape[1]//10]):,:]
cv2.imshow("Cropped image",imgr)
hsv = cv2.cvtColor(imgr, cv2.COLOR_BGR2HSV)
h0, s0, Im0 = cv2.split(hsv)
w=Im0.shape[1] # From now, this is the image we will work on.
h=Im0.shape[0]
# Blob image to compute the image distortion
blob=cv2.blur(Im0,(w//3,1))
blob=cv2.normalize(blob,None,0,255,cv2.NORM_MINMAX)
blob=cv2.threshold(blob,170,255,cv2.THRESH_BINARY)[1]
cv2.imshow("Blob image",blob)
x=[]
y=[]
for i in range(w):
for j in range(h):
if blob[j,i]==0:
x.append(i)
y.append(j)
x=np.array(x)
y=np.array(y)
model = np.polyfit(x,y, 2)
print("Regression parameters for the second-degree polynomial: ")
print(model)
plt.plot(x,y,'x')
X=np.linspace(0,w)
plt.plot(X,X*X*model[0]+X*model[1]+model[2])
Ymean=np.mean(X*X*model[0]+X*model[1]+model[2])
# Remapping the cropped image with the found model parameters
map_x = np.zeros((Im0.shape[0], Im0.shape[1]), dtype=np.float32)
map_y = np.zeros((Im0.shape[0], Im0.shape[1]), dtype=np.float32)
for i in range(w):
for j in range(h):
map_x[j,i]=i
map_y[j,i]=j+i*i*model[0]+i*model[1]+model[2]-Ymean
Im1=cv2.remap(Im0, map_x, map_y, interpolation=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REPLICATE)
# Actual detection on the rectified image: Im1
Im1=cv2.normalize(Im1,None,0,255,cv2.NORM_MINMAX)
blur_radius=8
threshold=120
Im1= cv2.blur(Im1, (blur_radius,blur_radius))
kernel = np.ones((4,4), np.uint8)
Im1=255-cv2.erode(255-Im1, kernel)#, cv2.BORDER_REPLICATE)
Im1=cv2.normalize(Im1,None,0,255,cv2.NORM_MINMAX)
Im1 = cv2.threshold(Im1, threshold, 255, cv2.THRESH_BINARY)[1]
cv2.imshow("Rectified image for text detection",Im1)
strings=pytesseract.image_to_string(Im1, lang = 'eng', config='--psm 11 --oem 3 -c tessedit_char_whitelist=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-')
strings=strings.split()
strings=max(strings,key=len)
print('=============================')
print("Rectified image")
print('RESULT: ',strings)
print('=============================')
# For comparison: detection on the non rectified image
# using the same parameters:
Im2 = Im0 # whithout remapping
Im2 = cv2.normalize(Im2,None,0,255,cv2.NORM_MINMAX)
Im2 = cv2.blur(Im2, (blur_radius,blur_radius))
Im2 = 255-cv2.erode(255-Im2, kernel)#, cv2.BORDER_REPLICATE)
Im2 = cv2.normalize(Im2,None,0,255,cv2.NORM_MINMAX)
Im2 = cv2.threshold(Im2, threshold, 255, cv2.THRESH_BINARY)[1]
strings=pytesseract.image_to_string(Im2, lang = 'eng', config='--psm 11 --oem 3 -c tessedit_char_whitelist=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-')
strings=strings.split()
strings=max(strings,key=len)
print('================================================')
print("Test on the non rectified image")
print("with the same blur, erode, threshold and")
print("tesseract parameters")
print('RESULT: ',strings)
print('================================================')
cv2.imshow("Unrectified image for text detection",Im2)
# Close windows
print("Press any key on an opened opencv window to close")
cv2.waitKey()
plt.close()
cv2.destroyAllWindows()
import cv2
import numpy as np
import pytesseract
from PIL import Image, ImageStat
# Load image
image = cv2.imread('a.png')
img=image.copy()
# Remove border
kernel_vertical = cv2.getStructuringElement(cv2.MORPH_RECT, (1,50))
temp1 = 255 - cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel_vertical)
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (50,1))
temp2 = 255 - cv2.morphologyEx(image, cv2.MORPH_CLOSE, horizontal_kernel)
temp3 = cv2.add(temp1, temp2)
result = cv2.add(temp3, image)
# Convert to grayscale and Otsu's threshold
gray = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray,(5,5),0)
_,thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY_INV)
kernel = np.ones((3,3), np.uint8)
dilated = cv2.dilate(thresh, kernel, iterations = 5)
# Find the biggest Contour (Where the words are)
contours, hierarchy = cv2.findContours(dilated,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
Reg = []
for j in range(len(contours)-1):
for i in range(len(contours)-1):
if len(contours[i+1])>len(contours[i]):
Reg = contours[i]
contours [i] = contours[i+1]
contours [i+1] = Reg
x, y, w, h = cv2.boundingRect(contours[0])
img_cut = np.zeros(shape=(h,w))
img_cut = gray[y:y+h, x:x+w]
img_cut = 255-img_cut
# Tesseract
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
print(pytesseract.image_to_string(img_cut, lang = 'eng', config='--psm 7 --oem 3 -c tessedit_char_whitelist=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-'))
cv2.imwrite('generator.jpg',img_cut)
cv2.imshow('img', img_cut)
cv2.waitKey()
Tesseract recognition : EGO1-012R210124 (Actually it's unfavorable, I try my best.)
First of all this is my original image which I try to detect the defects (parallel lines) on a brushed aluminium surface.
Here is the steps I take:
Gaussian Blur
Dilate the image
Converting the image to grayscale
Morph Close Operation
Dilate again
Difference of the image
Canny Edge Detection
Finding the contours
Drawing a green line around the contours
Here is my code:
import numpy as np
import cv2
from matplotlib import pyplot as plt
import imutils
path = ''
path_output = ''
img_bgr = cv2.imread(path)
plt.imshow(img_bgr)
# bgr to rgb
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)
# Converting to grayscale
img_just_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
# Displaying the grayscale image
plt.imshow(img_just_gray, cmap='gray')
# Gaussian Blur
ksize_w = 13
ksize_h = 13
img_first_gb = cv2.GaussianBlur(img_rgb, (ksize_w,ksize_h), 0, 0, cv2.BORDER_REPLICATE);
plt.imshow(img_first_gb)
# Dilate the image
dilated_img = cv2.dilate(img_first_gb, np.ones((11,11), np.uint8))
plt.imshow(dilated_img)
# Converting to grayscale
img_gray_operated = cv2.cvtColor(dilated_img, cv2.COLOR_BGR2GRAY)
# Displaying the grayscale image
plt.imshow(img_gray_operated, cmap='gray')
# closing:
kernel_closing = np.ones((7,7),np.uint8)
img_closing = cv2.morphologyEx(img_gray_operated, cv2.MORPH_CLOSE, kernel_closing)
plt.imshow(img_closing, cmap='gray')
# dilation:
# add pixels to the boundaries of objects in an image
kernel_dilation = np.ones((3,3),np.uint8)
img_dilation2 = cv2.dilate(img_closing, kernel_dilation, iterations = 1)
plt.imshow(img_dilation2, cmap='gray')
diff_img = 255 - cv2.absdiff(img_just_gray, img_dilation2)
plt.imshow(diff_img, cmap='gray')
# canny
edgesToFindImage = img_dilation2
v = np.median(img_just_gray)
#print(v)
sigma = 0.33
lower_thresh = int(max(0,(1.0-sigma)*v))
higher_thresh = int(min(255,(1.0+sigma)*v))
img_edges = cv2.Canny(edgesToFindImage, lower_thresh, higher_thresh)
plt.imshow(img_edges, cmap='gray')
kernel_dilation2 = np.ones((2,2),np.uint8)
img_dilation2 = cv2.dilate(img_edges, kernel_dilation, iterations = 2)
plt.imshow(img_dilation2, cmap='gray')
# find contours
contoursToFindImage = img_dilation2
(_, cnts, _) = cv2.findContours(contoursToFindImage.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
print(type(cnts))
print(len(cnts))
# -1 for all
cntsWhichOne = -1
# -1 for infill
# >0 for edge thickness
cntsInfillOrEdgeThickness = 3
img_drawing_contours_on_rgb_image = cv2.drawContours(img_rgb.copy(), cnts, cntsWhichOne, (0, 255, 0), cntsInfillOrEdgeThickness)
plt.imshow(img_drawing_contours_on_rgb_image)
and this is the result.
How can I improve this detection? Is there a more effective method to detect lines?
Here is one way in Python OpenCV. You are close, you should use adaptive thresholding, morphology to clean up the small regions and skip the canny edges.
Input:
import cv2
import numpy as np
# load image
img = cv2.imread('scratches.jpg')
# convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# adaptive threshold
thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, -35)
# apply morphology
kernel = np.ones((3,30),np.uint8)
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
kernel = np.ones((3,35),np.uint8)
morph = cv2.morphologyEx(morph, cv2.MORPH_OPEN, kernel)
# get hough line segments
threshold = 25
minLineLength = 10
maxLineGap = 20
lines = cv2.HoughLinesP(morph, 1, 30*np.pi/360, threshold, minLineLength, maxLineGap)
# draw lines
linear1 = np.zeros_like(thresh)
linear2 = img.copy()
for [line] in lines:
x1 = line[0]
y1 = line[1]
x2 = line[2]
y2 = line[3]
cv2.line(linear1, (x1,y1), (x2,y2), 255, 1)
cv2.line(linear2, (x1,y1), (x2,y2), (0,0,255), 1)
print('number of lines:',len(lines))
# save resulting masked image
cv2.imwrite('scratches_thresh.jpg', thresh)
cv2.imwrite('scratches_morph.jpg', morph)
cv2.imwrite('scratches_lines1.jpg', linear1)
cv2.imwrite('scratches_lines2.jpg', linear2)
# display result
cv2.imshow("thresh", thresh)
cv2.imshow("morph", morph)
cv2.imshow("lines1", linear1)
cv2.imshow("lines2", linear2)
cv2.waitKey(0)
cv2.destroyAllWindows()
Threshold image:
Morphology cleaned image:
Lines on original image:
Lines on black background:
I've been trying to clean this image for OCR but getting mixed results:
Best I achieved:
def image_smoothening(img):
ret1, th1 = cv2.threshold(img, 180, 255, cv2.THRESH_BINARY)
ret2, th2 = cv2.threshold(th1, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
blur = cv2.GaussianBlur(th2, (1, 1), 0)
ret3, th3 = cv2.threshold(
blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
return th3
def remove_noise_and_smooth(img):
filtered = cv2.adaptiveThreshold(img.astype(
np.uint8), 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 45, 3)
kernel = np.ones((1, 1), np.uint8)
opening = cv2.morphologyEx(filtered, cv2.MORPH_OPEN, kernel)
closing = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, kernel)
img = image_smoothening(img)
or_image = cv2.bitwise_or(img, closing)
return or_image
Any clue as to what I'm missing?
My MATLAB code to solve it. I know you are writing in Python so you'll have to translate.
%Read in
im = imread('DuQy7.png');
%Convert to grayscale
img = rgb2gray(im);
img = rescale(img);
%Binarize with threshold of 0.7/1.0
imbw = imbinarize(img,0.7/1);
%Flip blacks/whites
imbw = imcomplement(imbw);
%Label, L is labelled image, n is # of labels
[L,n] = bwlabeln(imbw);
count = zeros(n,1);
[y,x] = size(L);
%Get count for each label
L = uint8(L);
for j=1:y
for i=1:x
if L(j,i) ~= 0
count(L(j,i)) = count(L(j,i)) + 1;
end
end
end
%Find label with most values in image
max = 0;
maxi = 1;
for index=1:n
if max < count(index)
max = count(index);
maxi = index;
end
end
%Replace large region and color other labels to white
for j=1:y
for i=1:x
if L(j,i) == maxi
L(j,i) = 0;
elseif L(j,i) ~= 0
L(j,i) = 256;
end
end
end
%view and save
imshow(L)
imwrite(L,'outputTXT.bmp');
You could probably better adjust the threshold to better cut out background regions that got included. You could also look for labelled regions that are very small and remove them since they are probably erroneously included.
Some parts of the background are going to be impossible to get rid of since they are indistinguishable from the actual symbols. For example, between symbol x2,y1 and x2,y2 there is a black background region between the outlined white which is the same value as the symbols. Therefore it would be very difficult to parse out.
You can do "division normalization" in Python/OpenCV to remove the background. But that will not help with the outline font issue.
Input:
import cv2
import numpy as np
# read the image
img = cv2.imread('img.png')
# convert to gray
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# apply morphology
kernel = cv2.getStructuringElement(cv2.MORPH_RECT , (3,3))
smooth = cv2.morphologyEx(gray, cv2.MORPH_DILATE, kernel)
# alternate blur in place of morphology
#smooth = cv2.GaussianBlur(gray, (15,15), 0)
# divide gray by morphology image
division = cv2.divide(gray, smooth, scale=255)
# threshold
result = cv2.threshold(division, 0, 255, cv2.THRESH_OTSU )[1]
# save results
cv2.imwrite('img_thresh.png',result)
# show results
cv2.imshow('smooth', smooth)
cv2.imshow('division', division)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
How To Add Color Inside The Edges - Python (OpenCV)
I am Trying to remove the background From this image ,
Partatily i Succeed From the help of Internet,I created edge of image using canny,
But i want to add white background inside the object edge (inside the object edges) ,
For better Output
This Is the code for Remove Background ,That i Created
Inputed Image
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('img/aa.jpg')
original=img.copy()
l = int(max(5, 6))
u = int(min(6, 6))
edges = cv.GaussianBlur(img, (21, 51),3)
edges = cv.cvtColor(edges , cv.COLOR_BGR2GRAY)
mask = np.zeros(img.shape, dtype=np.uint8)
edges = cv.Canny(edges,l,u)
edges = cv.dilate(edges, None)
edges = cv.erode(edges, None)
_,thresh=cv.threshold(edges,0,255,cv.THRESH_BINARY + cv.THRESH_OTSU)
kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (5,5))
morphed = cv.morphologyEx(thresh, cv.MORPH_CLOSE, kernel)
dilate=cv.dilate(morphed, None, iterations = 1) #change iteration
mask=dilate
(cnts, _) =cv.findContours(dilate, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
for contour in cnts:
# (x, y, w, h) = cv.boundingRect(contour)
if cv.contourArea(contour) < 2000:
continue
cv.drawContours(mask ,contour, -1, (255, 255, 255), 3)
result = cv.bitwise_and(original, original, mask=mask)
result[dilate==0] = (0,0,0)
img1=cv.resize(mask,(600,400))
cv.imshow(' Image', img1)
img=cv.resize(result,(600,400))
cv.imshow('Original Image', img)
cv.waitKey()
OUTPUT
Any other thoughts would be greatly appreciated!
Here is how I would do that in Python/OpenCV.
Read the input
Convert to HSV color space
Apply color thresholding on the background color in hsv to create a mask
Invert the mask
Apply morphology to fill the holes and remove extraneous regions
Use mask to make background of images white
Input:
import cv2
import numpy as np
# load image and get dimensions
img = cv2.imread("man_face.jpeg")
# convert to hsv
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
# threshold using inRange
range1 = (50,0,50)
range2 = (120,120,170)
mask = cv2.inRange(hsv,range1,range2)
# invert mask
mask = 255 - mask
# apply morphology closing and opening to mask
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15,15))
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
result = img.copy()
result[mask==0] = (255,255,255)
# write result to disk
cv2.imwrite("man_face_mask.png", mask)
cv2.imwrite("man_face_white_background.jpg", result)
# display it
cv2.imshow("mask", mask)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Mask image:
Result:
I have tried to search and found two solutions that could solve the problem I have but are not working by my side. I want to remove the jagged edges or smooth the image attached. I have tried to implement the following codes
import cv2
import numpy as np
import os
os.chdir("F:/Examples")
image = cv2.imread("image1.jpeg")
blur = cv2.GaussianBlur(image, (21, 21), 0)
mask = np.zeros(image.shape[:2], dtype=np.uint8)
output = np.where(mask==np.array([255, 255, 255]), blur, image)
cv2.imwrite("", output)
The above codes returns unchanged image as if the applied mask didn't work. Here is the input image
I also tried to implement some solutions from different links including (How to blur the image according to segmentation map) without success. Any help will be acknowledged
While the edges or the external contour should be clear as in this image
I almost managed to solve it.
The main problem is that the contour is partially curved, and partially with straight lines.
Explanations are in the comments:
import numpy as np
import cv2
from scipy.interpolate import splprep, splev
im = cv2.imread('image1.jpeg')
bk = im.copy()
# Fill background with black color
cv2.floodFill(im, None, seedPoint=(1,1), newVal=(0, 0, 0), loDiff=(5, 5, 5), upDiff=(5, 5, 5))
gray = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY)
ret, thresh_gray = cv2.threshold(gray, 5, 255, cv2.THRESH_BINARY)
# Use "open" morphological operation for removing small contours (noise)
thresh_gray = cv2.morphologyEx(thresh_gray, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5)));
# Background
bk[(thresh_gray > 0)] = 0
bk = cv2.morphologyEx(bk, cv2.MORPH_DILATE, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (20,20)));
#cv2.imshow('bk', bk)
# Foreground
fg = im.copy()
tmm_fg = cv2.morphologyEx(fg, cv2.MORPH_DILATE, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (20,20)));
fg_gray = cv2.cvtColor(fg, cv2.COLOR_RGB2GRAY)
fg[(fg_gray==0)] = tmm_fg[(fg_gray==0)]
#thresh_gray = cv2.morphologyEx(thresh_gray, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (50,50)));
# Find contours (there is only one contour)
# _, contours, _ = cv2.findContours(thresh_gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # 3 outputs works only in OpenCV 3. [-2:] is used for compatibility.
contours, _ = cv2.findContours(thresh_gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2:] # https://stackoverflow.com/questions/48291581/how-to-use-cv2-findcontours-in-different-opencv-versions
c = contours[0]
# Smooth contour
# https://agniva.me/scipy/2016/10/25/contour-smoothing.html
x,y = c.T
x = x.tolist()[0]
y = y.tolist()[0]
tck, u = splprep([x,y], u=None, s=1.0, per=1)
u_new = np.linspace(u.min(), u.max(), 150)
x_new, y_new = splev(u_new, tck, der=0)
res_array = [[[int(i[0]), int(i[1])]] for i in zip(x_new,y_new)]
smoothened = np.asarray(res_array, dtype=np.int32)
# Build a mask
mask = np.zeros_like(thresh_gray)
cv2.drawContours(mask, [smoothened], -1, 255, -1)
# For testig
test_im = cv2.cvtColor(thresh_gray, cv2.COLOR_GRAY2RGB)
cv2.drawContours(test_im, [smoothened], 0, (0, 255, 0), 1)
res = bk
res[(mask > 0)] = fg[(mask > 0)]
cv2.imshow('test_im', test_im)
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()