I am attempting to use a grid of rectangles over my live webcam video with dynamically changing transparencies across the screen. The test I am doing here does not hold the dynamic aspect, but the rectangles should be different transparencies according to their normalized value in the rng array of values. All of the rectangles in the grid are resulting as the same transparency.
#===========================================================================================================================
# Import Necessary Libraries
import cv2 as cv
import numpy as np
from sklearn import preprocessing
import math
import itertools
#===========================================================================================================================
# Define a Video Capture
vid = cv.VideoCapture(0)
#===========================================================================================================================
# Initialize
height = int(input('Define Number of Rows: '))
width = int(input('Define Number of Columns: '))
hxw = height * width
#===========================================================================================================================
# Create a Random Number Array
randnums= np.random.randint(1,30000,(hxw))
normran = (randnums - randnums.min())/((randnums.max() - randnums.min()))
print(normran)
#===========================================================================================================================
# Create a Color Map
def colorgrid(img, normran):
h, w, _ = img.shape
dy = h / height
dx = w / width
g = np.linspace(0, w-dx, width)
b = np.linspace(g[1], w, width)
t = np.linspace(0, h-dy, height)
k = np.linspace(t[1], h, height)
overlay = img.copy()
thickness = 2
color3 = (0, 0, 0)
color2 = (0, 0, 255)
thickness1 = -1
i = -1
for spy, spay in itertools.product(t, k):
for spx, spax in itertools.product(g, b):
i = i + 1
alpha = normran[i]
spx = int(round(spx))
spax = int(round(spax))
spy = int(round(spy))
spay = int(round(spay))
startx = (spx, spy)
stopx = (spax, spay)
print(alpha)
print(i)
cv.rectangle(overlay, startx, stopx, color2, thickness1)
imgnew = cv.addWeighted(overlay, normran[i], img, 1, 0)
cv.rectangle(img, startx, stopx, color3, thickness)
if (i) == (hxw-1):
i = -1
break
return imgnew
#===========================================================================================================================
# Use the Camera
while(True):
# the 'q' button is set as the
# quitting button you may use any
# desired button of your choice
if cv.waitKey(1) & 0xFF == ord('q'):
break
# Capture the video frame
# by frame
ret, frame = vid.read()
frame = cv.flip(frame,1)
clr = colorgrid(frame, normran)
# Display the resulting frame
cv.imshow('Test', clr)
# After the loop release the cap object
vid.release()
# Destroy all the windows
cv.destroyAllWindows()
I tried to use a for loop that increments for the number of grid spaces defined and will end when the number of spaces are up. This is successful, but it seems as the image only results in the alpha value associated with normran[end]. The output of the input of 3 rows and 3 columns is:
0.90409176345848
0
0.07855800279531544
1
0.4564557328064003
2
0.9938310280013495
3
0.0
4
0.16005590630873778
5
1.0
6
0.9541182707600366
7
0.1760566774302376
8
0.90409176345848
0
0.07855800279531544
1
0.4564557328064003
2
0.9938310280013495
3
0.0
4
0.16005590630873778
5
1.0
6
0.9541182707600366
7
0.1760566774302376
8
^^ this repeats infinitely
An image of the live webcam
Related
I am using cv2.rectangles to attempt to draw a grid on an np.zeros window. I am looking for a way to use a for or while loop to automattically repeat the drawing of these lines (long thick rectangles) until the horizontal lines meet the bottom of the window and vertical lines the width.
I'd like for the algorithm to populate with rectangles of unchanging size/space between them, to the edges of any window size that is input. So the window would change size but the size of each grid line/square would not.
current code.
import cv2
import numpy as np
#create np.zeros window
frame = np.zeros((600,600, 3), np.uint8)
width = frame.shape[0]
height = frame.shape[1]
#starting points for vertical line, #earlier while loop wouldn't let me use'<' with tuples
vertp1y = 25
vertp1x = 0
vertp2y = 35
vertp2x = height
#starting points for horizontal lines
horizp1y = 0
horizp1x = 25
horizp2y = width
horizp2x = 35
#pt1 and pt2 parameters set this way to attempting using as variables in later while loop
vert_line=cv2.rectangle(frame, (vertp1y, vertp1x), (vertp2y, vertp2x), (0,225,0), -1)
horiz_line=cv2.rectangle(frame, (horizp1y, horizp1x), (horizp2y, horizp2x), (0,225,0), -1)
while vertp2y < width:
vertp1y = vertp1y + 25
vertp2y = vertp2y + 25
if vertp2y > width:
break
cv2.imshow('frame', frame)
cv2.waitKey(0)
cv2.destroyAllWindows()
when I run this I get no errors but the window with the two lines (rectangles) are returned unchanged.
I also attempted to use warpAffine translation using vert_line as the src, same issue, unchanged window returned.
M = np.float32([[-1,0,25], [0,1,0]])
vert_line_2 = cv2.warpAffine(vert_line, M, (frame.shape[1], frame.shape[0]))
I think the easiest option would be to draw the lines and columns instead of rectangles. To do this, you could try something like this:
import matplotlib.pyplot as plt
import numpy as np
class BoxesDrawer:
def __init__(self, box_size: tuple, border_size: int, color: tuple = (0, 255, 0)):
self.box_size = box_size
self.half_border_size = int(border_size // 2)
self.color = color
def draw_on(self, frame: np.asarray) -> np.asarray:
self._draw_row(frame)
self._draw_columns(frame)
return frame
def _draw_row(self, frame: np.asarray) -> np.asarray:
row_size = self.box_size[0]
index = 0
while True:
index += row_size
if index > frame.shape[0]:
break
frame[index - self.half_border_size:index + self.half_border_size, :, :] = self.color
return frame
def _draw_columns(self, frame: np.asarray) -> np.asarray:
column_size = self.box_size[1]
index = 0
while True:
index += column_size
if index > frame.shape[0]:
break
frame[:, index - self.half_border_size:index + self.half_border_size, :] = self.color
return frame
if __name__ == '__main__':
frame = np.zeros((224, 224, 3))
drawer = BoxesDrawer(box_size=(20, 20), border_size=3, color=(0, 255, 0))
drawer.draw_on(frame)
plt.figure()
plt.imshow(frame)
plt.show()
Not really a good looking code, though it should work for your use case. I'm 100% sure this could be optimized and you can even modify it to better fit your use case, but the baseline is here.
I was trying to add a logo to my video using OpenCv by modifying a code that I found online (that was made for merging two pictures). The process worked somehow but the added logo was displayed only like a quarter of it, not full.
So my questions are :
How do I change the size and the cropping of the added logo ?
Is it possible also to change it's position on the video (like in the bottom)?
Any help or documentation (for beginner) would be appreciated.
The code I used:
import cv2
import numpy as np
foreground = cv2.imread('logo.jpg')
cap = cv2.VideoCapture("video.mp4")
alpha = 0.4
while True:
ret, background = cap.read()
background = cv2.flip(background,1)
added_image = cv2.addWeighted(background[150:250,150:250,:],alpha,foreground[0:100,0:100,:],1-alpha,0)
background[150:250,150:250] = added_image
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(background,'alpha:{}'.format(alpha),(10,30), font, 1,(255,255,255),2,cv2.LINE_AA)
cv2.imshow('a',background)
k = cv2.waitKey(10)
if k == ord('q'):
break
if k == ord('a'):
alpha +=0.1
if alpha >=1.0:
alpha = 1.0
elif k== ord('d'):
alpha -= 0.1
if alpha <=0.0:
alpha = 0.0
cap.release()
cv2.destroyAllWindows()
I added comments and some ease-of-use stuff to your code to help make it easier to understand what each part is supposed to be doing. The overall structure is the same, but hopefully this helps you figure out why the overlay looks the way it does.
import cv2
import numpy as np
# load image and start video
foreground = cv2.imread('logo.jpg')
cap = cv2.VideoCapture("video.mp4")
# init font
font = cv2.FONT_HERSHEY_SIMPLEX
# position of logo on video
top_left = [0, 0] # the top-left corner of your logo goes here
tx = top_left[0] # less typing later
ty = top_left[1]
# crop of logo
left = 0
right = 100
top = 0 # y = 0 is the top of the image
bottom = 100
# calculate width and height of logo crop
width = right - left
height = bottom - top
# main loop
alpha = 0.4
while True:
# read image
ret, background = cap.read()
# horizontal flip to create mirror image
background = cv2.flip(background,1)
# quick primer on 2d image slicing:
# images are [y,x]
# crop = image[0:100, 300:500] will be a 200x100 image (width x height)
# the logo slice should give you a decent idea of what's going on
# WARNING: there are no out-of-bounds checks here, make sure that
# tx + width is less than the width of the background
# ty + height is less than the height of the background
# right is less than the width of the foreground
# bottom is less than the height of the foreground
# get crops of background and logo
background_slice = background[ty:ty+height, tx:tx+width];
logo_slice = foreground[top:bottom, left:right];
# since we don't change our crop, we could do the logo slice outside the loop
# but I'll keep it here for clarity
# add slice of logo onto slice of background
added_image = cv2.addWeighted(background_slice,alpha,logo_slice,1-alpha,0)
background[ty:ty+height, tx:tx+width] = added_image
# draw alpha value
cv2.putText(background,'alpha:{}'.format(alpha),(10,30), font, 1,(255,255,255),2,cv2.LINE_AA)
# show image
cv2.imshow('a',background)
k = cv2.waitKey(10)
# process keypresses
if k == ord('q'):
break
if k == ord('a'):
alpha +=0.1
if alpha >=1.0:
alpha = 1.0
elif k== ord('d'):
alpha -= 0.1
if alpha <=0.0:
alpha = 0.0
# close
cap.release()
cv2.destroyAllWindows()
Im trying to insert one picture(transparent .png) into another on certain coordinates.
While the solution from How to add an image over another image using x,y coordinates?
frame[y: y+insert_size[1], x: x+insert_size[0]] = image (where insert_size - width and height of inserted picture) works, i also dont want black pixels(thats how opencv represents transparent pixels) on the final image.
I wrote a function that iterates pixel by pixel, and while it works - it is horribly slow(it completes about 2 image inserts per second), code:
def insert_image(frame, image, insert_coordinates, masked_value):
img_height = len(image)
img_width = len(image[0])
mask = np.ndarray((3,), np.uint8, buffer=np.array(masked_value))
y_diff = 0 #current vertical position in insert picture
for y, line in enumerate(frame):
if y_diff == img_height-1:
continue #interested until last row
if y < insert_coordinates[1] or y > insert_coordinates[1]+img_height:
continue #interested only in rows that will be changed
else:
x_diff = 0 #current horizontal position in insert picture
for x, col in enumerate(line):
if x_diff == img_width-1:
continue #interested until last column
if x < insert_coordinates[0] or x > insert_coordinates[0]+img_width:
continue #interested only in columns that will be changed
else:
if (image[y_diff][x_diff] != mask).all():
frame[y][x] = image[y_diff][x_diff] #setting pixel value if its not of masked value
x_diff += 1
y_diff += 1
return frame
maybe there is a smarter way to do so?
opencv version 4.5.0
numpy version 1.20.0rc1
UPDATE:
By "insert" i do mean assign a pixel value from image to some pixel of frame.
i added data and code for reproducible example(also modified function so its a bit faster):
"frame" - original picture, that image will be added to to, has red square sized (500,500) at (100,100) coordinates
"image" - transparent .png, sized (500,500) that will be "inserted" into original frame
"result1" - result, where red pixels were replaced with black "transparent" pixels from inserted image
"result2" - desired result
code, requires opencv-python and numpy modules: example.py
import cv2
import numpy as np
import copy
def insert_image_v2(frame, image, insert_coordinates, masked_value):
img_height = len(image)
img_width = len(image[0])
mask = np.ndarray((3,), np.uint8, buffer=np.array(masked_value))
y_diff = 0
for y in range(insert_coordinates[1], insert_coordinates[1]+img_height, 1):
x_diff = 0
for x in range(insert_coordinates[0], insert_coordinates[0]+img_width, 1):
if (image[y_diff][x_diff] != mask).all():
frame[y][x] = image[y_diff][x_diff]
x_diff += 1
y_diff += 1
return frame
if __name__ == "__main__":
frame = cv2.imread('frame.png')
image = cv2.imread('image.png')
insert_size = (image.shape[0], image.shape[1])
insert_coordinates = (100, 100)
x = insert_coordinates[0]
y = insert_coordinates[1]
result1 = copy.deepcopy(frame)
result1[y: y+insert_size[1], x: x+insert_size[0]] = image
result2 = insert_image_v2(frame, image, insert_coordinates, [0,0,0])
cv2.imshow('result1', result1)
cv2.imshow('result2', result2)
cv2.imwrite('result1.jpg', result1)
cv2.imwrite('result2.jpg', result2)
print()
Found a solution in Image alpha composite with OpenCV, it is about 20 times faster than what i had. code:
import cv2
import numpy as np
import time
def insert_image_v2(frame, image, insert_coordinates):
x = insert_coordinates[0]
y = insert_coordinates[1]
insert_size = (image.shape[1], image.shape[0])
background = frame[y: y+insert_size[1], x: x+insert_size[0]]
foreground = image
kernel = np.ones((5,5), np.uint8)
image_gray = cv2.cvtColor(foreground, cv2.COLOR_BGR2GRAY)
ret, mask = cv2.threshold(image_gray, 1, 255, cv2.THRESH_BINARY_INV)
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
output = np.zeros(foreground.shape, dtype=foreground.dtype)
for i in range(3):
output[:,:,i] = background[:,:,i] * (opening/255)+foreground[:,:,i]*(1-opening/255)
frame[y: y+insert_size[1], x: x+insert_size[0]] = output
return frame
if __name__ == "__main__":
frame = cv2.imread('frame.png')
image = cv2.imread('image_1.png')
insert_size = (image.shape[0], image.shape[1])
insert_coordinates = (100, 100)
t1 = time.time()
frame = insert_image_v2(frame, image, insert_coordinates)
t2 = time.time()
print(f"{t2-t1}")
cv2.imshow('img', frame)
cv2.waitKey(0)
I am working on a problem in order to warp the perspective of images to get a front-parallel view. Non-front-parallel plane of the image is rotated by angle alpha with respect to x-axis and by angle beta with respect to y-axis, assuming optical axis of camera as z-axis. Can we estimate alpha and beta? If so how? What is the mathematical procedure one should follow in order to find these angles? Are there any library functions for this?
Thanks in advance for any help!
import cv2
import numpy as np
circles = np.zeros((4,2),np.int)
counter = 0
def mousePoints(event,x,y,flags,params):
global counter
if event == cv2.EVENT_LBUTTONDOWN:
circles[counter] = x,y
counter = counter + 1
print(circles)
img = cv2.imread("DSC_0273.JPG")
img = cv2.resize(img,(1500,1000))
q = 0
while True:
if counter == 4:
q = q+1
height1,width1 = 1080,1920
pts1 = np.float32([circles[0],circles[1],circles[2],circles[3]])
width = np.sqrt((circles[1][0] - circles[0][0])**2 + (circles[1][1] - circles[0][1])**2)
height = np.sqrt((circles[2][1] - circles[0][1])**2 + (circles[2][0] - circles[0][0])**2)
width = int(np.round(width))
height = int(np.round(height))
x1,y1 = circles[0]
pts2 = np.float32([[x1,y1],[(x1+width),y1],[(x1+width),(y1+height)],[x1,(y1+height)]])
matrix = cv2.getPerspectiveTransform(pts1,pts2)
if q == 1:
print(matrix.shape)
print(matrix)
imgOutput = cv2.warpPerspective(img,matrix,(width1,height1))
cv2.imshow("Output Image ", imgOutput)
for x in range (0,4):
cv2.circle(img,(circles[x][0],circles[x][1]),3,(0,255,0),cv2.FILLED)
cv2.imshow("Original Image ", img)
cv2.setMouseCallback("Original Image ", mousePoints)
cv2.waitKey(1)
I am trying to create my own face filtering augmented reality program in open-cv. The idea is it will map a beaver to the users face. Currently, I am unable to get proper transperency when loading this image with 'cv2.imread(...)'. It looks black in the background, and often shows partially in certain areas that are white. When I open this image in photoshop, I am fully capable of moving this image on top of a background with expected transparency results. I am wondering if the alpha is not getting rendered properly. Here is the relevent code where I am loading the image in.
import numpy
import cv2
def augment_stream(face: numpy.array, augment: numpy.array) -> numpy.array:
face_h, face_w, _ = face.shape
augment_h, augment_w, _ = augment.shape
scalar = min(face_h / augment_h, face_w / augment_w)
delta_augment_h = int(scalar * augment_h)
delta_augment_w = int(scalar * augment_w)
delta_augment_shape = (delta_augment_w, delta_augment_h)
resized_augment = cv2.resize(augment, delta_augment_shape)
augmented_face = face.copy()
dark_pixels = (resized_augment < 250).all(axis=2)
offset_x = int((face_w - delta_augment_w) / 2)
offset_y = int((face_h - delta_augment_h) / 2)
augmented_face[offset_y: offset_y+delta_augment_h, offset_x: offset_x+delta_augment_w][dark_pixels] = resized_augment[dark_pixels]
return augmented_face
def main():
stream = cv2.VideoCapture(0)
cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
augment = cv2.imread('assets/normal.png')
# tmp = cv2.cvtColor(augment, cv2.COLOR_BGR2GRAY)
# _,alpha = cv2.threshold(tmp,0,255,cv2.THRESH_BINARY)
# b, g, r = cv2.split(augment)
# rgba = [b,g,r, alpha]
# dst = cv2.merge(rgba,4)
# cv2.imwrite("assets/normal.png", dst)
while True:
ret, border = stream.read()
border_h, border_w, _ = border.shape
bw = cv2.equalizeHist(cv2.cvtColor(border, cv2.COLOR_BGR2GRAY))
rects = cascade.detectMultiScale(bw, scaleFactor=1.3, minNeighbors=4, minSize=(30, 30), flags=cv2.CASCADE_SCALE_IMAGE)
for x, y, w, h in rects:
y0 = int(y - 0.25*h)
y1 = int(y + 0.75*h)
x0 = x
x1 = x + w
if x0 < 0 or x1 > border_w or y0 < 0 or y1 > border_h:
continue
border[y0: y1, x0: x1] = augment_stream(border[y0: y1, x0: x1], augment)
cv2.imshow('border', border)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
stream.release()
cv2.destroyAllWindows()
if __name__ == '__main__':
main()
Using example from question overlay a smaller image on a larger image python OpenCv
I reduced it to show only how to put image
use cv2.IMREAD_UNCHANGED to load it as RGBA.
split it to RGB and A
use A to create masks for image and border
use loop to add channels
import cv2
stream = cv2.VideoCapture(0)
# load RGBA
augment = cv2.imread('image.png', cv2.IMREAD_UNCHANGED) # load RGBA
# make it smaller then frame - only for test
W = 320
H = 240
augment = cv2.resize(augment, (W, H))
# split image and alpha
image = augment[:,:,0:3]
alpha = augment[:,:,3]
mask_image = alpha / 255.0
mask_border = 1.0 - mask_image
# ROI - region of interest
x1 = 200
y1 = 100
x2 = x1 + W
y2 = y1 + H
while True:
ret, border = stream.read()
# copy only in some region (ROI) (don't assign to variable) but gives worse result
#cv2.copyTo(image, alpha, border[y1:y2, x1:x2])
for c in range(0, 3): # channels RGB
border[y1:y2, x1:x2, c] = (image[:, :, c]*mask_image + border[y1:y2, x1:x2, c]*mask_border)
cv2.imshow('border', border)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
stream.release()
cv2.destroyAllWindows()
BTW: I tried to use cv2.copyTo(image, mask_image, border) but it gives worse result - maybe mask/alpha needs 3 channels.
It seems it can be done in C/C++ - how to insert a small size image on to a big image