Save image with no background opencv - python

I want to create a small scrypt that creates png images with no background. I've reading some information and I'm not sure if this is possible with opencv. (Sorry if it is a silly question but I'm newbie with this library).
Create an image is easy,
import cv2
import numpy as np
# Create a black image
img = np.zeros((512,512,3), np.uint8)
# Drawing a circle
circle = cv2.circle(img,(256,256), 63, (0,0,255), -1)
#
cv2.imwrite('circle.png',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
But, it is possible save it without background? In this example, it is possible just save the circle?
Thank you very much!!!

I have added transparency channel(or layer) to the image here in the code below. It will give a feel that there is nothing in the background.
import cv2
import numpy as np
# Create a black image
img = np.zeros((512,512, 4), np.uint8)
# Drawing a circle
circle = cv2.circle(img, (256,256), 63, (0,0,255, 255), -1)
#
cv2.imwrite('circle.png',img)

import cv2
import numpy as np
# Create a black image
img = np.zeros((512,512,3), np.uint8)
# Drawing a circle
circle = cv2.circle(img, (256,256), 63, (0,0,255), -1)
# Convert circle to grayscale
gray = cv2.cvtColor(circle, cv2.COLOR_BGR2GRAY)
# Threshold to make a mask
mask = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)[1]
# Put mask into alpha channel of Circle
result = np.dstack((circle, mask))
#
cv2.imwrite('circle.png',result)

Related

remove the element attached to the image border

I'm using OpenCV to detect Pneumonia in chest-x-ray using Image Processing, so I need to remove the attached area to the image border to get the lung only, can anyone help me code this in python?
This image explains what I want this image after applying this methods: resized, Histogram Equalization, otsu Thresholded and inverse binary Thresholded, morphological processes(opening then closing)
This is the Original Image Original Image
This is how I would approach the problem in Python/OpenCV. Add a white border all around, flood fill it with black to replace the white, then remove the extra border.
Input:
import cv2
import numpy as np
# read image
img = cv2.imread('lungs.jpg')
h, w = img.shape[:2]
# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# add 1 pixel white border all around
pad = cv2.copyMakeBorder(gray, 1,1,1,1, cv2.BORDER_CONSTANT, value=255)
h, w = pad.shape
# create zeros mask 2 pixels larger in each dimension
mask = np.zeros([h + 2, w + 2], np.uint8)
# floodfill outer white border with black
img_floodfill = cv2.floodFill(pad, mask, (0,0), 0, (5), (0), flags=8)[1]
# remove border
img_floodfill = img_floodfill[1:h-1, 1:w-1]
# save cropped image
cv2.imwrite('lungs_floodfilled.png',img_floodfill)
# show the images
cv2.imshow("img_floodfill", img_floodfill)
cv2.waitKey(0)
cv2.destroyAllWindows()
You can try using a morphological reconstruction with a border as a marker. This is an analogue of the function imclearborder from Matlab or Octave.
import cv2
import numpy as np
img = cv2.imread('5R0Zs.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 40, 255, cv2.THRESH_BINARY)[1]
kernel = np.ones((7,7),np.uint8)
kernel2 = np.ones((3,3),np.uint8)
marker = thresh.copy()
marker[1:-1,1:-1]=0
while True:
tmp=marker.copy()
marker=cv2.dilate(marker, kernel2)
marker=cv2.min(thresh, marker)
difference = cv2.subtract(marker, tmp)
if cv2.countNonZero(difference) == 0:
break
mask=cv2.bitwise_not(marker)
mask_color = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
out=cv2.bitwise_and(img, mask_color)
cv2.imwrite('out.png', out)
cv2.imshow('result', out )
cv2.waitKey(0) # waits until a key is pressed
cv2.destroyAllWindows()

Drawing OpenCV contours and save as transparent image

I'm trying to draw contours i have found using findContours.
If i draw like this, i get a black background with the contour drawn on it.
out = np.zeros_like(someimage)
cv2.drawContours(out, contours, -1, 255, 1)
cv2.imwrite('contours.png',out)
If i draw like this, i get a fully transparent image with no drawn contours.
out = np.zeros((55, 55, 4), dtype=np.uint8)
cv2.drawContours(out, contours, -1, 255, 1)
cv2.imwrite('contours.png',out)
How do i go about making an image with size (55,55) and draw a contour on this, while keeping a transparent background?
Thanks
To work with transparent images in OpenCV you need to utilize the fourth channel after BGR called alpha with controls it. So instead of creating a three-channel image, create one with four channels, and also while drawing make sure you assign the fourth channel to 255.
mask = np.zeros((55, 55, 4), dtype=np.uint8)
cv2.drawContours(mask, cnts, -1, (255, 255, 255, 255), 1) #change first three channels to any color you want.
cv2.imwrite('res.png', mask)
Input image whose contours to draw.
Result
In Python/OpenCV, use the black and white image as the alpha channel as well as using it for a 3 channel BGR image.
cntr_img = np.zeros((55, 55, 4), dtype=np.uint8)
cv2.drawContours(cntr_img, contours, -1, 255, 1)
out = cv2.cvtColor(cntr_img, cv2.COLOR_GRAY2BGRA)
out[:,:,3] = cntr_img
cv2.imwrite('contours.png',out)
This works for me in Python/OpenCV. I am using a white blob on black background for input, since I do not have a contour image available. The contour image needs to be grayscale.
Input:
import cv2
import numpy as np
# read image
img = cv2.imread('mask.png')
# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
out = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGRA)
out[:,:,3] = gray
# write output
cv2.imwrite('mask_transp.png',out)
# display it
cv2.imshow("out", out)
cv2.waitKey(0)
Transparent result (download to see it since it is white on transparent background):

how to detect irregular circles in python with opencv

I want to create a vision system for the detection of defect in SMD capacitors, the defect is called "pinhole" and they are small holes in the surface of the chip that are generated at the time of construction. my objective is to create an algorithm that is able of detecting these holes and with this, discard the chips that have this defect
For the moment I have created two codes:
the first one converts the original image to a binary image so that I can clear the circles, the code and the result is as follows. after obtaining the image in a binary way, I don't know how to make my algorithm detect that there is a circle, according to what I have investigated with "HOUGH" it can only be applied to images in grayscale
`
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread("C:/Users/Johanna Menendez/Documents/UNIR/TFM/chips/20190905_124734.jpg", 0)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
newImg = cv2.resize(img, (0,0), fx=0.50, fy=0.50)
(thresh, blackAndWhiteImage) = cv2.threshold(newImg, 127, 255, cv2.THRESH_BINARY)
numpy_vertical_concat = np.concatenate((newImg, blackAndWhiteImage), axis=1)
cv2.imshow('binary', numpy_vertical_concat)
cv2.waitKey(0)
`
binary
-The second is with contours detection, the problem with this is that it not only detects circles and likewise after this I don't know how to make my algorithm detect that it is a circle
import cv2
`
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread("C:/Users/Johanna Menendez/Documents/UNIR/TFM/chips/20190905_124734.jpg", 0)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
newImg = cv2.resize(img, (0,0), fx=0.50, fy=0.50)
gray = cv2.cvtColor(newImg, cv2.COLOR_RGB2GRAY)
(thresh, blackAndWhiteImage) = cv2.threshold(gray, 130, 255, cv2.THRESH_BINARY)
(contours, hierarchy) = cv2.findContours(blackAndWhiteImage, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
img = cv2.drawContours(newImg, contours, -1, (0, 255, 0), 2)
#numpy_vertical_concat = np.concatenate((newImg, img), axis=1)
cv2.imshow('binary', img)
cv2.waitKey(0)`
contours detection
could help me find out how to know the way for my algorithm to detect the circles please?

Filling in Circles in OpenCV

I've been struggling with this for a while. I've been trying to figure out some sort of a way in OpenCV in Python to fill in while circles in an image that's entirely black and white.
To be clear this image has been tresholded using adaptive thresholding but now I have these rings which I'd like to be able to fill in. Ideally whatever algorithm is used to fill in circles should be able for both sets of pictures I included.
If anyone could offer any guidance in this regard I'd greatly appreciate it.
Before Algorithm:
After Algorithm:
Before Algorithm:
After Algorithm:
A simple search in Google would have given you this article, which answers exactly your question.
I adopted that solution for your input:
import cv2
import numpy as np
# Read image
im_in = cv2.imread("circles.jpg", cv2.IMREAD_GRAYSCALE)
# Threshold
th, im_th = cv2.threshold(im_in, 127, 255, cv2.THRESH_BINARY)
# Copy the thresholded image
im_floodfill = im_th.copy()
# Mask used to flood filling.
# NOTE: the size needs to be 2 pixels bigger on each side than the input image
h, w = im_th.shape[:2]
mask = np.zeros((h+2, w+2), np.uint8)
# Floodfill from point (0, 0)
cv2.floodFill(im_floodfill, mask, (0,0), 255)
# Invert floodfilled image
im_floodfill_inv = cv2.bitwise_not(im_floodfill)
# Combine the two images to get the foreground
im_out = im_th | im_floodfill_inv
# Display images.
cv2.imwrite("circles_filled.png", im_out)
Input file circles.png:
Output file circles_filled.png:
You can also fill the circles by drawing the contours.
import cv2
import numpy as np
#Run Main
if __name__ == "__main__" :
image = cv2.imread("circle.jpg", -1)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)
_,contours,_ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(image, contours, -1, (255,255,255), thickness=-1)
cv2.namedWindow('Image', cv2.WINDOW_NORMAL)
cv2.imshow('Image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

How to remove an extension to a blob caused by morphology

I have an image that I'm eroding and dilating like so:
kernel = np.ones((5,5),np.float32)/1
eroded_img = cv2.erode(self.inpainted_adjusted_image, kernel, iterations=10)
dilated_img = cv2.dilate(eroded_img, kernel, iterations=10)
Here's the result of the erosion and dilation:
and then I'm taking a threshold of it like so:
self.thresh = cv2.threshold(dilated_img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
But the threshold gives me an unwanted extension that I've marked in the image below (The region above the red line is the unwanted region):
How do I get rid of this unwanted region? Is there a better way to do what I'm doing?
Working with a different type of threshold (adaptive threshold, which takes local brigthness into account) will already get rid of your problem: The adaptive threshold result is what you are looking for.
[EDIT: I have taken the liberty of adding some code on Hough circles. I admit that I have played with the parameters for this single image to get a nice looking result, though I do not know what type of accuracy you are needing for such a type of problem]
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('image.png',0)
thresh = cv2.threshold(img, 210, 255, cv2.ADAPTIVE_THRESH_MEAN_C)[1]
canny = cv2.Canny(thresh,50,150)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles(canny,cv2.HOUGH_GRADIENT,1,20, param1=50,param2=23,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
# draw the outer circle
cv2.circle(cimg,(i[0],i[1]),i[2],(255,0,0),3)
# draw the center of the circle
cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)
titles = ['Original Image', 'Adaptive Thresholding', "Canny", "Hough Circle"]
images = [img, thresh, canny, cimg]
for i in xrange(4):
plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
Let us know if this is not yet sufficient.
From the binary Image it would be fairly easy to fit a circle using a Hough transform. Once you have the outer boundary of the circle i would suggest bleeding the boundary and cropping out the portion that outside the boundary.
Another approach is to adjust your threshold value. It looks like you could get away with that. You might need some morphological operations to get a clean edge. Using a disk kernel will help retain the shape to a good extent.
Since your question has been rolled back to its original version, I have attached a solution using flood fill which works on your images.
import numpy as np
import cv2
import sys
import matplotlib.pyplot as plt
img = cv2.imread('image.png', 0)
h, w = img.shape[:2]
mask = np.zeros((h+2, w+2), np.uint8)
gray = cv2.blur(img,(5,5))
(minVal, maxVal, minLoc, maxLoc) = cv2.minMaxLoc(gray)
print maxLoc
fixed_range = True
connectivity = 4
flooded = img.copy()
mask[:] = 0
connectivity = 4 #8
flags = connectivity
flags |= cv2.FLOODFILL_FIXED_RANGE
cv2.floodFill(flooded, mask, maxLoc, (255, 255, 255), (60,)*3, (60,)*3, flags)
thresh = cv2.threshold(flooded, 250, 255, cv2.THRESH_BINARY)[1]
titles = ['Original Image', 'Blurred', "Floodfill", "Threshold"]
images = [img, gray, flooded, thresh]
for i in xrange(4):
plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()

Categories