Warping an image using roll, pitch, and yaw - python

I am currently using OpenCV in Python to correct for image distortion in an aerial image. I have the data for the roll, pitch, and yaw. I understand that I need to create a warp matrix, and apply the matrix to my original coordinate points to create the output points for the image. I am able to affect the way the image moves, but I feel that there is an error because the only values that seem to work for very small values.
Here is my current code:
warp_mat = np.array([[math.cos(theta)*math.cos(psy), math.cos(phi)*math.sin(psy)+math.sin(phi)*math.sin(theta)*math.cos(psy), math.sin(phi)*math.sin(psy)-math.cos(phi)*math.sin(theta)*math.cos(psy)],\
[-1*math.cos(theta)*math.sin(psy), math.cos(phi)*math.cos(psy)-math.sin(phi)*math.sin(theta)*math.sin(psy), math.sin(phi)*math.cos(psy)+math.cos(phi)*math.sin(theta)*math.sin(psy)],\
[math.sin(theta), -1*math.sin(phi)*math.cos(theta), math.cos(phi)*math.cos(theta)]], dtype='float32')
srcPts = np.array([[-2064, 1161, 1],\
[2064, 1161, 1],\
[2064, -1161, 1],\
[-2064, -1161, 1]], dtype='float32')
dstPts = np.empty(shape = (4,3), dtype='float32')
dstPts[0][0] = srcPts[0][0] * warp_mat[0][0] + srcPts[0][1] * warp_mat[1][0] + srcPts[0][2] * warp_mat[2][0];
dstPts[0][1] = srcPts[0][0] * warp_mat[0][1] + srcPts[0][1] * warp_mat[1][1] + srcPts[0][2] * warp_mat[2][1];
dstPts[0][2] = srcPts[0][0] * warp_mat[0][2] + srcPts[0][1] * warp_mat[1][2] + srcPts[0][2] * warp_mat[2][2];
dstPts[1][0] = srcPts[1][0] * warp_mat[0][0] + srcPts[1][1] * warp_mat[1][0] + srcPts[1][2] * warp_mat[2][0];
dstPts[1][1] = srcPts[1][0] * warp_mat[0][1] + srcPts[1][1] * warp_mat[1][1] + srcPts[1][2] * warp_mat[2][1];
dstPts[1][2] = srcPts[1][0] * warp_mat[0][2] + srcPts[1][1] * warp_mat[1][2] + srcPts[1][2] * warp_mat[2][2];
dstPts[2][0] = srcPts[2][0] * warp_mat[0][0] + srcPts[2][1] * warp_mat[1][0] + srcPts[2][2] * warp_mat[2][0];
dstPts[2][1] = srcPts[2][0] * warp_mat[0][1] + srcPts[2][1] * warp_mat[1][1] + srcPts[2][2] * warp_mat[2][1];
dstPts[2][2] = srcPts[2][0] * warp_mat[0][2] + srcPts[2][1] * warp_mat[1][2] + srcPts[2][2] * warp_mat[2][2];
dstPts[3][0] = srcPts[3][0] * warp_mat[0][0] + srcPts[3][1] * warp_mat[1][0] + srcPts[3][2] * warp_mat[2][0];
dstPts[3][1] = srcPts[3][0] * warp_mat[0][1] + srcPts[3][1] * warp_mat[1][1] + srcPts[3][2] * warp_mat[2][1];
dstPts[3][2] = srcPts[3][0] * warp_mat[0][2] + srcPts[3][1] * warp_mat[1][2] + srcPts[3][2] * warp_mat[2][2];
dstPts[0][0] = dstPts[0][0] / dstPts[0][2];
dstPts[0][1] = dstPts[0][1] / dstPts[0][2];
dstPts[0][2] = dstPts[0][2] / dstPts[0][2];
dstPts[1][0] = dstPts[1][0] / dstPts[1][2];
dstPts[1][1] = dstPts[1][1] / dstPts[1][2];
dstPts[1][2] = dstPts[1][2] / dstPts[1][2];
dstPts[2][0] = dstPts[2][0] / dstPts[2][2];
dstPts[2][1] = dstPts[2][1] / dstPts[2][2];
dstPts[2][2] = dstPts[2][2] / dstPts[2][2];
dstPts[3][0] = dstPts[3][0] / dstPts[3][2];
dstPts[3][1] = dstPts[3][1] / dstPts[3][2];
dstPts[3][2] = dstPts[3][2] / dstPts[3][2];
srcPts2 = np.array([[srcPts[0][0],srcPts[0][1]],\
[srcPts[1][0],srcPts[1][1]],\
[srcPts[2][0],srcPts[2][1]],\
[srcPts[3][0],srcPts[3][1]]], dtype='float32')
dstPts2 = np.array([[dstPts[0][0],dstPts[0][1]],\
[dstPts[1][0],dstPts[1][1]],\
[dstPts[2][0],dstPts[2][1]],\
[dstPts[3][0],dstPts[3][1]]], dtype='float32')
transMatrix = cv.getPerspectiveTransform(srcPts2, dstPts2)
dst = cv.warpPerspective(imgFile,transMatrix,(4128,2322) ,borderMode = cv.BORDER_CONSTANT,borderValue = 0)

At the beginning of your code you are calculating the warp matrix by projecting four points and then use getPerspectiveTransform() to find out the transFormation matrix. This should work, but it is more complicated than necessary. If you know the roll, pitch and yaw angles, you can directly calculate the transformation matrix. Have a look at the function BirdsEyeView() in http://image2measure.net/files/calib3Dto2D.cpp . It does exactly that.
I had to change the line
Mat R = RX * RY * RZ;
to
Mat R = RZ * RX * RY;
in order to get it the transformation right.
f is the focal length in pixels.
If rh is the horizontal resolution of your image and oh the horizontal opening angle of the camera
f = (rh/2)/tan(oh/2)
Choose the same value for Distance if you don't want to scale the image, a bigger one than f to enlarge it or a smaller one to shrink it.

The code BirdsEyeView() works for me but I don't know why the Roll and Pitch angles are exchanged. When I change "alpha", the image is warped in pitch and when I change "beta" the image in warped in roll. So, I changed my rotation matrix, as can be seen below.
Also, the RY has a signal error. You can check Ry at: http://en.wikipedia.org/wiki/Rotation_matrix
I think this is the reason why Adrian changed the multiplication order from R = RX * RY * RZ to R = RZ * RX * RY.
The rotation metrix I use:
Mat RX = (Mat_<double>(4, 4) <<
1, 0, 0, 0,
0, cos(beta), -sin(beta), 0,
0, sin(beta), cos(beta), 0,
0, 0, 0, 1);
Mat RY = (Mat_<double>(4, 4) <<
cos(alpha), 0, sin(alpha), 0,
0, 1, 0, 0,
-sin(alpha), 0, cos(alpha), 0,
0, 0, 0, 1);
Mat RZ = (Mat_<double>(4, 4) <<
cos(gamma), -sin(gamma), 0, 0,
sin(gamma), cos(gamma), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
Regards

Related

Efficient boolean disk packing mask

I need to make a mask of hexagonal packed disks. The code below does the job, but I don't feel like its efficient. I'm learning python as well so I'd love to get some expert advice on how to do this more computationally efficient.
r = 0.01
X, Y = np.mgrid[0:1:1000j, 0:1:1000j]
mask = np.full(X.shape, False)
px, py = np.mgrid[r : 1 : 2 * r * np.sqrt(3), r : 1 + r + np.finfo(float).eps: 2 * r]
px = np.vstack((px, px + r * np.sqrt(3)))
py = np.vstack((py, py - r))
fig, ax = plt.subplots(figsize= (12, 12), dpi=50)
img = ax.imshow(mask * 1, cmap = 'gray', vmin = 0, vmax = 1, extent = [0, 1, 0, 1])
for i, _ in np.ndenumerate(px): #is this loop dumb and inefficient?
C = (X - px[i]) ** 2 + (Y - py[i]) ** 2
mask = mask | (C < r ** 2)
img.set_data(mask * 1)
ax.set_aspect(1)
In particular, is there a way to vectorize the for loop?
Thanks
It may be efficient to create a single tile of the pattern, and then repeat it horizontally and vertically as needed:
Create a tile:
import numpy as np
import matplotlib.pyplot as plt
r = 0.01
sq3 = np.sqrt(3)
samples = 1000
X, Y = np.mgrid[1:(1 + 2 * sq3):int(sq3 * samples) * 1j, 0:2:samples * 1j]
XY = np.c_[X.flatten(), Y.flatten()]
# coordinates of centers of disks; suffices to take disks of radius 1 here
p = np.array([[1, 1], [(1 + sq3), 0], [(1 + sq3), 2], [(1 + 2 * sq3), 1]])
# compute the distance from each point of XY to each disk center
dist = (XY**2).sum(axis=1).reshape(-1, 1) + (p**2).sum(axis=1) - 2 * (XY # p.T)
# mask points outside the disks
tile = (np.min(dist, axis=1) < 1).reshape(X.shape)
fig, ax = plt.subplots(figsize=(5, 5))
ax.set_aspect(1)
plt.imshow(tile, extent=[0, 2 * r, r, (1 + 2 * sq3) * r]);
It gives:
Repeat the tile:
# the number of times to repeat the tile in the horizontal and vertical directions
h, w = 20, 30
# donwsample the tile as needed and replicate
sample_rate = 10
mask = np.tile(tile[::sample_rate, ::sample_rate], (h, w))
fig, ax = plt.subplots(figsize=(8, 8))
ax.set_aspect(1)
ax.imshow(mask, extent=[0, 2 * r * w, 0, 2 * sq3 * r * h]);
This gives:

How to project pixels on eigenvectors in OpenCV?

given a contour, I can extract mean, and eigenvectors by performing PCA. Then I want to project all pixels inside a contour on an eigenvector. Below are my code and my images
my input images
Read image, extract contours, and draw the first component
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
src = cv.imread(cv.samples.findFile('/Users/bryan/Desktop/lung.png'))
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
def drawAxis(img, p_, q_, colour, scale):
p = list(p_)
q = list(q_)
## [visualization1]
angle = atan2(p[1] - q[1], p[0] - q[0]) # angle in radians
hypotenuse = sqrt((p[1] - q[1]) * (p[1] - q[1]) + (p[0] - q[0]) * (p[0] - q[0]))
# Here we lengthen the arrow by a factor of scale
q[0] = p[0] - scale * hypotenuse * cos(angle)
q[1] = p[1] - scale * hypotenuse * sin(angle)
cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv.LINE_AA)
# create the arrow hooks
p[0] = q[0] + 9 * cos(angle + pi / 4)
p[1] = q[1] + 9 * sin(angle + pi / 4)
cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv.LINE_AA)
p[0] = q[0] + 9 * cos(angle - pi / 4)
p[1] = q[1] + 9 * sin(angle - pi / 4)
cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv.LINE_AA)
def getOrientation(pts, img, scale_factor=25):
## [pca]
# Construct a buffer used by the pca analysis
sz = len(pts)
data_pts = np.empty((sz, 2), dtype=np.float64)
for i in range(data_pts.shape[0]):
data_pts[i, 0] = pts[i, 0, 0]
data_pts[i, 1] = pts[i, 0, 1]
# Perform PCA analysis
mean = np.empty(0)
mean, eigenvectors, eigenvalues = cv.PCACompute2(data_pts, mean)
# Store the center of the object
cntr = (int(mean[0, 0]), int(mean[0, 1]))
## [pca]
## [visualization]
# Draw the principal components
cv.circle(img, cntr, 3, (255, 0, 255), 2)
p1 = (
cntr[0] + scale_factor * eigenvectors[0, 0],
cntr[1] + scale_factor * eigenvectors[0, 1])
p2 = (
cntr[0] - scale_factor * eigenvectors[1, 0],
cntr[1] - scale_factor * eigenvectors[1, 1])
drawAxis(img, cntr, p1, (0, 255, 0), 4)
## [visualization]
# doing projections along eigenvectors
dim1_ = []
for _ in data_pts:
p = make_vector_projection(_, np.array(p1))
dim1_.append(p.astype(int))
dim1 = np.array(dim1_)
dim2_ = []
for _ in data_pts:
p = make_vector_projection(_, np.array(p2))
dim2_.append(p.astype(int))
dim2 = np.array(dim2_)
return mean, eigenvectors, eigenvalues, p1, p2, dim1, dim2
for i, c in enumerate(contours):
mean, evecs, evalues, p1, p2, dim1, dim2 = getOrientation(c, src)
plt.figure(figsize=(10, 10))
plt.axis('equal')
plt.gca().invert_yaxis()
plt.imshow(src)
Look as I expected but I want to double-check, I compute angles between a random vector in the first dimension and the first eigenvector. I defined
def unit_vector(vector):
""" Returns the unit vector of the vector. """
return vector / np.linalg.norm(vector)
def angle_between(v1, v2):
""" Returns the angle in radians between vectors 'v1' and 'v2'::
>>> angle_between((1, 0, 0), (0, 1, 0))
1.5707963267948966
>>> angle_between((1, 0, 0), (1, 0, 0))
0.0
>>> angle_between((1, 0, 0), (-1, 0, 0))
3.141592653589793
"""
v1_u = unit_vector(v1)
v2_u = unit_vector(v2)
return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))
for i, c in enumerate(contours):
mean, evecs, evalues, p1, p2, dim1, dim2 = getOrientation(c, src)
draw_point = dim1[45].astype(int) # extract the first dimension
print(draw_point, evecs[0], angle_between(draw_point, p1))
print('#'* 10)
I got output like below, angle_between is in radian, close to zero means they are paralleled
[ 97 148] [ 0.14189901 -0.98988114] 0.002321780300502494
##########
[332 163] [-0.22199134 -0.97504864] 0.0006249775357550807
##########
My problem occurs when I want to plot projected points on eigenvectors. Points I plotted don't lie on green line (my eigenvector). My code is
point_colors = [
(0, 0, 255), # blue
(0, 255, 0) # green
]
for i, c in enumerate(contours):
mean, evecs, evalues, p1, p2, dim1, dim2 = getOrientation(c, src)
draw_point = dim1[45].astype(int)
print(draw_point, evecs[0], angle_between(draw_point, p1))
cv.circle(src, (draw_point[1], draw_point[0]), 7, point_colors[i], 2) # plot point
print('#'* 10)
and output
My question is, I want to plot all projected pixels on the eigenvector lines, but my calculation doesn't seem correct as points don't lie on the eigenvector lines. Could you help?

Solar System Simulation - Where are my calculations going wrong?

I am making a 2D solar system simulation for a project using physical equations, x and y coordinates and a dictionary using 2D lists. I am using a tkinter canvas to construct the animation.
The planet 'earth' seems to disappear of screen with a very slow acceleration at first, then with a huge acceleration not long after. I can't see the problem.
Beforehand, I had only the earth revolving around the Sun, which was successful using the equations in a similar way and moving the earth on the canvas by the x and y components of the the displacement calculated. The dictionary is order by {(-body):[[x, y], [x velocity, y velocity], mass, [change in x displacement, change in y displacement]}. The values calculated are stored or added to some values in this dictionary if necessary. Here's the code I'm using:
G = 6.67384 * 10 ** -11
scale = 10 ** 13
speed = 1
global user_status
screen = Tk()
screen.title('Solar System' + ' - ' + user_status)
screen.geometry('1300x700')
ani = Canvas(screen, width=1300, height=700)
ani.pack()
ani.create_rectangle(0, 0, 1300, 700, fill='Black')
sunx = 636
suny = 343
sun = ani.create_oval(sunx-10, suny-10, sunx+10, suny+10, fill='yellow')
earthx = 746
earthy = 343
moonx = 747
moony = 343
bodies = {'sun': [[sunx, suny], [0, 0], 1.989 * 10 ** 30 * speed / scale, [0, 0]],
'earth':[[earthx, earthy], [0, 347.3833062 * 1.033447099], 5.972 * 10 ** 24 * speed / scale, [0, 0]],
'moon': [[moonx, moony], [0, 360], 7.34767309 * 10 ** 22 * speed / scale, [0, 0]]
}
body_names = []
for Ω in bodies.keys():
body_names.append(Ω)
moon = ani.create_oval(moonx - 4, moony - 4, moonx + 4, moony + 4, fill='grey70')
earth = ani.create_oval(earthx-6, earthy-6, earthx+6, earthy+6, fill='sky blue')
timestep = 0.0001
while True:
for i in range(len(body_names)):
body1 = body_names[i]
x1 = bodies[body1][0][0]
y1 = bodies[body1][0][1]
total_Fx = 0
total_Fy = 0
body1_mass = bodies[body1][2]
for j in range(len(body_names)):
body2 = body_names[j]
if body1 != body2:
x2 = bodies[body2][0][0]
y2 = bodies[body2][0][1]
body2_mass = bodies[body2][2]
r = sqrt(((x1 - x2) ** 2) + ((y1 - y2) ** 2))
rx = (x1 - x2)
angle = (acos(rx/r))
F = (G * body1_mass * body2_mass) / (r ** 2)
Fx = F * cos(angle)
Fy = F * sin(angle)
total_Fx += Fx
total_Fy += Fy
ax = (total_Fx / body1_mass)
ay = (total_Fy / body1_mass)
ux = bodies[body1][1][0]
uy = bodies[body1][1][1]
vx = ux - (ax * timestep)
if x1 <= sunx:
vy = uy + (ay * timestep)
else:
vy = uy - (ay * timestep)
sx = vx * timestep * speed
sy = vy * timestep * speed
bodies[body1][3][0] = sx
bodies[body1][3][1] = sy
bodies[body1][1][0] += vx
bodies[body1][1][1] += vy
bodies[body1][0][0] = x1 + sx
bodies[body1][0][1] = y1 + sy
print(bodies[body1][1], body1)
move_e_x = bodies['earth'][3][0]
move_e_y = bodies['earth'][3][1]
ani.move(earth, move_e_x, move_e_y)
move_m_x = bodies['moon'][3][0]
move_m_y = bodies['moon'][3][1]
ani.move(moon, move_m_x, move_m_y)
ani.update()
I want the objects to simultaneously orbit the sun and each other, but I get this error message after the bodies have gone far off the screen:
Traceback (most recent call last):
File "/Users/apple/Documents/School/Computer Science/NEA/NEA Programming/Solar System Simulator.py", line 380, in <module>
simulate() # Calls the 'simulate' function
File "/Users/apple/Documents/School/Computer Science/NEA/NEA Programming/Solar System Simulator.py", line 290, in simulate
r = sqrt(((x1 - x2) ** 2) + ((y1 - y2) ** 2))
OverflowError: (34, 'Result too large')
I know this may not be a very efficient piece of code, but all I need is some help on how to do it this way. The problem seems to occur in the displacement or velocity calculations. Any ideas?
The initial velocity is getting received twice: in each loop, the initial velocity is called and used, so itself and the new velocity are added together, causing it to more than double each time.
The new added velocity is now calculated and then only added once to the initial velocity. The planets now orbit. There are currently 9 bodies orbiting the Sun.

What Is the Algorithm Behind Photoshop's “Black and White” Adjustment Layer?

I did lot's of research but I didn't find anything (but I also don't know what kind of keywords to search for exactly). I want to be able to convert an input RGB image to grayscale but I want to be able to add more or less Reds/Yellows/Greens/Cyans/Blues/Magentas like in Photoshop. Do you know what are the equation or where I can found these equations so that I can implemented my own optimized RGB to Grayscale conversion?
Edit:
In Photoshop it is called Black/White adjustment layer. I have found something but actually it doesn't seem to work. Here is my implementation (in comments are the resources needed to understand the algorithm):
import numpy as np
import scipy.misc
import matplotlib.pyplot as plt
%matplotlib inline
# Adapted from the answers of Ivan Kuckir and Royi here:
# https://dsp.stackexchange.com/questions/688/what-is-the-algorithm-behind-photoshops-black-and-white-adjustment-layer?newreg=77420cc185fd44099d8be961e736eb0c
def rgb2hls(img):
"""Adapted to use numpy from
https://github.com/python/cpython/blob/2.7/Lib/colorsys.py"""
r, g, b = img[:, :, 0], img[:, :, 1], img[:, :, 2]
maxc = np.max(img, axis=-1)
minc = np.min(img, axis=-1)
l = (minc + maxc) / 2
mask = np.ones_like(r)
mask[np.where(minc == maxc)] = 0
mask = mask.astype(np.bool)
smask = np.greater(l, 0.5).astype(np.float32)
s = (1.0 - smask) * ((maxc - minc) / (maxc + minc)) + smask * ((maxc - minc) / (2.0 - maxc - minc))
s[~mask] = 0
rc = np.where(mask, (maxc - r) / (maxc - minc), 0)
gc = np.where(mask, (maxc - g) / (maxc - minc), 0)
bc = np.where(mask, (maxc - b) / (maxc - minc), 0)
rmask = np.equal(r, maxc).astype(np.float32)
gmask = np.equal(g, maxc).astype(np.float32)
rgmask = np.logical_or(rmask, gmask).astype(np.float32)
h = rmask * (bc - gc) + gmask * (2.0 + rc - bc) + (1.0 - rgmask) * (4.0 + gc - rc)
h = np.remainder(h / 6.0, 1.0)
h[~mask] = 0
return np.stack([h, l, s], axis=-1)
def black_and_white_adjustment(image, weights):
# normalize input image to (0, 1) if uint8
if 'uint8' in (image).dtype.name:
image = image / 255
# linearly remap input coeff [-200, 300] to [-2.5, 2.5]
weights = (weights - 50) / 100
n_weights = len(weights)
h, w = image.shape[:2]
# convert rgb to hls
hls_img = rgb2hls(image)
output = np.zeros((h, w), dtype=np.float32)
# see figure 9 of https://en.wikipedia.org/wiki/HSL_and_HSV
# to understand the algorithm
for y in range(h):
for x in range(w):
hue_val = 6 * hls_img[y, x, 0]
# Use distance on a hexagone (maybe circular distance is better?)
diff_val = min(abs(0 - hue_val), abs(1 - (0 - hue_val)))
luminance_coeff = weights[0] * max(0, 1 - diff_val)
for k in range(1, n_weights):
luminance_coeff += weights[k] * max(0, 1 - abs(k - hue_val))
# output[y, x] = min(max(hls_img[y, x, 1] * (1 + luminance_coeff), 0), 1)
output[y, x] = hls_img[y, x, 1] * (1 + luminance_coeff)
return output
image = scipy.misc.imread("your_image_here.png")
w = np.array([40, 85, 204, 60, 20, 80])
out = black_and_white_adjustment(image, w)
plt.figure(figsize=(15, 20))
plt.imshow(out, cmap='gray')
Thank you
Here's an attempt using PIL rather than numpy. It should be easy to convert. Without a copy of Photoshop to compare with, I can't guarantee it matches the output exactly but it does produce the exact values for the sample shown in your link. The values r_w, y_w, g_w, c_w, b_w, m_w are the weights to be applied to each color, with 1.0 equating to 100% in the corresponding Photoshop slider. Naturally they can also be negative.
from PIL import Image
im = Image.open(r'c:\temp\temp.png')
def ps_black_and_white(im, weights):
r_w, y_w, g_w, c_w, b_w, m_w = [w/100 for w in weights]
im = im.convert('RGB')
pix = im.load()
for y in range(im.size[1]):
for x in range(im.size[0]):
r, g, b = pix[x, y]
gray = min([r, g, b])
r -= gray
g -= gray
b -= gray
if r == 0:
cyan = min(g, b)
g -= cyan
b -= cyan
gray += cyan * c_w + g * g_w + b * b_w
elif g == 0:
magenta = min(r, b)
r -= magenta
b -= magenta
gray += magenta * m_w + r * r_w + b * b_w
else:
yellow = min(r, g)
r -= yellow
g -= yellow
gray += yellow * y_w + r * r_w + g * g_w
gray = max(0, min(255, int(round(gray))))
pix[x, y] = (gray, gray, gray)
return im
Using this provided test image, here are some example results.
ps_black_and_white(im, [-17, 300, -100, 300, -200, 300])
ps_black_and_white(im, [40, 60, 40, 60, 20, 80])
ps_black_and_white(im, [106, 65, 17, 17, 104, 19])
I answer my own question by adding the numpy/scipy version of the code, if it can be of any interest for anybody in the future. If you want to upvote an answer, you should upvote the answer of Mark Ransom !
import numpy as np
import scipy.misc
import matplotlib.pyplot as plt
%matplotlib inline
def black_and_white_adjustment(img, weights):
rw, yw, gw, cw, bw, mw = weights / 100
h, w = img.shape[:2]
min_c = np.min(img, axis=-1).astype(np.float)
# max_c = np.max(img, axis=-1).astype(np.float)
# Can try different definitions as explained in the Ligtness section from
# https://en.wikipedia.org/wiki/HSL_and_HSV
# like: luminance = (min_c + max_c) / 2 ...
luminance = min_c
diff = img - min_c[:, :, None]
red_mask = (diff[:, :, 0] == 0)
green_mask = np.logical_and((diff[:, :, 1] == 0), ~red_mask)
blue_mask = ~np.logical_or(red_mask, green_mask)
c = np.min(diff[:, :, 1:], axis=-1)
m = np.min(diff[:, :, [0, 2]], axis=-1)
yel = np.min(diff[:, :, :2], axis=-1)
luminance = luminance + red_mask * (c * cw + (diff[:, :, 1] - c) * gw + (diff[:, :, 2] - c) * bw) \
+ green_mask * (m * mw + (diff[:, :, 0] - m) * rw + (diff[:, :, 2] - m) * bw) \
+ blue_mask * (yel * yw + (diff[:, :, 0] - yel) * rw + (diff[:, :, 1] - yel) * gw)
return np.clip(luminance, 0, 255).astype(np.uint8)
input_img = scipy.misc.imread("palette.jpg")
weights = np.array([106, 65, 17, 17, 104, 19])
bw_image = black_and_white_adjustment(input_img, weights)
plt.figure(figsize=(15, 20))
plt.imshow(bw_image, cmap="gray")
This code is faster as it uses vect operations.

Rotation of arbitrary plane around a given axis results in inconsistent results

I am trying to rotate and translate arbitrary planes around some arbitrary axis.
For testing purposes I have written a simple python program that rotates a random plane around the X axis in degrees.
Unfortunately when checking the angle between the planes I get inconsistent results. This is the code:
def angle_between_planes(plane1, plane2):
plane1 = (plane1 / np.linalg.norm(plane1))[:3]
plane2 = (plane2/ np.linalg.norm(plane2))[:3]
cos_a = np.dot(plane1.T, plane2) / (np.linalg.norm(plane1) * np.linalg.norm(plane2))
print(np.arccos(cos_a)[0, 0])
def test():
axis = np.array([1, 0, 0])
theta = np.pi / 2
translation = np.array([0, 0, 0])
T = get_transformation(translation, axis * theta)
for i in range(1, 10):
source = np.append(np.random.randint(1, 20, size=3), 0).reshape(4, 1)
target = np.dot(T, source)
angle_between_planes(source, target)
It prints:
1.21297144225
1.1614420953
1.48042948278
1.10098697889
0.992418096794
1.16954303911
1.04180591409
1.08015300394
1.51949177153
When debugging this code I see that the transformation matrix is correct, as it shows that it is
I'm not sure what's wrong and would love any assistance here.
*
The code that generates the transformation matrix is:
def get_transformation(translation_vec, rotation_vec):
r_4 = np.array([0, 0, 0, 1]).reshape(1, 4)
rotation_vec= rotation_vec.reshape(3, 1)
theta = np.linalg.norm(rotation_vec)
axis = rotation_vec/ theta
R = get_rotation_mat_from_axis_and_angle(axis, theta)
T = translation_vec.reshape(3, 1)
R_T = np.append(R, T, axis = 1)
return np.append(R_T, r_4, axis=0)
def get_rotation_mat_from_axis_and_angle(axis, theta):
axis = axis / np.linalg.norm(axis)
a, b, c = axis
omct = 1 - np.cos(theta)
ct = np.cos(theta)
st = np.sin(theta)
rotation_matrix = np.array([a * a * omct + ct, a * b * omct - c * st, a * c * omct + b * st,
a * b * omct + c * st, b * b * omct + ct, b * c * omct - a * st,
a * c * omct - b * st, b * c * omct + a * st, c * c * omct + ct]).reshape(3, 3)
rotation_matrix[abs(rotation_matrix) < 1e-8] = 0
return rotation_matrix
The source you generate is not a vector. In order to be one, it should have its fourth coordinate equal to zero.
You could generate valid ones with:
source = np.append(np.random.randint(1, 20, size=3), 0).reshape(4, 1)
Note that your code can't be tested as you pasted it in your question: for example, vec = vec.reshape(3, 1) in get_transformation uses vec that hasn't been defined anywhere before...

Categories

Resources