Fill gap between two lines with color - python

Here's my code:
def region_of_interest(image):
height = image.shape[0]
polygons = np.array([
[(300, height), (1100, height), (600, 400)] #Dimensions of Polygon
])
mask = np.zeros_like(image)
cv2.fillPoly(mask, polygons, 255)
masked_image = cv2.bitwise_and(image, mask)
return masked_image
I want to shade the area between the polygon, and I've tried changing the RGB color in the cv2.fillPoly line, but that hasn't worked.
I would prefer for the color to be transparent, but a solid gradient is fine too.

here is a working code , if the alpha value is one, there would be no transparency if the value is 0 it will be totally black.
import cv2
import numpy as np
img = cv2.imread("1.jpeg")
height = img.shape[0]
alpha = 0.7
line1 = ((50,50),(50,200))
img = cv2.line(img,line1[0],line1[1],(0,255,0),2)
line2 = ((100,50),(100,200))
img = cv2.line(img,line2[0],line2[1],(0,255,0),2)
# cv2.rectangle(img,(10,1),(50,100),(0,0,255),3)
# img[1:100, 10:50] = img[1:100, 10:50]*0.5
img[line1[0][1]:line2[1][1], line1[1][0]:line2[1][0]] = img[line1[0][1]:line2[1][1],
line1[1][0]:line2[1][0]]*alpha
# img = cv2.fillPoly(img, polygons, 255)
cv2.imshow("img",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
you can change it to make it white instead of black if the alpha is 0 and dark if alpha is 1 by subtracting the pixels by 255 like this
img[line1[0][1]:line2[1][1], line1[1][0]:line2[1][0]] = 255-(img[line1[0]
[1]:line2[1][1], line1[1][0]:line2[1][0]]*alpha)

Related

How can I make all background white in a binary image

In the image I would like to have only the lungs in black not the background. The background [Top black and bottom black area of the image] must be white not black. How can I do that in python?. Code that resuls in the image above from an original grayscale image ("image") is:
from skimage.filters import sobel
img = cv2.GaussianBlur(image, (5, 5), 0)
img = cv2.erode(img, None, iterations=2)
img = cv2.dilate(img, None, iterations=2)
elevation_map = sobel(img)
markers = np.zeros_like(image)
markers[image < 70] = 1
markers[image > 254] = 2
segmentation = skimage.morphology.watershed(elevation_map, markers)
fig, ax = plt.subplots(figsize=(7, 7))
ax.imshow(segmentation, cmap=plt.cm.gray, interpolation='nearest')
ax.axis('off')
ax.set_title('segmentation')
Assuming we have the segmentation image as posted above, and we want to fill the surrounding background with while.
One option is iterating the borders, and apply floodFill where pixel is black:
import cv2
import numpy as np
gray = cv2.imread('segmentation.png', cv2.IMREAD_GRAYSCALE) # Read image as grayscale
for x in range(gray.shape[1]):
# Fill dark top pixels:
if gray[0, x] == 0:
cv2.floodFill(gray, None, seedPoint=(x, 0), newVal=255, loDiff=3, upDiff=3) # Fill the background with white color
# Fill dark bottom pixels:
if gray[-1, x] == 0:
cv2.floodFill(gray, None, seedPoint=(x, gray.shape[0]-1), newVal=255, loDiff=3, upDiff=3) # Fill the background with white color
for y in range(gray.shape[0]):
# Fill dark left side pixels:
if gray[y, 0] == 0:
cv2.floodFill(gray, None, seedPoint=(0, y), newVal=255, loDiff=3, upDiff=3) # Fill the background with white color
# Fill dark right side pixels:
if gray[y, -1] == 0:
cv2.floodFill(gray, None, seedPoint=(gray.shape[1]-1, y), newVal=255, loDiff=3, upDiff=3) # Fill the background with white color
cv2.imshow('gray', gray)
cv2.waitKey()
cv2.destroyAllWindows()
Other option is use something as MATLAB imfill(BW,'holes').
Fill the center black with white.
In the original image, fill with while where both original and filled image are black:
import cv2
import numpy as np
from skimage.morphology import reconstruction
def imfill(img):
# https://stackoverflow.com/questions/36294025/python-equivalent-to-matlab-funciton-imfill-for-grayscale
# Use the matlab reference Soille, P., Morphological Image Analysis: Principles and Applications, Springer-Verlag, 1999, pp. 208-209.
# 6.3.7 Fillhole
# The holes of a binary image correspond to the set of its regional minima which
# are not connected to the image border. This definition holds for grey scale
# images. Hence, filling the holes of a grey scale image comes down to remove
# all minima which are not connected to the image border, or, equivalently,
# impose the set of minima which are connected to the image border. The
# marker image 1m used in the morphological reconstruction by erosion is set
# to the maximum image value except along its border where the values of the
# original image are kept:
seed = np.ones_like(img)*255
img[ : ,0] = 0
img[ : ,-1] = 0
img[ 0 ,:] = 0
img[ -1 ,:] = 0
seed[ : ,0] = 0
seed[ : ,-1] = 0
seed[ 0 ,:] = 0
seed[ -1 ,:] = 0
fill_img = reconstruction(seed, img, method='erosion')
return fill_img
gray = cv2.imread('segmentation.png', cv2.IMREAD_GRAYSCALE) # Read image as grayscale
fill_gray = imfill(gray)
# Fill with white where both gray and fill_gray are zeros
gray[(gray == 0) & (fill_gray == 0)] = 255;
cv2.imshow('gray', gray)
cv2.imshow('fill_gray', fill_gray)
cv2.waitKey()
cv2.destroyAllWindows()
I also thought of solving it using findContours with hierarchy, but it's a bit more coding.
Output:
For removing the black spot in the center (if needed), we may use connectedComponentsWithStats, and fill the small "spot" according to the area.
Edit:
Example for incorporating the above solution with your code:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import skimage.io
import skimage.color
import skimage.filters
from skimage.io import imread
from skimage.color import rgb2gray
from skimage.filters import sobel
import cv2
from skimage import data
image = cv2.imread('/content/drive/MyDrive/Covid_data/Training Set/covid/covid/ct_scan_100/22.jpg')
fig, ax = plt.subplots(figsize=(7, 7))
ax.imshow(image, cmap=plt.cm.gray, interpolation='nearest')
ax.axis('off')
ax.set_title('Original Image')
# Image in grayscale
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Blurring the image [img]
img = cv2.GaussianBlur(image, (5, 5), 0)
img = cv2.erode(img, None, iterations=2)
img = cv2.dilate(img, None, iterations=2)
#img = image
elevation_map = sobel(img)
fig, ax = plt.subplots(figsize=(7, 7))
ax.imshow(elevation_map, cmap=plt.cm.gray, interpolation='nearest')
ax.axis('off')
ax.set_title('elevation_map')
# we find markers of the background and the coins based on the extreme parts of the histogram of grey values
markers = np.zeros_like(image)
# Choosing extreme parts of the histogram of grey values
markers[image < 70] = 1
markers[image > 254] = 2
#we use the watershed transform to fill regions of the elevation map starting from the markers determined above:
segmentation = skimage.morphology.watershed(elevation_map, markers)
fig, ax = plt.subplots(figsize=(7, 7))
ax.imshow(segmentation, cmap=plt.cm.gray, interpolation='nearest')
ax.axis('off')
ax.set_title('segmentation')
#gray = cv2.imread('segmentation.png', cv2.IMREAD_GRAYSCALE) # Read image as grayscale
# Convert segmentation to uint8 image, where 255 is white and 0 is black (OpenCV style mask).
ret, gray = ret, gray = cv2.threshold(segmentation.astype(np.uint8), 1, 255, cv2.THRESH_BINARY)
for x in range(gray.shape[1]):
# Fill dark top pixels:
if gray[0, x] == 0:
cv2.floodFill(gray, None, seedPoint=(x, 0), newVal=255, loDiff=3, upDiff=3) # Fill the background with white color
# Fill dark bottom pixels:
if gray[-1, x] == 0:
cv2.floodFill(gray, None, seedPoint=(x, gray.shape[0]-1), newVal=255, loDiff=3, upDiff=3) # Fill the background with white color
for y in range(gray.shape[0]):
# Fill dark left side pixels:
if gray[y, 0] == 0:
cv2.floodFill(gray, None, seedPoint=(0, y), newVal=255, loDiff=3, upDiff=3) # Fill the background with white color
# Fill dark right side pixels:
if gray[y, -1] == 0:
cv2.floodFill(gray, None, seedPoint=(gray.shape[1]-1, y), newVal=255, loDiff=3, upDiff=3) # Fill the background with white color
fig, ax = plt.subplots(figsize=(7, 7))
ax.imshow(gray, cmap=plt.cm.gray, interpolation='nearest')
ax.axis('off')
ax.set_title('gray')
Using: ret, gray = cv2.threshold(segmentation.astype(np.uint8), 1, 255, cv2.THRESH_BINARY)
Replaces all the white pixels in segmentation with 255 and all the black pixels with 0.

How to crop image using masked image and overlay on top of other image with same color?

I'm trying to crop an image based on the mask image and paste the cropped image on the new background image. I was able to achieve this but the cropped image is in gray color instead of color as in the original image
As it can be seen in the above image cropped image color is not the same as the original source cat image color, cropped image is a greyish color whereas in the original image it contains yellow golden color.
my code is as below to perform this
import cv2
src1=cv2.imread('cat.jpg',0)
mask=cv2.imread('mask_cat.jpg',0)
ret, thresh1 = cv2.threshold(mask, 0, 255, cv2.THRESH_BINARY)
src1 [thresh1==0] = 0
h, w = src1.shape
red = (0, 0, 0)
width, height = 1742, 815
back =cv2.cvtColor( create_blank(width, height, rgb_color=red),cv2.COLOR_BGR2GRAY)
hh, ww = back.shape
yoff = round((hh-h)/2)
xoff = round((ww-w)/2)
result = back.copy()
result[yoff:yoff+h, xoff:xoff+w] = src1
How can I get the same color in the cropped image as the original source color from where it cropped? Any suggestion or help solving this will be appreciated.
import cv2
cat = cv2.imread('cat.jpg')
mask_cat = cv2.imread('mask_cat.jpg', 0)
result = cv2.bitwise_and(cat,cat,mask = mask_cat)
I also see you try to reshape the image.It can be done as follows.
width, height = 1742, 815
reshaped_result = cv2.resize(result, dsize=(width, height))
To place the cropped image on resized image
width, height = 1742, 815
result_final = np.zeros((height,width,3), np.uint8)
h, w = result.shape[:2]
hh, ww = result_final.shape[:2]
yoff = round((hh-h)/2)
xoff = round((ww-w)/2)
result_final[yoff:yoff+h, xoff:xoff+w] = result

What's the most simple way to crop a circle thumbnail from an image?

I am trying to crop a centered (or not centered) circle from this image:
I stole this code from the existing questions regarding this topic on stack overflow, something goes wrong though:
import cv2
file = 'dog.png'
img = cv2.imread(file)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
circle = cv2.HoughCircles(img,
3,
dp=1.5,
minDist=10,
minRadius=1,
maxRadius=10)
x = circle[0][0][0]
y = circle[0][0][1]
r = circle[0][0][2]
rectX = (x - r)
rectY = (y - r)
crop_img = img[rectY:(rectY+2*r), rectX:(rectX+2*r)]
cv2.imwrite('dog_circle.png', crop_img)
Output:
Traceback (most recent call last):
File "C:\Users\Artur\Desktop\crop_circle - Kopie\crop_circle.py", line 14, in <module>
x = circle[0][0][0]
TypeError: 'NoneType' object is not subscriptable
cv2.HoughCircles() seems to produce None instead of a cropped circle array. How do I fix this?
first: HoughCircles is used to detect circles on image, not to crop it.
You can't have circle image. Image is always rectangle but some of pixels can be transparent (alpha channel in RGBA) and programs will not display them.
So you can first crop image to have square and later add alpha channel with information which pixels should be visible. And here you can use mask with white circle on black background. At the end you have to save it as png or tiff because jpg can't keep alpha channel.
I use module PIL/pillow for this.
I crop square region in center of image but you can use different coordinates for this.
Next I create grayscale image with the same size and black background and draw white circle/ellipse.
Finally I add this image as alpha channel to cropped image and save it as png.
from PIL import Image, ImageDraw
filename = 'dog.jpg'
# load image
img = Image.open(filename)
# crop image
width, height = img.size
x = (width - height)//2
img_cropped = img.crop((x, 0, x+height, height))
# create grayscale image with white circle (255) on black background (0)
mask = Image.new('L', img_cropped.size)
mask_draw = ImageDraw.Draw(mask)
width, height = img_cropped.size
mask_draw.ellipse((0, 0, width, height), fill=255)
#mask.show()
# add mask as alpha channel
img_cropped.putalpha(mask)
# save as png which keeps alpha channel
img_cropped.save('dog_circle.png')
img_cropped.show()
Result
BTW:
In mask you can use values from 0 to 255 and different pixels may have different transparency - some of them can be half-transparent to make smooth border.
If you want to use it in HTML on own page then you don't have to create circle image because web browser can round corners of image and display it as circle. You have to use CSS for this.
EDIT: Example with more circles on mask.
from PIL import Image, ImageDraw
filename = 'dog.jpg'
# load image
img = Image.open(filename)
# crop image
width, height = img.size
x = (width - height)//2
img_cropped = img.crop((x, 0, x+height, height))
# create grayscale image with white circle (255) on black background (0)
mask = Image.new('L', img_cropped.size)
mask_draw = ImageDraw.Draw(mask)
width, height = img_cropped.size
mask_draw.ellipse((50, 50, width-50, height-50), fill=255)
mask_draw.ellipse((0, 0, 250, 250), fill=255)
mask_draw.ellipse((width-250, 0, width, 250), fill=255)
# add mask as alpha channel
img_cropped.putalpha(mask)
# save as png which keeps alpha channel
img_cropped.save('dog_2.png')
img_cropped.show()
This answer explains how to apply a mask. First, read in the image:
import cv2
import numpy as np
img = cv2.imread('dog.jpg')
Next create a mask, or a blank image that is the same size as the source image:
h,w,_ = img.shape
mask = np.zeros((h,w), np.uint8)
Then, draw a circle on the mask. Change these parameters based on where the face is:
cv2.circle(mask, (678,321), 5, 255, 654)
Finally, mask the source image:
img = cv2.bitwise_and(img, img, mask= mask)
Here is the mask:
And the output:
The idea is to create a black mask then draw the desired region to crop out in white using cv2.circle(). From there we can use cv2.bitwise_and() with the original image and the mask. To crop the result, we can use cv2.boundingRect() on the mask to obtain the ROI then use Numpy slicing to extract the result. For this example I used the center point derived from the image's width and height
import cv2
import numpy as np
# Create mask and draw circle onto mask
image = cv2.imread('1.jpg')
mask = np.zeros(image.shape, dtype=np.uint8)
x,y = image.shape[1], image.shape[0]
cv2.circle(mask, (x//2,y//2), 300, (255,255,255), -1)
# Bitwise-and for ROI
ROI = cv2.bitwise_and(image, mask)
# Crop mask and turn background white
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
x,y,w,h = cv2.boundingRect(mask)
result = ROI[y:y+h,x:x+w]
mask = mask[y:y+h,x:x+w]
result[mask==0] = (255,255,255)
cv2.imshow('result', result)
cv2.waitKey()

How to crop rectangular shapes in an image using Python

Can anyone give me advice on how to crop the two rectangular boxes and save it?
I already tried this code, but it does not crop very well
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()
This is the result. It only crops the smaller box. What I want is for it to crop the two squares.
if you have the coordinates of the rectangles you can try:
cropped = img [y1:y2, x1:x2]
cv2.imwrite('cropped.png', cropped)
The first line crops the image base on the given coordinates assuming (y1

How to change white images background color to black color in one folder?

I want to change my images background color to black color. I tried with my own codes but it did not work, moreover it remove the object. Here is my script :
image = cv2.imread("./1.jpg")
r = 150.0 / image.shape[1]
dim = (150, int(image.shape[0] * r))
resized = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)
lower_white = np.array([220, 220, 220], dtype=np.uint8)
upper_white = np.array([255, 255, 255], dtype=np.uint8)
mask = cv2.inRange(resized, lower_white, upper_white)
res = cv2.bitwise_not(resized, resized, mask)
cv2.imshow('res', res)
I will be very appreciate if anyone could help me out.Thank you.
See the image
Instead of using RGB color range to filter the background color you should use HSV color space. Select a hsv range to filter your background color. Here is simple implementation of the same.
image = cv2.imread("D:\Image\VaS38.jpg")
r = 150.0 / image.shape[1]
dim = (150, int(image.shape[0] * r))
resized = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)
lower_white = np.array([80, 1, 1],np.uint8) #lower hsv value
upper_white = np.array([130, 255, 255],np.uint8) #upper hsv value
hsv_img = cv2.cvtColor(resized,cv2.COLOR_BGR2HSV) #rgb to hsv color space
#filter the background pixels
frame_threshed = cv2.inRange(hsv_img, lower_white, upper_white)
kernel = np.ones((5,5),np.uint8)
#dilate the resultant image to remove noises in the background
#Number of iterations and kernal size will depend on the backgound noises size
dilation = cv2.dilate(frame_threshed,kernel,iterations = 2)
resized[dilation==255] = (0,0,0) #convert background pixels to black color
cv2.imshow('res', resized)
cv2.waitKey(0)
Result image:
PS: Please note that in your case there are slight variations of HSV values near foreground object boundaries so the final result is not perfect. May be you can tune the HSV color range more precisely to get better result, or you can use foreground objects outermost edge as a limiting line to erosion.

Categories

Resources