Rectangles uneven in CV2 grid - python

I am trying to draw a grid on a (very) large image. I am using cv2 and Python 3 for reference. See the following code:
output_image = np.zeros((23000, 23000), dtype=np.int)
output_image = np.uint8(np.stack((output_image, output_image, output_image), 2)*255)
...
import cv2
def visualise_tiles(image, num_tiles):
X = image[:,0]
Y = image[:,1]
x_range = int(max(X) - min(X))
y_range = int(max(Y) - min(Y))
x_step = int((max(X) - min(X)) // num_tiles)
y_step = int((max(Y) - min(Y)) // num_tiles)
for i in tqdm(range(num_tiles)):
for j in range(num_tiles):
x_left, y_left = x_step * i, y_step * (j + 1)
x_right, y_right = x_step * (i + 1), y_step * j
cv2.rectangle(output_image, (x_left, y_left), (x_right, y_right), (255, 255, 0), 100)
When given a specific image, the x_range is 9786 and y_range is 21674. The x_step thus evaluates to 489, and the y_step to 1083. This is fine, and we expect the resultant rectangles to be non-square, which they are. All good.
My problem is that there is some weird 'stretching' that goes on in the output and the rectangles are uneven, whereas I want a perfect grid. See in particular the attached image at 15000 on the y-axis and 5000 on the x-axis:
I am not sure why this is - can anyone explain?

Related

Floating Point Error in script, which computes a side plane

I have already searched stackoverflow and used different approaches like scipy special library to fix this error, but without success. I always get the Message: "invalid value encountered in double_scalars".Maybe someone can figure out the code snippet and tell me how to do the calculation with floating point numbers.
for i in xrange(0, int(grid_size/2)): # i -> row (y-axis velo plane / x-axis image plane)
for j in xrange(0, i+1): # j -> col (x-axis velo plane / y-axis image plane)
# Compute plane coords
x_min = cell_size * j
x_max = cell_size * (j + 1)
y_min = cell_size * i
y_max = cell_size * (i + 1)
max_angle = np.arctan(y_max / x_min) if x_min !=0 else np.pi/2
change_angle = np.arctan(y_min / x_min) if x_min !=0 else np.pi/2
min_angle = np.arctan(y_min / x_max) if y_min !=0 else 0
num_hor_rays = (max_angle-min_angle)/hrad_res
for p in xrange(0, nplanes):
current_hangle = min_angle
current_vngle = lowrad_angle + p * vrad_res
# Trace ray
# Side plane
while current_hangle < change_angle:
y = y_min
**x = np.cos(current_hangle)*y/np.sin(current_hangle)**
The problem is that in x are stored values, that produce a large number. Would be nice if someone could help me, Thanks a lot

Image Cropping with transformation matrix?

I am training a neural network to do Human Single Pose Estimation on the MPII dataset. Within it, many images contain more than one person and I need to crop the image in order to extract each single person.
Of each I have the position (or center) and the scale w.r.t. 200 px height.
This code does just what I need:
def get_transform(center, scale, res, rot=0):
# Generate transformation matrix
h = 200 * scale
t = np.zeros((3, 3))
t[0, 0] = float(res[1]) / h
t[1, 1] = float(res[0]) / h
t[0, 2] = res[1] * (-float(center[0]) / h + .5)
t[1, 2] = res[0] * (-float(center[1]) / h + .5)
t[2, 2] = 1
if not rot == 0:
rot = -rot # To match direction of rotation from cropping
rot_mat = np.zeros((3,3))
rot_rad = rot * np.pi / 180
sn,cs = np.sin(rot_rad), np.cos(rot_rad)
rot_mat[0,:2] = [cs, -sn]
rot_mat[1,:2] = [sn, cs]
rot_mat[2,2] = 1
# Need to rotate around center
t_mat = np.eye(3)
t_mat[0,2] = -res[1]/2
t_mat[1,2] = -res[0]/2
t_inv = t_mat.copy()
t_inv[:2,2] *= -1
t = np.dot(t_inv,np.dot(rot_mat,np.dot(t_mat,t)))
return t
def transform(pt, center, scale, res, invert=0, rot=0):
# Transform pixel location to different reference
t = get_transform(center, scale, res, rot=rot)
if invert:
t = np.linalg.inv(t)
new_pt = np.array([pt[0], pt[1], 1.]).T
new_pt = np.dot(t, new_pt)
return new_pt[:2].astype(int)
def crop(img, center, scale, res, rot=0):
# Upper left point
ul = np.array(transform([0, 0], center, scale, res, invert=1))
# Bottom right point
br = np.array(transform(res, center, scale, res, invert=1))
new_shape = [br[1] - ul[1], br[0] - ul[0]]
if len(img.shape) > 2:
new_shape += [img.shape[2]]
new_img = np.zeros(new_shape)
# Range to fill new array
new_x = max(0, -ul[0]), min(br[0], len(img[0])) - ul[0]
new_y = max(0, -ul[1]), min(br[1], len(img)) - ul[1]
# Range to sample from original image
old_x = max(0, ul[0]), min(len(img[0]), br[0])
old_y = max(0, ul[1]), min(len(img), br[1])
new_img[new_y[0]:new_y[1], new_x[0]:new_x[1]] = img[old_y[0]:old_y[1], old_x[0]:old_x[1]]
return cv2.resize(new_img, res)
However, I haven't figured out what kind of transformation matrix it is (the one that was created to derive ul or br).
Could someone explain to me what happens in these functions?
Thank you

OpenCV: Remap in reverse order

I want to project an image from spherical to cubemap. From what I understood studying maths, I need to create a theta, phi distribution for each pixel and then convert it into cartesian system to get a normalized pixel map.
I used the following code to do so
theta = 0
phi = np.pi/2
squareLength = 2048
# theta phi distribution for X-positive face
t = np.linspace(theta + np.pi/4, theta - np.pi/4, squareLength)
p = np.linspace(phi + np.pi/4, phi - np.pi/4, squareLength)
x, y = np.meshgrid(t, p)
# converting into cartesion sytem for X-positive face (where r is the distance from sphere center to cube plane and X is constantly 0.5 in cartesian system)
X = np.zeros_like(y)
X[:,:] = 0.5
r = X / (np.cos(x) * np.sin(y))
Y = r * np.sin(x) * np.sin(y)
Z = r * np.cos(y)
XYZ = np.stack((X, Y, Z), axis=2)
# shifting pixels from the negative side
XYZ = XYZ + [0, 0.5, 0.5]
# since i want to project on X-positive face my map should be
x_map = -XYZ[:, :, 1] * squareLength
y_map = XYZ[:,:, 2] * squareLength
The above map created should give me my desired result with cv2.remap() but it's not. Then I tried looping through pixels and implement my own remap without interpolation or extrapolation. With some hit and trial, I deduced the following formula which gives me the correct result
for i in range(2048):
for j in range(2048):
try:
image[int(y_map[i,j]), int(x_map[i,j])] = im[i, j]
except:
pass
which is reverse of actual cv2 remapping which says dst(x,y)=src(mapx(x,y),mapy(x,y))
I do not understand if did the math all wrong or is there a way to covert x_map and y_map to correct forms so that cv2.remap() gives the desired result.
INPUT IMAGE
DESIRED RESULT (this one is without interpolation using loops)
CURRENT RESULT (using cv2.remap())
I'm quite new in opencv and I didn't work with so difficult math algorithms before but I tried to do this. I rewrote your code a bit and here it is:
import numpy as np
import cv2
src = cv2.imread("data/pink_sq.png")
def make_map():
theta = 0
phi = np.pi / 2
squareLength = 4000
# theta phi distribution for X-positive face
t = np.linspace((theta - np.pi / 4), (theta + np.pi / 4), squareLength)
p = np.linspace((phi + np.pi / 4), (phi - np.pi / 4), squareLength)
x, y = np.meshgrid(t, p)
x_res = np.zeros_like(y)
x_res[:, :] = 0.5
r = x_res * (np.cos(x))
r /= np.amax(r)
y_res = r * x
z_res = r * np.cos(y)
xyz = np.stack((x_res, y_res, z_res), axis=2)
# shifting pixels from the negative side
xyz = xyz + [0, 0.5, 0.5]
# since i want to project on X-positive face my map should be
x_map = xyz[:, :, 1] * squareLength
y_map = xyz[:, :, 2] * squareLength
map_x = y_map.astype("float32")
map_y = x_map.astype("float32")
return map_x, map_y
map_x, map_y = make_map()
dst = cv2.remap(src, map_y, map_x, cv2.INTER_LINEAR)
cv2.imwrite("res.png", dst)
I don't understand the math in this code at all but I rewrote it a bit and I should say that it works quite good. Here is the result image:
And yes, there is a bit difference between my result picture and yours but I hope it is ok :) If I'm not right somewhere of course downvote this answer because I'm not sure that it is correct one.
I'm almost certain the issue has to do with the orientation of the reference frame in space. Maybe if you clarify the Math a bit we can help.

How to plot circles every 20 pixels between two randomly generated points in Pygame?

The homework question I'm working on is the following:
"Draw two randomly placed radius 10 circles on the screen then draw radius 2 circles every twenty pixels from the center of one to the center of the other."
I'm having no trouble randomly generating the two radius 10 circles, but I have no idea how to plot the radius 2 circles between them.
I've tried briefly to plot a line between them, and if there's a way to plot my points along that line, I could definitely do that. I've looked up similar issues and a lot of them mention Bresenham's line algorithm but I doubt that is the answer as it seems very advanced.
Here is the code I have so far for the problem:
import pygame
from random import randint
linecolour = 0,0,0
bgcolour = 255, 255, 255
width = 600
height = 600
screen = pygame.display.set_mode((width, height))
running = 1
x = []
y = []
for i in range (2):
x.append(randint(0,600))
y.append(randint(0,600))
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT: # or other types of events
done = True
screen.fill(bgcolour)
for i,j in zip(x,y):
pygame.draw.circle(screen,linecolour,(i,j),10)
pygame.draw.circle(screen,linecolour,(i,j),2)
pygame.display.flip()
Calculate the direction vector from 1 point to the other:
dir = x[1]-x[0], y[1]-y[0]
Calculate the Euclidean distance between the points. Note you've to import math:
dist = math.sqrt(dir[0]*dir[0] + dir[1]*dir[1])
or as pointed out in the answer of #MadPhysicist
dist = math.hypot(*dir)
the number of points to be drawn is int(dist) // 20. Calculate the points on the line in a loop:
for i in range(int(dist) // 20):
px = int(x[0] + dir[0] * i*20/dist)
py = int(y[0] + dir[1] * i*20/dist)
The code to draw the small points may look like this:
done = False
while not done:
# [...]
dir = x[1]-x[0], y[1]-y[0]
dist = math.hypot(*dir)
for i in range(1, int(dist) // 20 + 1):
pt = int(x[0] + dir[0] * i*20/dist), int(y[0] + dir[1] *i*20/dist)
pygame.draw.circle(screen, linecolour, pt, 2)
This is a very simple problem if you look at it the right way. I would recommend looking at it in terms of polar coordinates. If you have two circles centered at (x[0], y[0]) and (x[1], y[1]), the slope of the line between them is (y[1] - y[0]) / (x[1] - x[0]), but you can also look at the angle of the line:
phi = math.atan2(y[0] - y[1], x[0] - x[1])
The distance from one center to the other is given by
r = math.hypot(y[0] - y[1], x[0] - x[1])
Now you can easily step along the line from (x[0], y[0]) at an angle of phi in steps of 20 until your distance exceeds r. The x-coorindate of the i-th step will be
i * 20 * math.cos(phi)
Similarly the y-coordinate will be
i * 20 * math.sin(phi)
You can calculate the total number of steps as r // 20. Also, math.sin(math.atan2(y, x)) simplifies to y / math.hypot(y, x) and the similar cosine simplifies to x / math.hypot(y, x). So all in all, you get
sep = 20
dx = x[1] - x[0]
dy = y[1] - y[0]
r = math.hypot(dy, dx)
n = int(r // sep)
x_step = sep * dx / r
y_step = sep * dy / r
coords = [(x[0] + i * x_step, y[0] + i * y_step) for i in range(n)]
If you need integer coordinates:
coords = [(x[0] + int(i * x_step), y[0] + int(i * y_step)) for i in range(n)]
To plot:
for coord in [(x[0] + int(i * x_step), y[0] + int(i * y_step)) for i in range(n)]:
pygame.draw.circle(screen, linecolour, coord, 2)

Bezier Curve using static points

So I found this code on line and it does a random Bezier Curve which uses random points. I was trying to make it non random so that it would use static points I got it to use only 4 points which was easy. I have never used PIL before in python and in fact I am slowly learning python. And I have only really done front end work (html, javascript, css, etc) and I just wanted to know if some one can help me.
Here is the code I found on line:
# Random Bezier Curve using De Casteljau's algorithm
# http://en.wikipedia.org/wiki/Bezier_curve
# http://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm
# FB - 201111244
import random
from PIL import Image, ImageDraw
imgx = 500
imgy = 500
image = Image.new("RGB", (imgx, imgy))
draw = ImageDraw.Draw(image)
def B(coorArr, i, j, t):
if j == 0:
return coorArr[i]
return B(coorArr, i, j - 1, t) * (1 - t) + B(coorArr, i + 1, j - 1, t) * t
n = 4 # number of control points
coorArrX = []
coorArrY = []
for k in range(n):
x = (0, imgx - 1)
y = (0, imgy - 1)
coorArrX.append(x)
coorArrY.append(y)
# plot the curve
numSteps = 10000
for k in range(numSteps):
t = float(k) / (numSteps - 1)
x = int(B(coorArrX, 0, n - 1, t))
y = int(B(coorArrY, 0, n - 1, t))
try:
image.putpixel((x, y), (0, 255, 0))
except:
pass
# plot the control points
cr = 3 # circle radius
for k in range(n):
x = coorArrX[k]
y = coorArrY[k]
try:
draw.ellipse((x - cr, y - cr, x + cr, y + cr), (255, 0, 0))
except:
pass
# image.save("BezierCurve.png", "PNG")
image.show() I add this so I can see it right away
Any help if at all would be great.
Ok The long detailed BS that began this all is below the long line. The resulting answer is here.
Your static points are x,y coordinates with the x values and y values placed in seperate arrays (coorArrx and coorArrY respectively) make sure to never use a value = imgx or imy.
# Random Bezier Curve using De Casteljau's algorithm
# http://en.wikipedia.org/wiki/Bezier_curve
# http://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm
# FB - 201111244
import random
from PIL import Image, ImageDraw
imgx = 500
imgy = 500
image = Image.new("RGB", (imgx, imgy))
draw = ImageDraw.Draw(image)
def B(coorArr, i, j, t):
if j == 0:
return coorArr[i]
return B(coorArr, i, j - 1, t) * (1 - t) + B(coorArr, i + 1, j - 1, t) * t
# n = random.randint(3, 6) # number of control points
n=4
#coorArrX = []
#coorArrY = []
#for k in range(n):
# x = random.randint(0, imgx - 1)
# y = random.randint(0, imgy - 1)
# coorArrX.append(x)
# coorArrY.append(y)
coorArrX=[3,129,12,77]
coorArrY=[128,52,12,491]
# plot the curve
numSteps = 10000
for k in range(numSteps):
t = float(k) / (numSteps - 1)
x = int(B(coorArrX, 0, n - 1, t))
y = int(B(coorArrY, 0, n - 1, t))
try:
image.putpixel((x, y), (0, 255, 0))
except:
pass
# plot the control points
cr = 3 # circle radius
for k in range(n):
x = coorArrX[k]
y = coorArrY[k]
try:
draw.ellipse((x - cr, y - cr, x + cr, y + cr), (255, 0, 0))
except:
pass
image.show()
=.........................................................................................=
I am also something of a newcommer to all of this, and I REFUSE to look this up as I see it like you do...a learning experiencee.
But as I look at this code I see something strange
for k in range(n):
x = (0, imgx - 1)
y = (0, imgy - 1)
coorArrX.append(x)
coorArrY.append(y)
Are you sure this part is correct? imgx is defined as 500 elsewhere, and n is 4.
so this could read as
for k in range(4):
x = (0, 500 - 1)
y = (0, 500 - 1)
which (since these values never change at all in this code) means:
x = (0, 499)
y = (0, 499)
on every pass.
So each time they get to :
coorArrX.append(x)
coorArrY.append(y)
They simply keep adding new copies of the same data to the array, so when it is done the array looks like this (internally)
[(0, 499), (0, 499), (0, 499), (0,499)]
What makes this more confusing, is that coorArrX and coorArrY are A) Identical, and B) identical in their basic parts(that is each element is identical). Therefore, when you get to this part of the code:
# plot the control points
cr = 3 # circle radius
for k in range(n):
x = coorArrX[k]
y = coorArrY[k]
try:
draw.ellipse((x - cr, y - cr, x + cr, y + cr), (255, 0, 0))
except:
pass
and you substitute in the values in the arrays, you get:
# plot the control points
cr = 3 # circle radius
for k in range(n):
x = coorArrX[k]
y = coorArrY[k]
try:
draw.ellipse(((0, 499) - 3, (0, 499) - 3, (0, 499) + 3, (0, 499) + 3), (255, 0, 0))
except:
pass
Now this is the part that controls the drawing of the curved segments for the plot, but I do not see how centering an elispe on those impossible coordinate sets can draw anything?!
Broke down and did a copy paste test run. This code is purely bogus, either placed to dupe people into wasting time, or placed where OP found it for same reason.
But it was fun trying!!
From your description, the only problem seems to be about Python basics. I have rearranged the code as follows, so the only things that need to be touched are at bottom. Now, if you want to manually specify 4 control points, go ahead and do it (in the following code I have specified 4 of them myself as an example). You need to understand that, in the original code, coorArrX and coorArrY are just lists, which will hold 4 points each (x and y coordinates, respectively). If you are manually specifying them, there is no point in using a loop to write them. I hope this code is clear enough:
# Random Bezier Curve using De Casteljau's algorithm
# http://en.wikipedia.org/wiki/Bezier_curve
# http://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm
# FB - 201111244
from PIL import Image, ImageDraw
def plot_curve(image, px, py, steps=1000, color=(0, 255, 0)):
def B(coord, i, j, t):
if j == 0:
return coord[i]
return (B(coord, i, j - 1, t) * (1 - t) +
B(coord, i + 1, j - 1, t) * t)
img = image.load()
for k in range(steps):
t = float(k) / (steps - 1)
x = int(B(px, 0, n - 1, t))
y = int(B(py, 0, n - 1, t))
try:
img[x, y] = color
except IndexError:
pass
def plot_control_points(image, px, py, radi=3, color=(255, 0, 0)):
draw = ImageDraw.Draw(image)
for x, y in zip(px, py):
draw.ellipse((x - radi, y - radi, x + radi, y + radi), color)
# Your fixed, manually specified, points.
n = 4
coord_x = [25, 220, 430, 410]
coord_y = [250, 10, 450, 40]
image = Image.new("RGB", (500, 500))
plot_curve(image, coord_x, coord_y)
plot_control_points(image, coord_x, coord_y)
image.save("BezierCurve.png")

Categories

Resources