drawing the game board in Haskell - problem - haskell

I'm trying to build the board for the Peg Solitaire game but I'm stuck. I hope you can help me. The following code runs and generates a square of 3 on 3 circles. How could I make 3 more such squares but put them in other positions? I use the Gloss library
module Main(main) where
import Graphics.Gloss
import Graphics.Gloss.Data.ViewPort
import Graphics.Gloss.Interface.Pure.Game
import Data.List
width, height, offset :: Int
width = 400
height = 400
offset = 100
window :: Display
window = InWindow "Peg Solitaire" (width, height) (offset, offset)
background :: Color
background = white
drawing :: Picture
drawing = Pictures [ (translate (x * 40) (y * 40) $ circleSolid 12)| x<-[-1..1], y<-[2..4] ]
main = display window background drawing

I am not familiar with your graphic library, but apparently you can use a list comprehension and pass it to the Pictures constructor.
So it is just a matter of writing the appropriate list comprehension expression.
The expression you give can be rewritten as:
drawing1 = let circles1 = [ (translate (x1 * 40) (y1 * 40) $ circleSolid 12) |
x1 <- [-1..1], y1 <- [2..4] ] in Pictures circles1
If you would like to arrange your 3+1=4 circle groups into a regular grid, you can introduce extra loop levels, say with variables x0 and y0, like this:
drawing2 = let circles2 = [ (translate (x0*200 + x1*40) (y0*200 + y1*40)
$ circleSolid 12) |
x0 <- [0,1] , y0 <- [0,1],
x1 <- [-1..1], y1 <- [2..4] ]
in Pictures circles2
If you prefer to arrange the circle groups in arbitrary fashion, you can introduce one extra loop variable, say cg which is to run over the main coordinates of the circle groups:
circleGroups = [ (0,0), (0,200), (200,0), (200,200) ] -- list of (x,y) pairs
drawing3 = let circles3 = [
translate ((fst cg)*200 + x1*40) ((snd cg)*200 + y1*40)
$ circleSolid 12 |
cg <- circleGroups,
x1 <- [-1..1], y1 <- [2..4] ]
in Pictures circles3
Note: Please please limit your source code to about 80 characters per line, so we don't have to use the horizontal slider. This is really a massive hindrance to code readability. Thanks.

Related

How to generate two sets of distinct points on a sphere in julia language?

I need to apply the PCA at different points of a spherical cap, but I don’t know how to build these sets of different points, I need at least 2 sets.
Here is a picture with the idea of what I need.
Spherical Cap
If I correctly understand, here is how I would do in R.
library(uniformly)
library(pracma)
library(rgl)
# sample points on a spherical cap
points_on_cap1 <- runif_on_sphericalCap(300, r = 2, h = 0.5)
# convert to spherical coordinates
sphcoords1 <- cart2sph(points_on_cap1)
# sample points on a spherical cap
points_on_cap2 <- runif_on_sphericalCap(300, r = 2, h = 0.5)
# rotate them, because this is the same spherical cap as before
points_on_cap2 <- rotate3d(points_on_cap2, 3*pi/4, 1, 1, 1)
# convert to spherical coordinates
sphcoords2 <- cart2sph(points_on_cap2)
# 3D plot
spheres3d(0, 0, 0, radius = 2, alpha = 0.5, color = "yellow")
points3d(points_on_cap1, color = "blue")
points3d(points_on_cap2, color = "red")
# 2D plot (of the spherical coordinates)
plot(
sphcoords1[, 1:2], xlim = c(-pi, pi), ylim = c(-pi/2, pi/2),
pch = 19, col = "blue"
)
points(sphcoords2[, 1:2], pch = 19, col = "red")
Do I understand?
Here is the function runif_on_sphericalCap:
function(n, r = 1, h){
stopifnot(h > 0, h < 2*r)
xy <- runif_in_sphere(n, 2L, 1)
k <- h * apply(xy, 1L, crossprod)
s <- sqrt(h * (2*r - k))
cbind(s*xy, r-k)
}
It always samples on a spherical cap with symmetry axis joining the center of the sphere to the North pole. That is why I do a rotation, to get another spherical cap.
Say me if I understand and I'll try to help you to convert the code to Julia.
EDIT: Julia code
using Random, Distributions, LinearAlgebra
function runif_in_sphere(n::I, d::I, r::R) where {I<:Integer, R<:Number}
G = Normal()
sims = rand(G, n, d)
norms = map(norm, eachrow(sims))
u = rand(n) .^ (1/d)
return r .* u .* broadcast(*, 1 ./ norms, sims)
end
function runif_on_sphericalCap(n::I, r::Number, h::Number) where {I<:Integer}
if h <= 0 || h >= 2*r
error("")
end
xy = runif_in_sphere(n, 2, 1.0)
k = h .* map(x -> dot(x,x), eachrow(xy))
s = sqrt.(h .* (2*r .- k))
return hcat(broadcast(*, s, xy), r .- k)
end

Filling pixels under or above some function

Seems like a simple problem, but I just cant wrap my head around it.
I have a config file in which I declare a few functions. It looks like this:
"bandDefinitions" : [
{
"0": ["x^2 + 2*x + 5 - y", "ABOVE"]
},
{
"0": ["sin(6*x) - y", "UNDER"]
},
{
"0": ["tan(x) - y", "ABOVE"]
}
]
These functions should generate 3 images. Every image should be filled depending on solution of equations, and provided position (Under or Above). I need to move the coordinate system to the center of the image, so I'm adding -y into the equation. Part of image which should be filled should be colored white, and the other part should be colored black.
To explain what I mean, I'm providing images for quadratic and sin functions.
What I'm doing is solve the equation for x in [-W/2, W/2] and store the solutions into the array, like this:
#Generates X axis dots and solves an expression which defines a band
#Coordinate system is moved to the center of the image
def __solveKernelDefinition(self, f):
xAxis = range(-kernelSize, kernelSize)
dots = []
for x in xAxis:
sol = f(x, kernelSize/2)
dots.append(sol)
print(dots)
return dots
I'm testing if some pixel should be colored white like this:
def shouldPixelGetNoise(y, x, i, currentBand):
shouldGetNoise = True
for bandKey in currentBand.bandDefinition.keys():
if shouldGetNoise:
pixelSol = currentBand.bandDefinition[bandKey][2](x, y)
renderPos = currentBand.bandDefinition[bandKey][1]
bandSol = currentBand.bandDefinition[bandKey][0]
shouldGetNoise = shouldGetNoise and pixelSol <= bandSol[i] if renderPos == Position.UNDER else pixelSol >= bandSol[i]
else:
break
return shouldGetNoise
def kernelNoise(kernelSize, num_octaves, persistence, currentBand, dimensions=2):
simplex = SimplexNoise(num_octaves, persistence, dimensions)
data = []
for i in range(kernelSize):
data.append([])
i1 = i - int(kernelSize / 2)
for j in range(kernelSize):
j1 = j - int(kernelSize / 2)
if(shouldPixelGetNoise(i1, j1, i, currentBand)):
noise = normalize(simplex.fractal(i, j, hgrid=kernelSize))
data[i].append(noise * 255)
else:
data[i].append(0)
I'm only getting good output for convex quadratic functions. If I try to combine them, I get a black image. Sin just doesn't work at all. I see that this bruteforce approach won't lead me anywhere, so I was wondering what algorithm should I use to generate these kinds of images?
As far as I understood, you want to plot your functions and fill up above or under of these functions. You might easily do this by creating a grid (i.e. a 2D Cartesian coordinate system) in numpy, and define your functions on the grid.
import numpy as np
import matplotlib.pyplot as plt
max_ax = 100
resolution_x = max_ax/5
resolution_y = max_ax/20
y,x = np.ogrid[-max_ax:max_ax+1, -max_ax:max_ax+1]
y,x = y/resolution_y, x/resolution_x
func1 = x**2 + 2*x + 5 <= -y
resolution_x = max_ax
resolution_y = max_ax
y,x = np.ogrid[-max_ax:max_ax+1, -max_ax:max_ax+1]
y,x = y/resolution_y, x/resolution_x
func2 = np.sin(6*x) <= y
func3 = np.tan(x) <= -y
fig,ax = plt.subplots(1,3)
ax[0].set_title('f(x)=x**2 + 2*x + 5')
ax[0].imshow(func1,cmap='gray')
ax[1].set_title('f(x)=sin(6*x)')
ax[1].imshow(func2,cmap='gray')
ax[2].set_title('f(x)=tan(x)')
ax[2].imshow(func3,cmap='gray')
plt.show()
Is this what you are looking for?
Edit: I adjusted the limits of x- and y-axes. Because, for example, sin(x) does not make much sense outside of the range [-1,1].

TkInter python - creating points on a canvas to obtain a Sierpinsky triangle

I want to make a program which plots a Sierpinsky triangle (of any modulo). In order to do it I've used TkInter. The program generates the fractal by moving a point randomly, always keeping it in the sides. After repeating the process many times, the fractal appears.
However, there's a problem. I don't know how to plot points on a canvas in TkInter. The rest of the program is OK, but I had to "cheat" in order to plot the points by drawing small lines instead of points. It works more or less, but it doesn't have as much resolution as it could have.
Is there a function to plot points on a canvas, or another tool to do it (using Python)? Ideas for improving the rest of the program are also welcome.
Thanks. Here's what I have:
from tkinter import *
import random
import math
def plotpoint(x, y):
global canvas
point = canvas.create_line(x-1, y-1, x+1, y+1, fill = "#000000")
x = 0 #Initial coordinates
y = 0
#x and y will always be in the interval [0, 1]
mod = int(input("What is the modulo of the Sierpinsky triangle that you want to generate? "))
points = int(input("How many points do you want the triangle to have? "))
tkengine = Tk() #Window in which the triangle will be generated
window = Frame(tkengine)
window.pack()
canvas = Canvas(window, height = 700, width = 808, bg = "#FFFFFF") #The dimensions of the canvas make the triangle look equilateral
canvas.pack()
for t in range(points):
#Procedure for placing the points
while True:
#First, randomly choose one of the mod(mod+1)/2 triangles of the first step. a and b are two vectors which point to the chosen triangle. a goes one triangle to the right and b one up-right. The algorithm gives the same probability to every triangle, although it's not efficient.
a = random.randint(0,mod-1)
b = random.randint(0,mod-1)
if a + b < mod:
break
#The previous point is dilated towards the origin of coordinates so that the big triangle of step 0 becomes the small one at the bottom-left of step one (divide by modulus). Then the vectors are added in order to move the point to the same place in another triangle.
x = x / mod + a / mod + b / 2 / mod
y = y / mod + b / mod
#Coordinates [0,1] converted to pixels, for plotting in the canvas.
X = math.floor(x * 808)
Y = math.floor((1-y) * 700)
plotpoint(X, Y)
tkengine.mainloop()
If you are wanting to plot pixels, a canvas is probably the wrong choice. You can create a PhotoImage and modify individual pixels. It's a little slow if you plot each individual pixel, but you can get dramatic speedups if you only call the put method once for each row of the image.
Here's a complete example:
from tkinter import *
import random
import math
def plotpoint(x, y):
global the_image
the_image.put(('#000000',), to=(x,y))
x = 0
y = 0
mod = 3
points = 100000
tkengine = Tk() #Window in which the triangle will be generated
window = Frame(tkengine)
window.pack()
the_image = PhotoImage(width=809, height=700)
label = Label(window, image=the_image, borderwidth=2, relief="raised")
label.pack(fill="both", expand=True)
for t in range(points):
while True:
a = random.randint(0,mod-1)
b = random.randint(0,mod-1)
if a + b < mod:
break
x = x / mod + a / mod + b / 2 / mod
y = y / mod + b / mod
X = math.floor(x * 808)
Y = math.floor((1-y) * 700)
plotpoint(X, Y)
tkengine.mainloop()
You can use canvas.create_oval with the same coordinates for the two corners of the bounding box:
from tkinter import *
import random
import math
def plotpoint(x, y):
global canvas
# point = canvas.create_line(x-1, y-1, x+1, y+1, fill = "#000000")
point = canvas.create_oval(x, y, x, y, fill="#000000", outline="#000000")
x = 0 #Initial coordinates
y = 0
#x and y will always be in the interval [0, 1]
mod = int(input("What is the modulo of the Sierpinsky triangle that you want to generate? "))
points = int(input("How many points do you want the triangle to have? "))
tkengine = Tk() #Window in which the triangle will be generated
window = Frame(tkengine)
window.pack()
canvas = Canvas(window, height = 700, width = 808, bg = "#FFFFFF") #The dimensions of the canvas make the triangle look equilateral
canvas.pack()
for t in range(points):
#Procedure for placing the points
while True:
#First, randomly choose one of the mod(mod+1)/2 triangles of the first step. a and b are two vectors which point to the chosen triangle. a goes one triangle to the right and b one up-right. The algorithm gives the same probability to every triangle, although it's not efficient.
a = random.randint(0,mod-1)
b = random.randint(0,mod-1)
if a + b < mod:
break
#The previous point is dilated towards the origin of coordinates so that the big triangle of step 0 becomes the small one at the bottom-left of step one (divide by modulus). Then the vectors are added in order to move the point to the same place in another triangle.
x = x / mod + a / mod + b / 2 / mod
y = y / mod + b / mod
#Coordinates [0,1] converted to pixels, for plotting in the canvas.
X = math.floor(x * 808)
Y = math.floor((1-y) * 700)
plotpoint(X, Y)
tkengine.mainloop()
with a depth of 3 and 100,000 points, this gives:
Finally found a solution: if a 1x1 point is to be placed in pixel (x,y), a command which does it exactly is:
point = canvas.create_line(x, y, x+1, y+1, fill = "colour")
The oval is a good idea for 2x2 points.
Something remarkable about the original program is that it uses a lot of RAM if every point is treated as a separate object.

How do you check for intersection between a line segment and a line ray emanating from a point at an angle from horizontal?

Given a line segment, that is two points (x1,y1) and (x2,y2), one point P(x,y) and an angle theta. How do we find if this line segment and the line ray that emanates from P at an angle theta from horizontal intersects or not? If they do intersect, how to find the point of intersection?
Let's label the points q = (x1, y1) and q + s = (x2, y2). Hence s = (x2 − x1, y2 − y1). Then the problem looks like this:
Let r = (cos θ, sin θ). Then any point on the ray through p is representable as p + t r (for a scalar parameter 0 ≤ t) and any point on the line segment is representable as q + u s (for a scalar parameter 0 ≤ u ≤ 1).
The two lines intersect if we can find t and u such that p + t r = q + u s:
See this answer for how to find this point (or determine that there is no such point).
Then your line segment intersects the ray if 0 ≤ t and 0 ≤ u ≤ 1.
Here is a C# code for the algorithm given in other answers:
/// <summary>
/// Returns the distance from the ray origin to the intersection point or null if there is no intersection.
/// </summary>
public double? GetRayToLineSegmentIntersection(Point rayOrigin, Vector rayDirection, Point point1, Point point2)
{
var v1 = rayOrigin - point1;
var v2 = point2 - point1;
var v3 = new Vector(-rayDirection.Y, rayDirection.X);
var dot = v2 * v3;
if (Math.Abs(dot) < 0.000001)
return null;
var t1 = Vector.CrossProduct(v2, v1) / dot;
var t2 = (v1 * v3) / dot;
if (t1 >= 0.0 && (t2 >= 0.0 && t2 <= 1.0))
return t1;
return null;
}
Thanks Gareth for a great answer. Here is the solution implemented in Python. Feel free to remove the tests and just copy paste the actual function. I have followed the write-up of the methods that appeared here, https://rootllama.wordpress.com/2014/06/20/ray-line-segment-intersection-test-in-2d/.
import numpy as np
def magnitude(vector):
return np.sqrt(np.dot(np.array(vector),np.array(vector)))
def norm(vector):
return np.array(vector)/magnitude(np.array(vector))
def lineRayIntersectionPoint(rayOrigin, rayDirection, point1, point2):
"""
>>> # Line segment
>>> z1 = (0,0)
>>> z2 = (10, 10)
>>>
>>> # Test ray 1 -- intersecting ray
>>> r = (0, 5)
>>> d = norm((1,0))
>>> len(lineRayIntersectionPoint(r,d,z1,z2)) == 1
True
>>> # Test ray 2 -- intersecting ray
>>> r = (5, 0)
>>> d = norm((0,1))
>>> len(lineRayIntersectionPoint(r,d,z1,z2)) == 1
True
>>> # Test ray 3 -- intersecting perpendicular ray
>>> r0 = (0,10)
>>> r1 = (10,0)
>>> d = norm(np.array(r1)-np.array(r0))
>>> len(lineRayIntersectionPoint(r0,d,z1,z2)) == 1
True
>>> # Test ray 4 -- intersecting perpendicular ray
>>> r0 = (0, 10)
>>> r1 = (10, 0)
>>> d = norm(np.array(r0)-np.array(r1))
>>> len(lineRayIntersectionPoint(r1,d,z1,z2)) == 1
True
>>> # Test ray 5 -- non intersecting anti-parallel ray
>>> r = (-2, 0)
>>> d = norm(np.array(z1)-np.array(z2))
>>> len(lineRayIntersectionPoint(r,d,z1,z2)) == 0
True
>>> # Test ray 6 --intersecting perpendicular ray
>>> r = (-2, 0)
>>> d = norm(np.array(z1)-np.array(z2))
>>> len(lineRayIntersectionPoint(r,d,z1,z2)) == 0
True
"""
# Convert to numpy arrays
rayOrigin = np.array(rayOrigin, dtype=np.float)
rayDirection = np.array(norm(rayDirection), dtype=np.float)
point1 = np.array(point1, dtype=np.float)
point2 = np.array(point2, dtype=np.float)
# Ray-Line Segment Intersection Test in 2D
# http://bit.ly/1CoxdrG
v1 = rayOrigin - point1
v2 = point2 - point1
v3 = np.array([-rayDirection[1], rayDirection[0]])
t1 = np.cross(v2, v1) / np.dot(v2, v3)
t2 = np.dot(v1, v3) / np.dot(v2, v3)
if t1 >= 0.0 and t2 >= 0.0 and t2 <= 1.0:
return [rayOrigin + t1 * rayDirection]
return []
if __name__ == "__main__":
import doctest
doctest.testmod()
Note: this solution works without making vector classes or defining vector multiplication/division, but is longer to implement. It also avoids division by zero errors. If you just want a block of code and don’t care about the derivation, scroll to the bottom of the post.
Let’s say we have a ray defined by x, y, and theta, and a line defined by x1, y1, x2, and y2.
First, let’s draw two rays that point from the ray’s origin to the ends of the line segment. In pseudocode, that’s
theta1 = atan2(y1-y, x1-x);
theta2 = atan2(y2-y, x2-x);
Next we check whether the ray is inside these two new rays. They all have the same origin, so we only have to check the angles.
To make this easier, let’s shift all the angles so theta1 is on the x axis, then put everything back into a range of -pi to pi. In pseudocode that’s
dtheta = theta2-theta1; //this is where theta2 ends up after shifting
ntheta = theta-theta1; //this is where the ray ends up after shifting
dtheta = atan2(sin(dtheta), cos(dtheta))
ntheta = atan2(sin(ntheta), cos(ntheta))
(Note: Taking the atan2 of the sin and cos of the angle just resets the range of the angle to within -pi and pi without changing the angle.)
Now imagine drawing a line from theta2’s new location (dtheta) to theta1’s new location (0 radians). That’s where the line segment ended up.
The only time where the ray intersects the line segment is when theta is between theta1 and theta2, which is the same as when ntheta is between dtheta and 0 radians. Here is the corresponding pseudocode:
sign(ntheta)==sign(dtheta)&&
abs(ntheta)<=abs(dtheta)
This will tell you if the two lines intersect. Here it is in one pseudocode block:
theta1=atan2(y1-y, x1-x);
theta2=atan2(y2-y, x2-x);
dtheta=theta2-theta1;
ntheta=theta-theta1;
dtheta=atan2(sin(dtheta), cos(dtheta))
ntheta=atan2(sin(ntheta), cos(ntheta))
return (sign(ntheta)==sign(dtheta)&&
abs(ntheta)<=abs(dtheta));
Now that we know the points intersect, we need to find the point of intersection. We’ll be working from a completely clean slate here, so we can ignore any code up to this part. To find the point of intersection, you can use the following system of equations and solve for xp and yp, where lb and rb are the y-intercepts of the line segment and the ray, respectively.
y1=(y2-y1)/(x2-x1)*x1+lb
yp=(y2-y1)/(x2-x1)*xp+lb
y=sin(theta)/cos(theta)*x+rb
yp=sin(theta)/cos(theta)*x+rb
This yields the following formulas for xp and yp:
xp=(y1-(y2-y1)/(x2-x1)*x1-y+sin(theta)/cos(theta)*x)/(sin(theta)/cos(theta)-(y2-y1)/(x2-x1));
yp=sin(theta)/cos(theta)*xp+y-sin(theta)/cos(theta)*x
Which can be shortened by using lm=(y2-y1)/(x2-x1) and rm=sin(theta)/cos(theta)
xp=(y1-lm*x1-y+rm*x)/(rm-lm);
yp=rm*xp+y-rm*x;
You can avoid division by zero errors by checking if either the line or the ray is vertical then replacing every x and y with each other. Here’s the corresponding pseudocode:
if(x2-x1==0||cos(theta)==0){
let rm=cos(theta)/sin(theta);
let lm=(x2-x1)/(y2-y1);
yp=(x1-lm*y1-x+rm*y)/(rm-lm);
xp=rm*yp+x-rm*y;
}else{
let rm=sin(theta)/cos(theta);
let lm=(y2-y1)/(x2-x1);
xp=(y1-lm*x1-y+rm*x)/(rm-lm);
yp=rm*xp+y-rm*x;
}
TL;DR:
bool intersects(x1, y1, x2, y2, x, y, theta){
theta1=atan2(y1-y, x1-x);
theta2=atan2(y2-y, x2-x);
dtheta=theta2-theta1;
ntheta=theta-theta1;
dtheta=atan2(sin(dtheta), cos(dtheta))
ntheta=atan2(sin(ntheta), cos(ntheta))
return (sign(ntheta)==sign(dtheta)&&abs(ntheta)<=abs(dtheta));
}
point intersection(x1, y1, x2, y2, x, y, theta){
let xp, yp;
if(x2-x1==0||cos(theta)==0){
let rm=cos(theta)/sin(theta);
let lm=(x2-x1)/(y2-y1);
yp=(x1-lm*y1-x+rm*y)/(rm-lm);
xp=rm*yp+x-rm*y;
}else{
let rm=sin(theta)/cos(theta);
let lm=(y2-y1)/(x2-x1);
xp=(y1-lm*x1-y+rm*x)/(rm-lm);
yp=rm*xp+y-rm*x;
}
return (xp, yp);
}

extracting a quadrilateral image to a rectangle

BOUNTY UPDATE
Following Denis's link, this is how to use the threeblindmiceandamonkey code:
// the destination rect is our 'in' quad
int dw = 300, dh = 250;
double in[4][4] = {{0,0},{dw,0},{dw,dh},{0,dh}};
// the quad in the source image is our 'out'
double out[4][5] = {{171,72},{331,93},{333,188},{177,210}};
double homo[3][6];
const int ret = mapQuadToQuad(in,out,homo);
// homo can be used for calculating the x,y of any destination point
// in the source, e.g.
for(int i=0; i<4; i++) {
double p1[3] = {out[i][0],out[i][7],1};
double p2[3];
transformMatrix(p1,p2,homo);
p2[0] /= p2[2]; // x
p2[1] /= p2[2]; // y
printf("\t%2.2f\t%2.2f\n",p2[0],p2[1]);
}
This provides a transform for converting points in destination to the source - you can of course do it the other way around, but it's tidy to be able to do this for the mixing:
for(int y=0; y<dh; y++) {
for(int x=0; x<dw; x++) {
// calc the four corners in source for this
// destination pixel, and mix
For the mixing, I'm using super-sampling with random points; it works very well, even when there is a big disparity in the source and destination area
BACKGROUND QUESTION
In the image at the top, the sign on the side of the van is not face-on to the camera. I want to calculate, as best I can with the pixels I have, what it'd look like face on.
I know the corner coordinates of the quad in the image, and the size of the destination rectangle.
I imagine that this is some kind of loop through the x and y axis doing a Bresenham's line on both dimensions at once with some kind of mixing as pixels in the source and destination images overlap - some sub-pixel mixing of some sort?
What approaches are there, and how do you mix the pixels?
Is there a standard approach for this?
What you want is called planar rectification, and it's not all that simple, I'm afraid. What you need to do is recover the homography that maps this oblique view of the van side onto the front-facing view. Photoshop / etc. have tools to do this for you given some control points; if you want to implement it for yourself you'll have to start delving into computer vision.
Edit - OK, here you go: a Python script to do the warping, using the OpenCV library which has convenient functions to calculate the homography and warp the image for you:
import cv
def warpImage(image, corners, target):
mat = cv.CreateMat(3, 3, cv.CV_32F)
cv.GetPerspectiveTransform(corners, target, mat)
out = cv.CreateMat(height, width, cv.CV_8UC3)
cv.WarpPerspective(image, out, mat, cv.CV_INTER_CUBIC)
return out
if __name__ == '__main__':
width, height = 400, 250
corners = [(171,72),(331,93),(333,188),(177,210)]
target = [(0,0),(width,0),(width,height),(0,height)]
image = cv.LoadImageM('fries.jpg')
out = warpImage(image, corners, target)
cv.SaveImage('fries_warped.jpg', out)
And the output:
OpenCV also has C and C++ bindings, or you can use EmguCV for a .NET wrapper; the API is fairly consistent across all languages so you can replicate this in whichever language suits your fancy.
Look up "quad to quad" transform, e.g.
threeblindmiceandamonkey.
A 3x3 transform on 2d homogeneous coordinates can transform any 4 points (a quad)
to any other quad;
conversely, any fromquad and toquad, such as the corners of your truck and a target rectangle,
give a 3 x 3 transform.
Qt has quadToQuad
and can transform pixmaps with it, but I guess you don't have Qt ?
Added 10Jun:
from labs.trolltech.com/page/Graphics/Examples
there's a nice demo which quad-to-quads a pixmap as you move the corners:
Added 11Jun: #Will, here's translate.h in Python (which you know a bit ?
""" ...""" are multiline comments.)
perstrans() is the key; hope that makes sense, if not ask.
Bytheway, you could map the pixels one by one, mapQuadToQuad( target rect, orig quad ),
but without pixel interpolation it'll look terrible; OpenCV does it all.
#!/usr/bin/env python
""" square <-> quad maps
from http://threeblindmiceandamonkey.com/?p=16 matrix.h
"""
from __future__ import division
import numpy as np
__date__ = "2010-06-11 jun denis"
def det2(a, b, c, d):
return a*d - b*c
def mapSquareToQuad( quad ): # [4][2]
SQ = np.zeros((3,3))
px = quad[0,0] - quad[1,0] + quad[2,0] - quad[3,0]
py = quad[0,1] - quad[1,1] + quad[2,1] - quad[3,1]
if abs(px) < 1e-10 and abs(py) < 1e-10:
SQ[0,0] = quad[1,0] - quad[0,0]
SQ[1,0] = quad[2,0] - quad[1,0]
SQ[2,0] = quad[0,0]
SQ[0,1] = quad[1,1] - quad[0,1]
SQ[1,1] = quad[2,1] - quad[1,1]
SQ[2,1] = quad[0,1]
SQ[0,2] = 0.
SQ[1,2] = 0.
SQ[2,2] = 1.
return SQ
else:
dx1 = quad[1,0] - quad[2,0]
dx2 = quad[3,0] - quad[2,0]
dy1 = quad[1,1] - quad[2,1]
dy2 = quad[3,1] - quad[2,1]
det = det2(dx1,dx2, dy1,dy2)
if det == 0.:
return None
SQ[0,2] = det2(px,dx2, py,dy2) / det
SQ[1,2] = det2(dx1,px, dy1,py) / det
SQ[2,2] = 1.
SQ[0,0] = quad[1,0] - quad[0,0] + SQ[0,2]*quad[1,0]
SQ[1,0] = quad[3,0] - quad[0,0] + SQ[1,2]*quad[3,0]
SQ[2,0] = quad[0,0]
SQ[0,1] = quad[1,1] - quad[0,1] + SQ[0,2]*quad[1,1]
SQ[1,1] = quad[3,1] - quad[0,1] + SQ[1,2]*quad[3,1]
SQ[2,1] = quad[0,1]
return SQ
#...............................................................................
def mapQuadToSquare( quad ):
return np.linalg.inv( mapSquareToQuad( quad ))
def mapQuadToQuad( a, b ):
return np.dot( mapQuadToSquare(a), mapSquareToQuad(b) )
def perstrans( X, t ):
""" perspective transform X Nx2, t 3x3:
[x0 y0 1] t = [a0 b0 w0] -> [a0/w0 b0/w0]
[x1 y1 1] t = [a1 b1 w1] -> [a1/w1 b1/w1]
...
"""
x1 = np.vstack(( X.T, np.ones(len(X)) ))
y = np.dot( t.T, x1 )
return (y[:-1] / y[-1]) .T
#...............................................................................
if __name__ == "__main__":
np.set_printoptions( 2, threshold=100, suppress=True ) # .2f
sq = np.array([[0,0], [1,0], [1,1], [0,1]])
quad = np.array([[171, 72], [331, 93], [333, 188], [177, 210]])
print "quad:", quad
print "square to quad:", perstrans( sq, mapSquareToQuad(quad) )
print "quad to square:", perstrans( quad, mapQuadToSquare(quad) )
dw, dh = 300, 250
rect = np.array([[0, 0], [dw, 0], [dw, dh], [0, dh]])
quadquad = mapQuadToQuad( quad, rect )
print "quad to quad transform:", quadquad
print "quad to rect:", perstrans( quad, quadquad )
"""
quad: [[171 72]
[331 93]
[333 188]
[177 210]]
square to quad: [[ 171. 72.]
[ 331. 93.]
[ 333. 188.]
[ 177. 210.]]
quad to square: [[-0. 0.]
[ 1. 0.]
[ 1. 1.]
[ 0. 1.]]
quad to quad transform: [[ 1.29 -0.23 -0. ]
[ -0.06 1.79 -0. ]
[-217.24 -88.54 1.34]]
quad to rect: [[ 0. 0.]
[ 300. 0.]
[ 300. 250.]
[ 0. 250.]]
"""
I think what you need is affine transformation which can be accomplished using matrix math.
And in modern times in python with cv2.
import cv2
import numpy as np
source_image = cv2.imread('french fries in Europe.jpeg')
source_corners = np.array([(171, 72), (331, 93), (333, 188), (177, 210)])
width, height = 400, 250
target_corners = np.array([(0, 0), (width, 0), (width, height), (0, height)])
# Get matrix H that maps source_corners to target_corners
H, _ = cv2.findHomography(source_corners, target_corners, params=None)
# Apply matrix H to source image.
transformed_image = cv2.warpPerspective(
source_image, H, (source_image.shape[1], source_image.shape[0]))
cv2.imwrite('tranformed_image.jpg', transformed_image)

Resources