How to fill the image outside a bounding window with some colour - python-3.x

Is it possible to fill the image outside that bounding window with some colour like we do in paint with bucket fill using opencv,python

You can use flood fill to do that. You'll first need a mask to segment out the rectangle from your image.
First make a mask to segment out the rectangle. I did it by simple gray threshold. Once you have the mask, fill the roi with image and the rest with the color you want.
Code:
import cv2
import numpy as np
img = cv2.imread("face.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
h,w =img.shape[:2]
mask = np.zeros((h+2, w+2), np.uint8)
ret,thresh2 = cv2.threshold(gray, 10,255,cv2.THRESH_BINARY_INV)
cv2.floodFill(thresh2, mask, (350,250), (255,255,0));
thresh2 = cv2.cvtColor(thresh2,cv2.COLOR_GRAY2RGB)
thresh2[np.where((thresh2 == [0,0,0]).all(axis = 2))] = [0,255,255]
thresh2[np.where((thresh2 == [255,255,255]).all(axis=2))] = img[np.where((thresh2 == [255,255,255]).all(axis=2))]
cv2.imshow("img", thresh2)
cv2.waitKey(0)
cv2.destroyAllWindows()

ALL CREDITS TO I.NEWTON orwhatever his actual name is.. ;)
img = cv2.imread(f)
size=img.shape
dets1 = detector1(img)
for k1, d1 in enumerate(dets1):
x1=d1.left()
y1=d1.top()
w1=d1.right()
h1=d1.bottom()
cv2.rectangle(img,(x1,y1),(w1,h1),(0,0,0),1)
cv2.rectangle(img,(0,0),(x1,size[1]),(0,0,0),-1)
cv2.rectangle(img,(0,0),(size[1],y1),(0,0,0),-1)
cv2.rectangle(img,(size[1],0),(w1,size[0]),(0,0,0),-1)
img=cv2.rectangle(img,(size[1],size[0]),(0,h1),(0,0,0),-1)
cv2.imshow("img", thresh2)
cv2.waitKey(0)
cv2.destroyAllWindows()

Related

Adding a border around Region of Interest using python

I have a code which takes images from a folder, crops the region of interest around it using the ROI function, and then removes the background using the rembg library. But I want a border around that image, around that specific object itself, like the one we get in segmentation, except keeping the colours and the object intact. [NOT A RECTANGULAR BORDER]. Can anyone help and tell me how to do it?
Here is my code for reference:
import cv2
import numpy as np
import os
from os.path import join
from os import listdir
from PIL import Image
from rembg import remove
path = 'Some path'
folder = 'Some other path'
count = 1
def image_resize(image, width = None, height = None, inter = cv2.INTER_AREA):
dim = None
(h, w) = image.shape[:2]
if width is None and height is None:
return image
if width is None:
r = height / float(h)
dim = (int(w * r), height)
else:
r = width / float(w)
dim = (width, int(h * r))
resized = cv2.resize(image, dim, interpolation = inter)
return resized
for filename in os.listdir(folder):
img = cv2.imread(os.path.join(folder,filename))
if img is not None:
img = image_resize(img, height = 600)
roi = cv2.selectROI(img)
print(roi)
im_cropped = img[int(roi[1]):int(roi[1]+roi[3]),int(roi[0]):int(roi[0]+roi[2])]
rs = str(count)
rem = remove(im_cropped)
cv2.imshow("Removed Image", rem)
cv2.imwrite(os.path.join(path, rs + '.jpg'), rem)
count = count + 1
cv2.waitKey(0)
As i assume you mean a simple outline, i would suggest the following:
import cv2
import numpy as np
# Threshold the image
thresh = img > threshold
# Use binary dilation to widen the area
thresh_dil = cv2.dilate(thresh, np.ones((3, 3)) , iterations=1)
# Get the outlines by substracting the dilated image with the original area
outlines = thresh_dil - thresh
# Superimpose the outlines on your original image
img_with_outlines = img.copy()
img_with_outlines[outlines > 0] = 255
This should draw a white line around your detected object.
Note: This approach works grayscale images. For full colour images you can apply it for each channel individually.

Is there a way to tell if pyautogui is detecting the color/clicking?

import cv2
import numpy as np
import pyautogui
SCREEN_SIZE = (1920, 1080)
#define the codec
fourcc = cv2.VideoWriter_fourcc(*"XVID")
#create the video write object
out = cv2.VideoWriter("output.avi", fourcc, 20.0, (SCREEN_SIZE))
while True:
#make a screenshot
img = pyautogui.screenshot(region=(680, 785, 560, 20))
#convert these pixels to a proper numpy array to work with OpenCV
frame = np.array(img)
#convert colors from BGR to RGB
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
black = [0,0,0]
for x in range(img.width):
for y in range(img.height):
if img.getpixel((x, y)) == black:
print(x, y)
pyautogui.click(x, y)
#write the frame
out.write(frame)
#show the frame
cv2.imshow("screenshot", frame)
# if the user clicks q, it exits
if cv2.waitKey(1) == ord("q"):
break
# make sure everything is closed when exited
cv2.destroyAllWindows()
out.release()
I am creating a script to detect black squares, and click them. For some reason when using this, there is no error, but it is not clicking. Is there a way to tell whether it is detecting the color/clicking?
Edit: It does not output the coordinates, and when changing it to print "black" once finding the color, there is still no output.

How to convert ImageTk to Image?

Let's say I have some ImageTk.PhotoImage image stored in the variable imgtk. How can I convert it back to an Image.Image?
The reason is that I want to resize it, but it seems that .resize() only works for Image.Images.
I know it is awfully late, but I just came across the same issue and I just discovered that there is a getimage(imagetk) function in the ImageTk interface.
So, to get your imgtk back as an PIL Image you can do:
img = ImageTk.getimage( imgtk )
I just did a quick test on Windows (Python 3.8.5/Pillow 8.1.2/Tkinter 8.6) and it seems to work fine:
# imgtk is an ImageTk.PhotoImage object
img = ImageTk.getimage( imgtk )
img.show()
img.close()
Ok, that was not easy but I think I have a solution though you need to go into some private methods of label.image. Maybe there is a better way if so I would love to see.
import tkinter as tk
from tkinter import Label
import numpy as np
from PIL import Image, ImageTk
root = tk.Tk()
# create label1 with an image
image = Image.open('pic1.jpg')
image = image.resize((500, 750), Image.ANTIALIAS)
picture = ImageTk.PhotoImage(image=image)
label1 = Label(root, image=picture)
label1.image = picture
# extract rgb from image of label1
width, height = label1.image._PhotoImage__size
rgb = np.empty((height, width, 3))
for j in range(height):
for i in range(width):
rgb[j, i, :] = label1.image._PhotoImage__photo.get(x=i, y=j)
# create new image from rgb, resize and use for label2
new_image = Image.fromarray(rgb.astype('uint8'))
new_image = new_image.resize((250, 300), Image.ANTIALIAS)
picture2 = ImageTk.PhotoImage(image=new_image)
label2 = Label(root, image=picture2)
label2.image = picture2
# grid the two labels
label1.grid(row=0, column=0)
label2.grid(row=0, column=1)
root.mainloop()
Actually you can zoom and reduce the original picture by using the methods zoom to enlarge the picture (zoom(2) doubles the size) and subsample to reduce the size (subsample(2) halves the picture size).
for example
picture2 = label1.image._PhotoImage__photo.subsample(4)
reduces the size of the picture to a quarter and you can skip all the conversion to an Image.
According to label1.image._PhotoImage__photo.subsample.__doc__:
Return a new PhotoImage based on the same image as this widget but use only every Xth or Yth pixel. If y is not given, the default value is the same as x
and label1.image._PhotoImage__photo.zoom.__doc__:
Return a new PhotoImage with the same image as this widget but zoom it with a factor of x in the X direction and y in the Y direction. If y is not given, the default value is the same as x.

How to remove shadows from a video that has static background?

I am trying to detect moving object and remove shadow from a video that has a static background. I am using Mixture of Gaussians(MOG) method to detect moving objects. I am using opencv3 and python 3.5. How can I remove shadows from the video and foreground mask both? I have used erosion and dilation for reducing noise. But it doesn't remove the shadows.
import cv2
import numpy as np
cap = cv2.VideoCapture('TownCentreXVID.avi')
fgbg = cv2.createBackgroundSubtractorMOG2()
while(1):
_, frame = cap.read()
mask = fgbg.apply(frame)
kernel = np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
closing = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
window = cv2.namedWindow('Original', cv2.WINDOW_NORMAL| cv2.WINDOW_KEEPRATIO )
window = cv2.namedWindow('Mask', cv2.WINDOW_NORMAL| cv2.WINDOW_KEEPRATIO)
window = cv2.namedWindow('Opening', cv2.WINDOW_NORMAL| cv2.WINDOW_KEEPRATIO )
#window = cv2.namedWindow('Closing', cv2.WINDOW_NORMAL| cv2.WINDOW_KEEPRATIO)
cv2.imshow('Original',frame)
cv2.imshow('Mask',thresh)
cv2.imshow('Opening',opening)
#cv2.imshow('Closing',closing)
k = cv2.waitKey(5) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
cap.release()
The backgroundsubtractor returns a mask where foreground object are white and shadows are gray.
You can use thresholding to create a new mask without shadow, or with only the shadow.
Use the mask without the shadows to get only the foreground.
Use the mask with only shadow to replace the shadow on the background (with a reference background image).
Result:
Code:
import cv2
import numpy as np
# load image / mask
mask = cv2.imread("mask.png",0)
#threshold mask
ret, foreground = cv2.threshold(mask, 200, 255, cv2.THRESH_BINARY)
ret, shadow = cv2.threshold(mask, 200, 255, cv2.THRESH_TOZERO_INV)
# stack images vertically
res = np.concatenate((mask,foreground,shadow),axis=0)
#show image
cv2.imshow("Result",res)
cv2.waitKey(0)
cv2.destroyAllWindows()

Opencv draw a rectangle in a picture were never shown

Hey everybody i have some trouble using opencv 3.x and python 3.x.
What i want to do is to draw a basic rectangle in a picture but the rectangle will never be drawn.
I read this similar thread but it doesn't helped me with my fault.
Python OpenCV: mouse callback for drawing rectangle
It would be nice if someone could give me a hint.
#!/usr/bin/env python3
import cv2
import numpy as np
Path = 'picture.jpg'
image_float_size = 400.0
image_int_size = int(image_float_size)
color = [0,255,0]
rectangle = False
def on_event(event,x,y,flags,param):
global startpointx,startpointy,rectangle
if event == cv2.EVENT_LBUTTONDOWN:
rectangle = True
startpointx = x
startpointy = y
print('Down',x,y) #debugging
cv2.rectangle(resized,(x,y),(x,y),(0,255,0),-1)
elif event == cv2.EVENT_LBUTTONUP:
rectangle = False
print('Up',x,y)
cv2.rectangle(resized,(startpointx,startpointy),(x,y),(0,255,0),-1)
elif event == cv2.EVENT_MOUSEMOVE:
if rectangle:
print('Move',startpointx,startpointy,x,y)#debugging
cv2.rectangle(resized,(startpointx,startpointy),(x,y),(0,255,0),-1)
# Read the image and convert it into gray
image = cv2.imread(Path)
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# resize the image
ration = image_float_size / gray_image.shape[1]
dim = (image_int_size,int(gray_image.shape[0]*ration))
resized = cv2.resize(gray_image, dim, interpolation = cv2.INTER_AREA)
# set window for the image
cv2.namedWindow('window')
# mouse callback
cv2.setMouseCallback('window',on_event)
# wait forever for user to press any key, after key pressed close all windows
while True:
cv2.imshow('window',resized)
if cv2.waitKey(0):
break
cv2.destroyAllWindows()
You perform drawing (displaying of an image by using cv2.imshow) only once because cv2.waitKey(0) waits indefinitely. If you use some non-zero argument it will wait for that number of milliseconds. But notice that you're constantly rewriting/modifying an image. This is probably not what you want. I think you need to create a temporary (drawing) copy of an image first and restore it each time from original one before new drawing (rectangle).
#!/usr/bin/env python3
import cv2
import numpy as np
Path = 'data/lena.jpg'
image_float_size = 400.0
image_int_size = int(image_float_size)
color = [0,255,0]
rectangle = False
def on_event(event,x,y,flags,param):
global draw_image
global startpointx,startpointy,rectangle
if event == cv2.EVENT_LBUTTONDOWN:
rectangle = True
startpointx = x
startpointy = y
print('Down',x,y) #debugging
draw_image = resized.copy()
cv2.rectangle(draw_image,(x,y),(x,y),(0,255,0))
elif event == cv2.EVENT_LBUTTONUP:
rectangle = False
print('Up',x,y)
draw_image = resized.copy()
cv2.rectangle(draw_image,(startpointx,startpointy),(x,y),(0,255,0))
elif event == cv2.EVENT_MOUSEMOVE:
if rectangle:
print('Move',startpointx,startpointy,x,y)#debugging
draw_image = resized.copy()
cv2.rectangle(draw_image,(startpointx,startpointy),(x,y),(0,255,0))
# Read the image and convert it into gray
image = cv2.imread(Path)
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# resize the image
ration = image_float_size / gray_image.shape[1]
dim = (image_int_size,int(gray_image.shape[0]*ration))
resized = cv2.resize(gray_image, dim, interpolation = cv2.INTER_AREA)
draw_image = resized.copy()
# set window for the image
cv2.namedWindow('window')
# mouse callback
cv2.setMouseCallback('window',on_event)
while True:
cv2.imshow('window', draw_image)
ch = 0xFF & cv2.waitKey(1)
if ch == 27:
break
cv2.destroyAllWindows()

Resources