I'm detecting a rectangle with known dimensions in OpenCV. I've written code - which works - for detecting all four lines, but the furthest line is difficult to detect and usually causes problems when it comes time for warp the perspective.
My question (which I've looked into, but haven't found an answer for), is whether, theoretically, having 3 lines (angles and intercept) is enough to approximate the final line.
EDIT: I was asked to include my current code. Here it is:
import numpy as np
import cv2
from sklearn.cluster import AgglomerativeClustering
# find intersection of lines
def line_intersect(m1, b1, m2, b2):
if m1 == m2:
print ("These lines are parallel!!!")
return None
x = int((b2 - b1) / (m1 - m2))
y = int(m1 * x + b1)
return [x,y]
# method specs
width_error = 10
height_error = 10
kernel_size = 7
kernel_dilate = np.ones((1, 1), 'uint8')
kernel = np.ones((5, 5), 'uint8')
# read and process image
img = cv2.imread('assets/game-frames/hard-m-2019-124-1200.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)
edges = cv2.Canny(blur_gray, 0, 100, apertureSize = 3)
dilate_img = cv2.dilate(edges, kernel_dilate, iterations=3)
closing = cv2.morphologyEx(dilate_img, cv2.MORPH_CLOSE, kernel)
# find hough lines
lines = cv2.HoughLinesP(closing, 2, np.pi/180, 100, minLineLength=20, maxLineGap=10)
lines_np = np.reshape(lines, (np.int32(lines.size/4), 4))
lines_np = lines_np[(lines_np[:, 2] - lines_np[:, 0]) != 0]
lines_np = np.c_[lines_np, (lines_np[:, 3] - lines_np[:, 1])/(lines_np[:, 2] - lines_np[:, 0])]
lines_np = np.c_[lines_np, np.sqrt((lines_np[:, 3] - lines_np[:, 1])**2 + (lines_np[:, 2] - lines_np[:, 0])**2)]
# find lengthwise lines
lengthwise_lines = lines_np[(abs(lines_np[:, 4]) < 3.5) & (abs(lines_np[:, 4]) > 1.20) & (lines_np[:, 5] > 100)]
if lengthwise_lines[:, 4].size < 2: print('No lengthwise lines!')
ward_length = AgglomerativeClustering(n_clusters = None, distance_threshold = 0.50, linkage = "ward").fit(abs(lengthwise_lines[:, 4].reshape(-1,1)))
lengthwise_lines = np.c_[lengthwise_lines, ward_length.labels_]
length_clusts = np.bincount(ward_length.labels_).argsort()[-2:]
lengthwise_lines = lengthwise_lines[np.isin(ward_length.labels_, length_clusts)]
# find extremities
x_max = np.max(lengthwise_lines[:,[0,2]].reshape(-1,1))
x_min = np.amin(lengthwise_lines[:,[0,2]])
y_coords = lengthwise_lines[:, [1, 3]].reshape(-1,1)
ward = AgglomerativeClustering(n_clusters = None, distance_threshold = 10, linkage = "ward").fit(y_coords)
want_clusts = np.bincount(ward.labels_).argsort()[-2:]
y_ext1 = np.median(y_coords[np.isin(ward.labels_, want_clusts[0])])
y_ext2 = np.median(y_coords[np.isin(ward.labels_, want_clusts[1])])
y_max = max(y_ext1, y_ext2)
y_min = min(y_ext1, y_ext2)
# find outer lengthwise lines
length_want = ward_length.labels_[np.isin(ward_length.labels_, length_clusts)][np.argmin(abs(lengthwise_lines[:,4]))]
lengthwise_lines = lengthwise_lines[lengthwise_lines[:, 6] == length_want]
lengthwise_lines = np.c_[lengthwise_lines, (lengthwise_lines[:,1] - (lengthwise_lines[:,4]*lengthwise_lines[:,0]))]
lengthwise_lines = np.c_[lengthwise_lines, ((np.max(lengthwise_lines[:,1]) - lengthwise_lines[:,5])/lengthwise_lines[:,4])]
# subset left and right outer lengthwise lines
l_left = lengthwise_lines[lengthwise_lines[:,4] < 0]
l_right = lengthwise_lines[lengthwise_lines[:,4] > 0]
# find top and bottom lines
widthwise_lines = lines_np[(abs(lines_np[:, 4]) < 0.10)]
widthwise_lines = np.c_[widthwise_lines, (widthwise_lines[:,1] - (widthwise_lines[:,4]*widthwise_lines[:,0]))]
bottom_lines = widthwise_lines[np.where((np.amin(widthwise_lines[:,[1,3]], axis = 1) >= y_min - height_error) & (np.amin(widthwise_lines[:,[1,3]], axis = 1) <= y_min + height_error) & (np.amin(widthwise_lines[:,[0,2]], axis = 1) >= x_min - width_error) & (np.amax(widthwise_lines[:,[0,2]], axis = 1) <= x_max + width_error))[0]]
top_lines = widthwise_lines[np.where((np.amax(widthwise_lines[:,[1,3]], axis = 1) >= y_max - height_error) & (np.amax(widthwise_lines[:,[1,3]], axis = 1) <= y_max + height_error) & (np.amin(widthwise_lines[:,[0,2]], axis = 1) >= x_min - width_error) & (np.amax(widthwise_lines[:,[0,2]], axis = 1) <= x_max + width_error))[0]]
# if no lines found for any border, stop
if top_lines.size == 0 or bottom_lines.size == 0 or l_left.size == 0 or l_right.size == 0: print('No Lengthwise lines!')
# take median of left outer lengthwise lines
l_left_b = np.median(l_left[:,7])
l_left_m = np.median(l_left[:,4])
# take median of right outer lengthwise lines
l_right_b = np.median(l_right[:,7])
l_right_m = np.median(l_right[:,4])
# take median of top widthwise lines
top_lines_b = np.median(top_lines[:,6])
top_lines_m = np.median(top_lines[:,4])
# take median of bottom widthwise lines
bottom_lines_b = np.median(bottom_lines[:,6])
bottom_lines_m = np.median(bottom_lines[:,4])
# find interesction of lines
int_pt1 = line_intersect(l_left_m, l_left_b, bottom_lines_m, bottom_lines_b)
int_pt2 = line_intersect(l_right_m, l_right_b, bottom_lines_m, bottom_lines_b)
int_pt3 = line_intersect(l_left_m, l_left_b, top_lines_m, top_lines_b)
int_pt4 = line_intersect(l_right_m, l_right_b, top_lines_m, top_lines_b)
# draw intersections
cv2.circle(img, int_pt1, 3, (0, 255, 0), -1)
cv2.circle(img, int_pt3, 3, (255, 255, 0), -1)
cv2.circle(img, int_pt2, 3, (0, 255, 0), -1)
cv2.circle(img, int_pt4, 3, (255, 255, 0), -1)
# show image
cv2.imshow('frame diff ', img)
cv2.waitKey(0)
Original image:
Related
I want to draw a line on an image. I have only to give the angle and the end point of the line. How can I do this with Python?
I think it is easy by identifying the vertical line passing through that given point and ploting the line according to the angle. The line should ends with the given point.
I tried it with this code. But didn't work.
import math
def get_coords(x, y, angle, imwidth, imheight):
#img = cv2.imread('contours_none_image2.jpg', 1)
x1_length = (x-imwidth) / math.cos(angle)
y1_length = (y-imheight) / math.sin(angle)
length = max(abs(x1_length), abs(y1_length))
endx1 = x + length * math.cos(math.radians(angle))
endy1 = y + length * math.sin(math.radians(angle))
x2_length = (x-imwidth) / math.cos(angle+45)
y2_length = (y-imheight) / math.sin(angle+45)
length = max(abs(x2_length), abs(y2_length))
endx2 = x + length * math.cos(math.radians(angle+45))
endy2 = y + length * math.sin(math.radians(angle+45))
cv2.line(img, (int(endx1),int(endy1)), (int(endx2),int(endy2)), (0, 255, 255), 3)
cv2.imshow("contours_none_image2.jpg", img)
#cv2.imshow("contours_none_image2.jpg", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
return endx1, endy1, endx2, endy2
An interesting way for finding the intersection point between the Y axis and the line is by using three cross products with homogeneous coordinates.
Ways for finding lines intersections are described in Wikipedia.
The cross products solution using homogeneous coordinates is described here.
Start by finding a very "far" origin point (x, y) - outside the image:
length = cv2.norm(np.array([imwidth, imheight])) # Apply maximum possible length: length = sqrt(imwidth**2 + imheight**2)
x0 = x - length * math.cos(math.radians(angle))
y0 = y + length * math.sin(math.radians(angle)) # Reverse sings because y axis in image goes down
Finding intersection with the Y axis:
The Y axis may be described as a line from (0,0) to (0, imheight-1).
We may find the line representation in homogeneous coordinates using cross product:
p0 = np.array([0, 0, 1])
p1 = np.array([0, imheight-1, 1])
l0 = np.cross(p0, p1) # [-107, 0, 0]
In the same way we may find the representation of the line from (x0, y0) to (x, y):
p0 = np.array([x0, y0, 1])
p1 = np.array([x, y, 1])
l1 = np.cross(p0, p1)
Finding the intersection point using cross product between the lines, and "normalizing" the homogeneous coordinate:
p = np.cross(l0, l1)
p = p / p[2]
Code sample:
import math
import cv2
import numpy as np
img = np.zeros((108, 192, 3), np.uint8)
x, y, angle = 150, 20, 80
imheight, imwidth = img.shape[0], img.shape[1]
angle = 90 - angle # Usualy the angle is relative to the horizontal axis - use 90 - angle for swaping axes
length = cv2.norm(np.array([imwidth, imheight])) # Apply maximum possible length: length = sqrt(imwidth**2 + imheight**2)
x0 = x - length * math.cos(math.radians(angle))
y0 = y + length * math.sin(math.radians(angle)) # Reverse sings because y axis in image goes down
# http://robotics.stanford.edu/~birch/projective/node4.html
# Find lines in homogeneous coordinates (using cross product):
# l0 represents a line of Y axis.
p0 = np.array([0, 0, 1])
p1 = np.array([0, imheight-1, 1])
l0 = np.cross(p0, p1) # [-107, 0, 0]
# l1 represents
p0 = np.array([x0, y0, 1])
p1 = np.array([x, y, 1])
l1 = np.cross(p0, p1)
# https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
# Lines intersection in homogeneous coordinates (using cross product):
p = np.cross(l0, l1)
p = p / p[2]
x0, y0 = p[0], p[1]
# Convert from homogeneous coordinate to euclidean coordinate (divide by last element).
cv2.line(img, (int(x0),int(y0)), (int(x),int(y)), (0, 255, 255), 3)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Sample output:
More conventional solution:
We may simply assign x0 = 0, and find length:
x0 = x - length * cos(alpha)
y0 = y + length * sin(alpha)
Assign x0 = 0:
x - length * cos(alpha) = 0
=> x = length * cos(alpha)
=> length = x/cos(alpha)
Code:
length = x / math.cos(math.radians(angle)) # We better verify that math.cos(math.radians(angle)) != 0
x0 = 0
y0 = y + length * math.sin(math.radians(angle))
cv2.line(img, (int(x0),int(y0)), (int(x),int(y)), (255, 0, 0), 3)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Output:
I am working on a project in OpenCV where I have a contour and I have to fit some arcs with defined arc length, radius, chord length, and angle. With fit, I mean I have to choose the best arc, that would cover a piece of the contour.
What I have tried till now is in all the contours, I have found corners which are acting as my segments, then using some linear algebra I am calculating the radius and the center of the circle that would best fit that segment and then comparing this with all the other arcs, radius and central angle and fitting the with the least difference in values. This approach is not helping me much as the arcs that I am getting don't lie on the contour. Code is attached
for contours in final_contours:
iter = 0
dict_sorted, sorted_corners = sortCorners(contours, corners)
keys_dict_sorted = sorted(dict_sorted.keys())
for key in keys_dict_sorted:
[x, y] = dict_sorted[key]
bl_img = cv2.circle(bl_img, (x, y), 3, (255, 0, 0), -1)
# cv2_imshow(bl_img)
for i in range(len(keys_dict_sorted) ):
if (i != len(keys_dict_sorted)-1):
idx1 = keys_dict_sorted[i]
idx3 = keys_dict_sorted[i+1]
idx2 = math.floor((keys_dict_sorted[i] + keys_dict_sorted[i+1]) / 2)
else:
idx1 = keys_dict_sorted[i]
idx3 = keys_dict_sorted[0]
idx2 = math.floor((keys_dict_sorted[i] + keys_dict_sorted[0]) / 2)
radius, [c_x, c_y] = betterCircleFinder(contours[idx1][0], contours[idx2][0], contours[idx3][0])
if radius != -1:
radius = round(truncate(radius, -1))
c_x = round(truncate(c_x, -1))
c_y = round(truncate(c_y, -1))
pt1_angle = 180*(np.arctan2(np.array([contours[idx1][0][1]- c_y], dtype=float), np.array([contours[idx1][0][0] - c_x], dtype=float))/np.pi)
pt2_angle = 180*(np.arctan2(np.array([contours[idx3][0][1] - c_y], dtype=float),np.array([contours[idx3][0][0] - c_x], dtype=float))/np.pi)
# print(pt1_angle, pt2_angle)
angle = abs(pt1_angle - pt2_angle)
# print("Angle : ", angle)
actualRadius = radius * inchPerPixel
# print("Actual Radius : ", actualRadius)
b = random.randint(0,255)
g = random.randint(0, 255)
r = random.randint(0,255)
error = [0 for i in range(len(copings))]
idx = 0
for coping in copings:
err = abs(actualRadius - (coping.radius))
err1 = abs(angle - coping.centralAngle)
error[idx] = (0.5 * err/actualRadius) + (0.5 * err1[0]/coping.centralAngle)
idx+=1
# bl_img = draw_coris(bl_img, int(coping.radius * pixelPerInch), 0, pt1_angle, pt1_angle + coping.centralAngle , (c_x, c_y), (b, g,r), 5)
# cv2_imshow(bl_img)
index_min = min(range(len(error)), key=error.__getitem__)
# pprint.pprint(error)
# print(index_min, error[index_min])
# print(copings[index_min].radius * pixelPerInch)
bl_img = draw_coris(bl_img, int(copings[index_min].radius * pixelPerInch), 0, pt1_angle, pt1_angle + copings[index_min].centralAngle , (int(c_x), int(c_y)), (b, g,r), 5)
if (abs(c_x) > (2 ** 31 -1) or abs(c_y) > (2**31 -1)):
print("OVERFLOW VALUES")
# cv2_imshow(bl_img)
continue
def betterCircleFinder(pt1, pt2, pt3):
A = np.array([[2*pt1[0], 2*pt1[1], 1],
[2*pt2[0], 2*pt2[1], 1],
[2*pt3[0], 2*pt3[1], 1] ])
B = np.array([[-1 * (pt1[0] ** 2 + pt1[1]**2)],
[-1 * (pt2[0] ** 2 + pt2[1]**2)],
[-1 * (pt3[0] ** 2 + pt3[1]**2)]])
det = np.linalg.det(A)
if (det == 0):
return -1, [-1, -1]
C = np.linalg.inv(A)#B
C = np.squeeze(C)
c_x = -1 * C[0]
c_y = -1 * C[1]
c = C[2]
radius = math.sqrt(c_x ** 2 + c_y** 2 - c)
return radius, [c_x, c_y]
This is the code that find the details of the circle that best fits the segment.
I have large lists containing RGB values for pictures.
I use numpy to create patterns and/or pillow to load pictures and convert them to 3D-numpy arrays (int32). Now i want to restructure my array into hex-strings in a weird way:
Three hex-strings for R, G, B in this structure: '0000FFFF', where the first 4 characters always have to be zero, then 2 characters represent pixel n+1 and the last 2 cahracters represent pixel n.
I already have done this with a code which takes too long for larger images and i require some improvement. What i got so far:
import numpy
import numpy.matlib
#from matplotlib.colors import rgb2hex
import time
def pairwise(iterable):
"""Create a paired-list from a list."""
a = iter(iterable)
return zip(a, a)
def test(imgSize=[480,640], brightness=[255,255,255]):
#generate pattern
startPattern = time.time()
patternDescription = 'Stripe Test'
pattern = numpy.zeros((imgSize[0], imgSize[1], 3))
line = (numpy.r_[:imgSize[1]]%255)/255
colorChR = numpy.matlib.repmat(line, imgSize[0], 1)
colorChG = numpy.matlib.repmat(line, imgSize[0], 1)
colorChB = numpy.matlib.repmat(line, imgSize[0], 1)
colorChR[:, :] = 0
colorChR[:, 0:60] = 1
colorChG[:, :] = 0
colorChG[:, 0:60] = 1
colorChB[:, :] = 0
colorChB[:, 0:60] = 1
pattern[:, :, 0] = colorChR
pattern[:, :, 1] = colorChG
pattern[:, :, 2] = colorChB
stopPattern = time.time()
print('TIME: Pattern generation: ' + str(round(stopPattern-startPattern,3)) + ' s. ')
# first reshape
startReshape = time.time()
pattern[:, :, 0] = pattern[:, :, 0]*brightness[0] # red brightness multiplicator
pattern[:, :, 1] = pattern[:, :, 1]*brightness[1] # green brightness multiplicator
pattern[:, :, 2] = pattern[:, :, 2]*brightness[2] # blue brightness multiplicator
img = pattern.astype(int)
# IDEALLY I WANT TO CHANGE THE CODE ONLY FROM HERE ON
# redValues = pattern[:,:,0].astype(int)
# greenValues = pattern[:,:,1].astype(int)
# blueValues = pattern[:,:,2].astype(int)
# test = ("0000" + ("{:0>2X}" * len(redValues))).format(*tuple(redValues[::-1]))
# numpy.set_printoptions(formatter={'int':hex})
# #test = [ rgb2hex(img[i,:]) for i in range(img.shape[0]) ]
# rgb2hex = lambda r,g,b: '%02X%02X%02X' %(r,g,b)
# test = [ rgb2hex(*img[i,:]) for i in range(img.shape[0]) ]
# # img = numpy.array2string(img, formatter = {'int':lambda img: hex(img)})
imgReshape = numpy.reshape(img, (1, imgSize[0]*imgSize[1]*3)) #necessary?
redValues = imgReshape[0][0::3] #red values (0, 3, 6, ..)
greenValues = imgReshape[0][1::3] #green values (1, 4, 7, ..)
blueValues = imgReshape[0][2::3] #blue values (2, 5, 8, ..)
stopReshape = time.time()
print('TIME: Reshape into colors: ' + str(round(stopReshape-startReshape,3)) + ' s. ')
redString = ''
greenString = ''
blueString = ''
outData = dict()
startString = time.time()
for i, j in pairwise(redValues):
tempRed = "0000%02X%02X" % (int(j), int(i))
redString += tempRed
for i, j in pairwise(greenValues):
tempGreen = "0000%02X%02X" % (int(j), int(i))
greenString += tempGreen
for i, j in pairwise(blueValues):
tempBlue = "0000%02X%02X" % (int(j), int(i))
blueString += tempBlue
outData['red'] = redString
outData['green'] = greenString
outData['blue'] = blueString
stopString = time.time()
print('TIME: String formatting: ' + str(round(stopString-startString, 3)) + ' s')
print('DATATEST: First 200 red chars: ' + str(outData['red'][0:200]))
print('DATATEST: First 200 green chars: ' + str(outData['green'][0:200]))
print('DATATEST: First 200 blue chars: ' + str(outData['blue'][0:200]))
#return outData
Try to use numpy array instead:
redValues = np.random.randint(0, 255, (10, 2))
red = np.array(redValues).reshape(-1, 2)
red_channel = (red[:, 1] << 8) + red[:, 0]
redString = ''.join(map(lambda val: f'0000{val:04x}', red_channel))
I want to reproduce this image using matplotlib. The example docs have a numpy logo, but all the voxel cubes are homogenous in color.
I could imagine perhaps making a separate surface plot for each face I want to change but that seems impractical. Here's the code for the example docs numpy logo:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
def explode(data):
size = np.array(data.shape)*2
data_e = np.zeros(size - 1, dtype=data.dtype)
data_e[::2, ::2, ::2] = data
return data_e
# build up the numpy logo
n_voxels = np.zeros((4, 3, 4), dtype=bool)
n_voxels[0, 0, :] = True
n_voxels[-1, 0, :] = True
n_voxels[1, 0, 2] = True
n_voxels[2, 0, 1] = True
facecolors = np.where(n_voxels, '#FFD65DC0', '#7A88CCC0')
edgecolors = np.where(n_voxels, '#BFAB6E', '#7D84A6')
filled = np.ones(n_voxels.shape)
# upscale the above voxel image, leaving gaps
filled_2 = explode(filled)
fcolors_2 = explode(facecolors)
ecolors_2 = explode(edgecolors)
# Shrink the gaps
x, y, z = np.indices(np.array(filled_2.shape) + 1).astype(float) // 2
x[0::2, :, :] += 0.05
y[:, 0::2, :] += 0.05
z[:, :, 0::2] += 0.05
x[1::2, :, :] += 0.95
y[:, 1::2, :] += 0.95
z[:, :, 1::2] += 0.95
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.voxels(x, y, z, filled_2, facecolors=fcolors_2, edgecolors=ecolors_2)
plt.show()
'''
=====================================
Rotating 3D voxel animation of PYTHON
=====================================
Demonstrates using ``ax.voxels`` with uneven coordinates
'''
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as manimation
from math import copysign
def explode(data):
size = np.array(data.shape)*2
data_e = np.zeros(size - 1, dtype=data.dtype)
data_e[::2, ::2, ::2] = data
return data_e
def voxel_face(corns, dm, nf):
'''
Grab the corner coordinates of one voxel face
Parameters
----------
corns : np.indices array of corners for one voxel
dm : (dimension), values can be 0(x), 1(y), 2(z)
nf : (near/far face), values can be 0(near), 1(far)
'''
lc = corns.copy() #local copy so we don't swap original
if dm == 1 : #swap y into x and correct ordering
lc[0], lc[1] = corns[1].transpose(1,0,2), corns[0].transpose(1,0,2)
if dm == 2 : #swap z into x and correct ordering
lc[0], lc[2] = corns[2].transpose(2,1,0), corns[0].transpose(2,1,0)
ret = np.zeros((3,2,2))
xc1 = lc[0,nf,0,0] #hold x dim constant
ret[0,:] = np.array([[xc1, xc1], [xc1, xc1]])
yc1, yc2 = lc[1,0,0:2,0]
ret[1,:] = np.array([[yc1, yc2], [yc1, yc2]])
zc1, zc2 = lc[2,0,0,0:2]
ret[2,:] = np.array([[zc1, zc1], [zc2, zc2]])
if dm != 0 : #swap x back into desired dimension
ret[0], ret[dm] = ret[dm].copy(), ret[0].copy()
return ret
# build PYTHON letters
n_voxels = np.zeros((4, 4, 5), dtype=bool)
letters = [None]*6
letter_faces = np.zeros((6,2),dtype=int)
#P
n_voxels[0, 0, :] = True
n_voxels[:, 0, -3] = True
n_voxels[:, 0, -1] = True
n_voxels[-1, 0, -2] = True
letters[0] = np.array(np.where(n_voxels)).T
letter_faces[0] = [1, 0] #close y face
n_voxels[...] = False
#Y
n_voxels[-1, 0, -3:] = True
n_voxels[-1, -1, :] = True
n_voxels[-1, :, -3] = True
n_voxels[-1, :, 0] = True
letters[1] = np.array(np.where(n_voxels)).T
letter_faces[1] = [0, 1] #far x face
n_voxels[...] = False
#T
n_voxels[:, 0, -1] = True
n_voxels[1:3, :, -1] = True
letters[2] = np.array(np.where(n_voxels)).T
letter_faces[2] = [2, 1] #far z face
n_voxels[...] = False
#H
n_voxels[0, 0, :] = True
n_voxels[0, -1, :] = True
n_voxels[0, :, 2] = True
letters[3] = np.array(np.where(n_voxels)).T
letter_faces[3] = [0, 0] #close x face
n_voxels[...] = False
#O
n_voxels[0, 1:3, 0] = True
n_voxels[-1, 1:3, 0] = True
n_voxels[1:3, 0, 0] = True
n_voxels[1:3, -1, 0] = True
letters[4] = np.array(np.where(n_voxels)).T
letter_faces[4] = [2, 0] #close z face
n_voxels[...] = False
#N
n_voxels[0, -1, :] = True
n_voxels[-1, -1, :] = True
n_voxels[1, -1, 1:3] = True
n_voxels[2, -1, 2:4] = True
letters[5] = np.array(np.where(n_voxels)).T
letter_faces[5] = [1, 1] #far y face
n_voxels[...] = False
fcol = np.full(n_voxels.shape, '#7A88CC60')
ecol = np.full(n_voxels.shape, '#7D84A6')
filled = np.ones(n_voxels.shape)
# upscale the above voxel image, leaving gaps
filled_2 = explode(filled)
fcolors_2 = explode(fcol)
ecolors_2 = explode(ecol)
# Shrink the gaps
corn = np.indices(np.array(filled_2.shape) + 1).astype(float) // 2
ccorn = 0.05 #close corner
fcorn = 1.0 - ccorn
corn[0,0::2, :, :] += ccorn
corn[1,:, 0::2, :] += ccorn
corn[2,:, :, 0::2] += ccorn
corn[0,1::2, :, :] += fcorn
corn[1,:, 1::2, :] += fcorn
corn[2,:, :, 1::2] += fcorn
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.axis("off")
#Plot the voxels
x, y, z = corn
ax.voxels(x, y, z, filled_2, facecolors=fcolors_2, edgecolors=ecolors_2)
#Plot the letter square faces
jj=0
for j in [x for x in letters if x is not None]:
locf = np.empty((j.shape[0],3,2,2)) #local face
ji = 0
for i in j:
i = i * 2 #skip empty voxels
loc = corn[:,i[0]:i[0]+2,i[1]:i[1]+2,i[2]:i[2]+2] #local corners
locf[ji] = voxel_face(loc, letter_faces[jj,0], letter_faces[jj,1])
ax.plot_surface(locf[ji,0],locf[ji,1],locf[ji,2],color='#ffe500a0',
shade=False)
ji += 1
jj += 1
#Views: PY, P, Y, T, H, O, N, PY
view_elev = [ 5, 0, 0, 90, 0, -90, 0, 5]
view_azim = [-60, -90, 0, 90, 180, 180, 90, -60]
#'''
FFMpegWriter = manimation.writers['ffmpeg']
metadata = dict(title='Movie Test', artist='Matplotlib',
comment='Movie support!')
writer = FFMpegWriter(fps=25, metadata=metadata)
with writer.saving(fig, "pythonRot2.mp4", 100):
for j in range(20):
ax.view_init(view_elev[0], view_azim[0])
plt.draw()
writer.grab_frame()
for i in range(1,len(view_elev)):
de = (view_elev[i] - view_elev[i-1])
da = (view_azim[i] - view_azim[i-1])
if abs(da) >= 180 : #unecessary in this config
da -= copysign(360, da)
if abs(de) >= 180 :
de -= copysign(360, de)
if i != 1 :
steps = 60
else :
steps = 10
da = da / steps
de = de / steps
for j in range(10): #Pause on direct view of a letter
ax.view_init(view_elev[i-1], view_azim[i-1])
plt.draw()
writer.grab_frame()
for j in range(steps): #Rotate to next letter
ax.view_init(view_elev[i-1] + j*de,
view_azim[i-1] + j*da)
plt.draw()
writer.grab_frame()
#'''
I'm trying to evaluate the quality of image provided by implementing nearest neighbour and bi-linear interpolation to resize an image. Currently the two images look identical. I cannot seem to find out the reason for the bi-linear method not providing the smooth output picture it should. Below is nearest neighbour
def scale_image_NN(image, scaling_factor):
cv2.imshow('Original image', lena)
cv2.waitKey(0)
print 'Running'
size = np.shape(image)
scaled_image = np.zeros((size[0]*scaling_factor, size[1]*scaling_factor,3), dtype=np.uint32)
for i in range (0, scaling_factor*size[0]-3):
for j in range (0, scaling_factor*size[1]-3):
x = int(m.floor(i/scaling_factor))
y = int(m.floor(j/scaling_factor))
for k in range (0, 3):
scaled_image[i+1, j+1, k] = image[x+1, y+1, k]
cv2.imshow('Scaled image - NN', scaled_image)
cv2.waitKey(0)
cv2.imwrite('NN.jpg',scaled_image)
and subsequently bi-linear interpolation
def scale_image_BL(image, scaling_factor):
cv2.imshow('Original image', lena)
cv2.waitKey(0)
print 'Running'
orig_size = np.shape(image)
h = orig_size[0]
w = orig_size[1]
c = orig_size[2]
r = scaling_factor
padded_image = np.zeros((h*scaling_factor, w*scaling_factor, c), dtype=np.uint8)
for i in range (0, h*scaling_factor):
x1 = int(m.floor(i/r))
x2 = int(m.ceil(i/r))
if x1 == 0:
x1 = 1
x = np.remainder(i/r,1)
for j in range (0, w*scaling_factor):
y1 = int(m.floor(j/r))
y2 = int(m.ceil(j/r))
if y1 == 0:
y1 = 1
ctl = image[x1, y1, :]
cbl = image[x2, y1, :]
ctr = image[x1, y2, :]
cbr = image[x2, y2, :]
y = np.remainder(j/r, 1)
tr = (ctr*y) + (ctl*(1-y))
br = (ctr*y) + (cbl*(1-y))
padded_image[i, j, :] = (br*x)+(tr*(1-x))
scaledImage = padded_image.astype(np.uint8)
cv2.imshow('Scaled image - BL',scaledImage)
cv2.waitKey(0)
cv2.imwrite('BL.jpg',scaledImage)
The problem was due Why does Python return 0 for simple division calculation?
During the calculation of the two positions to interpolate between, say x1 or x2 in the bi-linear interpolation, python was returing 0 for simple division such as 1/2, and not 0.5, thus there weren't always two points to interpolate between resulting in the NN-type output.
For scale_image_BL(image, scaling_factor) to work, simply include :
from future import division
at the beginning of the script.