I'm trying to plot a rotated xyz axis using OpenCV projectPoints function. When testing rotation about the X and Y axis, I noticed that the Z axis is much longer than the X and Y axis when they should be the same length, but I am unsure why. Any help would be greatly appreciated!
Here are some images I generated:
Here is my code:
import numpy as np
import cv2
import sys
def rotByXAxis(angle):
return np.array([
[1, 0, 0],
[0, np.cos(angle), -np.sin(angle)],
[0, np.sin(angle), np.cos(angle)],
], dtype=np.float32)
def rotByYAxis(angle):
return np.array([
[ np.cos(angle), 0, np.sin(angle)],
[ 0, 1, 0],
[-np.sin(angle), 0, np.cos(angle)],
], dtype=np.float32)
def rotByZAxis(angle):
return np.array([
[ np.cos(angle), np.sin(angle), 0],
[-np.sin(angle), np.cos(angle), 0],
[ 0, 0, 1],
], dtype=np.float32)
def createCanvas(self, height, width):
blank_image = np.zeros((height, width, 3), np.uint8)
blank_image[:, :] = (255, 255, 255)
return blank_image
def draw3DAxis(self, image, rvec, tvec, cameraMatrix, scale=1, dist=None):
"""
Draw a 6d of axis (XYZ -> RGB) in the given rotation and translation
:param image - rgb numpy array
:rvec - euler rotations, numpy array of length 3,
use cv2.Rodrigues(R)[0] to convert from rotation matrix
:tvec - 3d translation vector, in meters (dtype must be float)
:cameraMatrix - intrinsic calibration matrix , 3x3
:scale - factor to control the axis lengths
:dist - optional distortion coefficients, numpy array of length 4. If None distortion is ignored.
"""
image = image.astype(np.float32)
dist = np.zeros(4, dtype=float) if dist is None else dist
if rvec.shape == (3, 3):
rvec, _ = cv2.Rodrigues(rvec)
points = scale * np.float32([
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
[0, 0, 0]
]).reshape(-1, 3)
axis_points, _ = cv2.projectPoints(points, rvec, tvec, cameraMatrix, dist)
print(axis_points)
image = cv2.arrowedLine(
image,
tuple(int(e) for e in axis_points[3].ravel()),
tuple(int(e) for e in axis_points[0].ravel()),
(255, 0, 0),
3,
tipLength=0.01 * scale,
)
image = cv2.arrowedLine(
image,
tuple(int(e) for e in axis_points[3].ravel()),
tuple(int(e) for e in axis_points[1].ravel()),
(0, 255, 0),
3,
tipLength=0.01 * scale,
)
image = cv2.arrowedLine(
image,
tuple(int(e) for e in axis_points[3].ravel()),
tuple(int(e) for e in axis_points[2].ravel()),
(0, 0, 255),
3,
tipLength=0.01 * scale,
)
return image
if __name__ == "__main__":
height = 300
width = 400
image = createCanvas(height, width)
rvec = rotByZAxis(-pi/2)
cameraMatrix = np.array([
[1.0, 0, width/2],
[ 0, 1.0, height/2],
[ 0, 0, 1.0 ]], dtype=np.float32)
image = draw3DAxis(
image,
rvec=rvec,
tvec=np.zeros(3, dtype=float),
cameraMatrix=cameraMatrix,
scale=20,
)
cv2.imshow("output", image)
key = cv2.waitKey(0)
if key:
sys.exit(1)
Related
I want to merge RGB channel in the function I created after I normalized each channel with the value of r,g,b that I define before, here is the equation
def color_norm(image_norm):
pixels = np.array(image_norm)
ar_mean = np.mean(pixels, axis=(0,1))
(H,W) = image_norm.shape[:2]
for x in range(H):
for y in range(W):
Ri = image_norm[:,:,0]
Gi = image_norm[:,:,1]
Bi = image_norm[:,:,2]
R = Ri[x,y] = np.min((Ri[x,y]/(ar_mean[0]))*(r))
if R > 255:
# saturate value
R = 255
else:
# add normally
R = R
G = Gi[x,y] = np.min((Gi[x,y]/(ar_mean[1]))*(g))
if G > 255:
# saturate value
G = 255
else:
# add normally
G = G
B = Bi[x,y] = np.min((Bi[x,y]/(ar_mean[2]))*(b))
if B > 255:
# saturate value
B = 255
else:
# add normally
B = B
image_norm[x,y] = [R,G,B]
when I tried in single image it works,but when I pass this function to the dataset(contain 5 images) it raise an error TypeError: Image data of dtype object cannot be converted to float anyone knows how to merge the RGB channel so that I can pass the function?
A full working code here
import cv2
import numpy as np
def color_norm(img, weights):
kb, kg, kr = weights
b = img[:, :, 0].astype(np.float32)
g = img[:, :, 1].astype(np.float32)
r = img[:, :, 2].astype(np.float32)
bm = np.mean(b)
gm = np.mean(g)
rm = np.mean(r)
img_out = np.empty_like(img, dtype=np.uint8)
img_out[:, :, 0] = np.minimum(b*kb/bm,255).astype(np.uint8)
img_out[:, :, 1] = np.minimum(g*kg/gm,255).astype(np.uint8)
img_out[:, :, 2] = np.minimum(r*kr/rm,255).astype(np.uint8)
return img_out
fin = r'D:\ColorfulMacaws-640x480.jpg'
img_bgr = cv2.imread(fin, cv2.IMREAD_COLOR)
cv2.imshow('Image before norm', img_bgr)
ws = (40, 50, 60)
imout = color_norm(img_bgr, ws)
cv2.imshow('Image after norm', imout)
cv2.waitKey(0)
cv2.destroyAllWindows()
Based on the code I tried, here's a solution to your question.
import numpy as np
def normalize_to_grayscale(image: np.array, weights: tuple):
"""normalized a numpy array of an image to 0-255 interval , returning middle gray if all values are equal
Args:
image (np.array): input image
weights (tuple): tuple of len 3, with weights of the channels
Returns:
np.array: normalized array, same size as input image
"""
# Convert image to float
image = image.astype(float)
H, W, _ = image.shape
# Convert image to grayscale
image_gray = np.zeros((H, W))
for x in range(H):
for y in range(W):
image_gray[x, y] = np.sum(image[x, y] * weights)
# Normalize the image
image_gray -= (image_gray.min())
max_value = image_gray.max()
if max_value == 0:
return np.zeros((H, W)) + 127.5
image_gray *= (255.0/image_gray.max())
return image_gray
# Create sample image
img = np.array([[[255, 0, 0], [0, 255, 0], [0, 0, 255]],
[[0, 0, 255], [255, 0, 0], [0, 255, 0]],
[[255, 0, 0], [0, 255, 0], [0, 0, 255]]])
weights = (1, 2, 3)
normalize_to_grayscale(img, weights)
It returns a result like this.
array([[ 0. , 127.5, 255. ],
[255. , 0. , 127.5],
[ 0. , 127.5, 255. ]])
I have an image of cells which I have thresholded and also detected the cells (using cv2).
I would like to create an array with values True or False to show whether each component touches the boundaries of the image (True) or not (False).
import cv2 as cv
# Read the image you want connected components of, IN BLACK AND WHITE
img = cv.imread('../images/37983_ERSyto/cellpaintingfollowup-reimage_a01_s1_w26ae36209-938b-45ef-b166-3aba3af125df.tif', cv.IMREAD_GRAYSCALE)
seed_pt = (100, 800) #point in the background
fill_color = 0
mask = np.zeros_like(img)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (6, 5))
for th in range(7,70):
#creates a copy of the mask:
prev_mask = mask.copy()
#thresholded image:
mask = cv.threshold(img, th, 22331, cv.THRESH_BINARY)[1]
#FloodFill: fill a connected component starting from the seed point with the specified color.
mask = cv.floodFill(mask, None, seed_pt, fill_color)[1]
#cv.bitwise: calculates the per-element bit-wise disjunction of two arrays or an array and a scalar. Superposition of thresholded images
mask = cv.bitwise_or(mask, prev_mask)
#clean speckles
mask = cv.morphologyEx(mask, cv.MORPH_OPEN, kernel)
#compute the connected components labeled image of boolean image and also produce a statistics output for each label
connectivity = 8 #You need to choose 4 or 8 for connectivity type.
#OBTAIN FEATURE OF THE AREA IN PIXELS OF THE CELLS
stats = cv.connectedComponentsWithStats(mask, connectivity, cv.CV_32S)[2]
label_area = stats[1:, cv.CC_STAT_AREA] #we dont include the first element because it represents the area of the background
#OBTAIN FEATURES OF THE CENTROID POSITION
centroids = cv.connectedComponentsWithStats(mask, connectivity, cv.CV_32S)[3]
label_centroids_x = centroids[1:, 0] #dont include the first element because it represents the background
label_centroids_y = centroids[1:,1]
#HIGHT: The vertical size of the bounding box.
label_hight = stats[1:, cv.CC_STAT_HEIGHT]
#WIDTH: The horizontal size of the bounding box.
label_width = stats[1:, cv.CC_STAT_WIDTH]
#TOUCHING IMAGE BOUNDARIES: is the component touching the boundaries of the matrix/image?--> True/False
label_bounary = #boolean array
I first thought about searching for the contour of every component and defining some restriction, but I have troubles understanding how the labels of every component are stored and therefore, I could not select the desired components.
Here is the image:
Thank you very much in advance.
Using your code (thanks for commenting), I got this mask. It's possible it's not the same since .jpg compression can mess with an image (it's not a lossless compression scheme)
#fmw42 is exactly right, he commented before I could finish my code
import cv2 as cv
import numpy as np
# Read the image you want connected components of, IN BLACK AND WHITE
img = cv.imread('cells.jpg', cv.IMREAD_GRAYSCALE)
seed_pt = (100, 800) #point in the background
fill_color = 0
mask = np.zeros_like(img)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (6, 5))
for th in range(7,70):
#creates a copy of the mask:
prev_mask = mask.copy()
#thresholded image:
mask = cv.threshold(img, th, 22331, cv.THRESH_BINARY)[1]
#FloodFill: fill a connected component starting from the seed point with the specified color.
mask = cv.floodFill(mask, None, seed_pt, fill_color)[1]
#cv.bitwise: calculates the per-element bit-wise disjunction of two arrays or an array and a scalar. Superposition of thresholded images
mask = cv.bitwise_or(mask, prev_mask)
#clean speckles
mask = cv.morphologyEx(mask, cv.MORPH_OPEN, kernel)
# show mask
cv.imshow("Mask", mask);
cv.waitKey(0);
# contours OpenCV 3.4, if you're using OpenCV 2 or 4, it returns (contours, _)
_, contours, _ = cv.findContours(mask, cv.RETR_TREE, cv.CHAIN_APPROX_NONE);
# get bounds and check if they're touching edge
height, width = mask.shape[:2];
touching_edge = []; # boolean array, index matches the contours list
for con in contours:
# get bounds
x, y, w, h = cv.boundingRect(con);
# check if touching edge
on_edge = False;
if x <= 0 or (x + w) >= (width - 1):
on_edge = True;
if y <= 0 or (y + h) >= (height - 1):
on_edge = True;
# add to list
touching_edge.append(on_edge);
# mark the contours on the edge
colored = cv.cvtColor(mask, cv.COLOR_GRAY2BGR);
for index in range(len(contours)):
if touching_edge[index]:
# drawContours(image, contour_list, index, color, thickness) # -1 is filled
cv.drawContours(colored, contours, index, (50,50,200), -1);
# show
cv.imshow("Colored", colored);
cv.waitKey(0);
If you're open to using scikit-image, you can try clear_border:
>>> import numpy as np
>>> from skimage.segmentation import clear_border
>>> labels = np.array([[0, 0, 0, 0, 0, 0, 0, 1, 0],
... [1, 1, 0, 0, 1, 0, 0, 1, 0],
... [1, 1, 0, 1, 0, 1, 0, 0, 0],
... [0, 0, 0, 1, 1, 1, 1, 0, 0],
... [0, 1, 1, 1, 1, 1, 1, 1, 0],
... [0, 0, 0, 0, 0, 0, 0, 0, 0]])
>>> clear_border(labels)
array([[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 1, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 1, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]])
I have a neural network that outputs segments of a face - I'm working on a function that combines these segments together and then clones them into a real face.
Example images are here: https://imgur.com/a/HnpqhEE, I do not have the reputation to include them inline.
So far my function takes the makeup face and lips segment and combines them with addition. The left and right eyes are then cloned in with seamlessClone (the right eye is flipped first).
Then the combined makeup segments are cloned into the normal face.
Very occasionally, my combination function fails and returns a (-215:Assertion failed) 0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols && 0 <= roi.y && 0 <= roi.height && roi.y + roi.height <= m.rows in function 'cv::Mat::Mat' error.
My function is below, I have only seen it error at the last seamlessClone
def combineFace(images, radius = 70):
# Given image segments and eye radii, combine face.
realFace = tensor2im(images['realNormal'])
makeupFace = tensor2im(images['fakeMakeupFace'])
makeupLeft = tensor2im(images['fakeMakeupLeft'])
makeupRight = tensor2im(images['fakeMakeupRight'])
makeupLips = tensor2im(images['fakeMakeupLips'])
makeupRight = cv2.flip(makeupRight, 1)
# I use cv2 and dlib to get face landmarks and interesting points.
normalLandmarks = faceLandmarks(realFace)
facePoints = getFacePoints(normalLandmarks)
# PP means pupil points
outerPoints, leftPP, rightPP, lipPoints, eyeMids = facePoints
# eyeMid is (x, y) of center of eye obtained from landmark points
leftEye = eyeMids[0]
rightEye = eyeMids[1]
faceMask = np.zeros(realFace.shape, realFace.dtype)
cv2.fillPoly(faceMask, [outerPoints], [255, 255, 255])
cv2.fillPoly(faceMask, [lipPoints], [0, 0, 0])
cv2.fillPoly(faceMask, [leftPP], [0, 0, 0])
cv2.fillPoly(faceMask, [rightPP], [0, 0, 0])
# Occasionally, the eye segments overlap eachother so I cut the right eye from the left and vice
# versa
leftMask = np.zeros(realFace.shape, realFace.dtype)
cv2.circle(leftMask, leftEye, radius, [255, 255, 255], -1)
cv2.circle(leftMask, rightEye, radius, [0, 0, 0], -1)
# Errors if i do not use UMat
cv2.circle(cv2.UMat(makeupLeft), rightEye, radius, [0, 0, 0], -1)
rightMask = np.zeros(realFace.shape, realFace.dtype)
cv2.circle(rightMask, rightEye, radius, [255, 255, 255], -1)
cv2.circle(rightMask, leftEye, radius, [0, 0, 0], -1)
cv2.circle(cv2.UMat(makeupRight), leftEye, radius, [0, 0, 0], -1)
# Combine face output and lips
baseCombine = makeupFace + makeupLips
# Left Eye
output = cv2.seamlessClone(makeupLeft, baseCombine, leftMask, leftEye, cv2.MIXED_CLONE)
output = cv2.seamlessClone(makeupRight, output, rightMask, rightEye, cv2.MIXED_CLONE)
# Get center of face
faceRect = cv2.boundingRect(outerPoints)
x, y, w, h = faceRect
output = cv2.bitwise_and(output, faceMask)
center = ( x + w // 2, y + h // 2)
# I have only seen the function error at this point
combinedFace = cv2.seamlessClone(output, realFace, faceMask, center, cv2.MIXED_CLONE)
return combinedFace
Any idea why this is occasionally erroring?
All input images have the form (256, 256, 3)
This version of the function works much better. There was something wrong with my face center calculation which was causing the error
def combineFace(images, radius = 70):
# Given image segments and eye radii, combine face.
realFace = tensor2im(images['realNormal'])
makeupFace = tensor2im(images['fakeMakeupFace'])
makeupLeft = tensor2im(images['fakeMakeupLeft'])
makeupRight = tensor2im(images['fakeMakeupRight'])
makeupLips = tensor2im(images['fakeMakeupLips'])
# Right eye is flipped before input into the network.
makeupRight = cv2.flip(makeupRight, 1)
normalLandmarks = faceLandmarks(realFace)
facePoints = getFacePoints(normalLandmarks)
outerPoints, leftPP, rightPP, lipPoints, eyeMids = facePoints
leftEye = eyeMids[0]
rightEye = eyeMids[1]
leftMask = np.zeros(makeupLeft.shape, makeupLeft.dtype)
cv2.circle(leftMask, leftEye, radius, [255, 255, 255], -1)
cv2.circle(leftMask, rightEye, radius, [0, 0, 0], -1)
# Errors if i do not use cv2.UMat
cv2.circle(cv2.UMat(makeupLeft), rightEye, radius, [0, 0, 0], -1)
rightMask = np.zeros(makeupRight.shape, makeupRight.dtype)
cv2.circle(rightMask, rightEye, radius, [255, 255, 255], -1)
cv2.circle(rightMask, leftEye, radius, [0, 0, 0], -1)
cv2.circle(cv2.UMat(makeupRight), leftEye, radius, [0, 0, 0], -1)
# Base output is combination of face without lips and pupils + lips
baseCombine = makeupFace + makeupLips
# Areas around eyes are changes
output = cv2.seamlessClone(makeupLeft, baseCombine, leftMask, leftEye, cv2.MIXED_CLONE)
output = cv2.seamlessClone(makeupRight, output, rightMask, rightEye, cv2.MIXED_CLONE)
# Find center of face
faceRect = cv2.boundingRect(outerPoints)
x, y, w, h = faceRect
if x < 0:
x = 0
if y < 0:
y = 0
faceCenter = ( x + w // 2, y + h // 2)
croppedOutput = output[y:y+h, x:x+w]
faceMask = np.zeros(realFace.shape, realFace.dtype)
cv2.fillPoly(faceMask, [outerPoints], [255, 255, 255])
cv2.fillPoly(faceMask, [lipPoints], [0, 0, 0])
cv2.fillPoly(faceMask, [leftPP], [0, 0, 0])
cv2.fillPoly(faceMask, [rightPP], [0, 0, 0])
croppedMask = faceMask[y:y+h, x:x+w]
if len(croppedOutput) == 0:
print("OUTPUT 0")
print("FACE RECT: ", faceRect)
sourceW, sourceH, sCH = realFace.shape
width, height, ch = croppedOutput.shape
faceWidth = width/2
faceHeight = height/2
xdiff = 0
ydiff = 0
cx = faceCenter[0]
cy = faceCenter[1]
if cx - faceWidth < 0:
# Face overflows left
xdiff = abs(cx - faceWidth)
if cx + faceWidth > sourceW:
xdiff = (cx + faceWidth - sourceW) * -1
if cy + faceHeight > sourceH:
ydiff = (cy + faceHeight - sourceH) * -1
if cy - faceHeight < 0:
ydiff = abs(cy - faceHeight)
centerx = int(cx + xdiff)
centery = int(cy + ydiff)
center = (centerx, centery)
# We move center, also move mask?
combinedFace = cv2.seamlessClone(croppedOutput, realFace, croppedMask, center, cv2.MIXED_CLONE)
return combinedFace
When I use warpAffine to shear an image:
M2 = np.float32([[1, 0, 0], [0.2, 1, 0]])
aff2 = cv2.warpAffine(im, M2, (W, H))
I obtain an image that is not sheared around the image center. I can see black triangular areas on one side of the image, and the other side does not have black areas.
How could I let the image be sheared symmetricly ?
You have to adjust your translation parameters (3rd column) to center your image. i.e. you have to translate half the width and height multiplied by a factor.
For example
M2 = np.float32([[1, 0, 0], [0.2, 1, 0]])
M2[0,2] = -M2[0,1] * W/2
M2[1,2] = -M2[1,0] * H/2
aff2 = cv2.warpAffine(im, M2, (W, H))
Before
After
Full code
import cv2
import numpy as np
import matplotlib.pyplot as plt
im = np.ones((100,100))
H, W = im.shape
M2 = np.float32([[1, 0, 0], [0.2, 1, 0]])
M2[0,2] = -M2[0,1] * W/2
M2[1,2] = -M2[1,0] * H/2
aff2 = cv2.warpAffine(im, M2, (W, H))
plt.imshow(aff2, cmap="gray")
I have a 2d numpy array that has 0's where there is not an object, and 1's where there is an object.
matrix.shape = (500, 425)
I want to create a numpy array mask of shape (500, 425, 3) such that:
mask = np.zeros((500, 425, 3))
if matrix[x][y] == 0:
mask[x][y] = np.array([0, 0, 0]) # Black pixel if no object
else:
mask[x][y] = np.array([0, 255, 0]) # Green pixel if object
So that I have green pixels where the object are, black pixels everywhere else. How do I create this mask? Will this work correctly, such that if I use cv2.addWeighted to the image and the mask, the object will have a transparent green mask over it?
You're describing an "over" or "blend" image compositing operation. You can combine the images directly using your mask image.The general formula for combining two images in this way is:
A*alpha + B*(1-alpha)
Where A is the image being placed on top of image B. Alpha can be any value between black and white. Gray alpha values will make A appear transparent. It is usually easier to convert to a float image because the math is much easier when the values are between 0 and 1.
If you have image A (your source image) and image B (a green image) and your mask (matrix). You can overlay image B on top of image A using:
outimg[x][y] = (B[x][y] * matrix[x][y]) + (A[x][y] * (1-matrix[x][y]))
or if you want transparency:
#50% transparency
t = 0.5
outimg[x][y] = (B[x][y] * (matrix[x][y]*t)) + (A[x][y] * (1-(matrix[x][y]*t)))
If you want to put image A on top of image B you can just reverse the terms in the expression.
Here is an example of compositing image A (a flat green image) on top of image B (the source image) with a mask and transparency:
source image:
mask image:
import numpy as np
import cv2
i = cv2.imread('lena.bmp')
#convert to floating point
img = np.array(i, dtype=np.float)
img /= 255.0
cv2.imshow('img',img)
cv2.waitKey(0)
j = cv2.imread('lena_mask.bmp')
#convert to floating point
mask = np.array(j, dtype=np.float)
mask /= 255.0
#set transparency to 25%
transparency = .25
mask*=transparency
cv2.imshow('img',mask)
cv2.waitKey(0)
#make a green overlay
green = np.ones(img.shape, dtype=np.float)*(0,1,0)
#green over original image
out = green*mask + img*(1.0-mask)
cv2.imshow('img',out)
cv2.waitKey(0)
cv2.destroyAllWindows()
Output image:
One easy way would be with broadcasting after extending matrix to 3D and simply multiplying with the green colour triplet, like so -
matrix[...,None]*[0,255,0]
Sample run -
In [35]: matrix
Out[35]:
array([[1, 0, 0, 0],
[1, 0, 0, 1],
[0, 0, 1, 0]])
In [36]: matrix[...,None]*[0,255,0]
Out[36]:
array([[[ 0, 255, 0],
[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0]],
[[ 0, 255, 0],
[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 255, 0]],
[[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 255, 0],
[ 0, 0, 0]]])
The second column signifying the green colour.
Please note that this is not alpha masking, which generally involves the fourth channel, but is a simple RGB masking.
Another approach with zeros based initialization and might be better on performance -
m,n = matrix.shape
out = np.zeros((m,n,3),dtype=np.uint8)
out[matrix==1,1] = 255 # green channel accessed with the last index being 1