How to use Bresenham's line drawing algorithm with clipping? - algorithm

When drawing a line with Bresenham line drawing algorithm,
where the line may not be within the bounds of the bitmap being written to - it would be useful to clip the results so they fit within the axis aligned bounds of the image being written to.
While its possible to first clip the line to the rectangle, then draw the line. This isn't ideal since it often gives a slightly different slant to the line (assuming int coords are being used).
Since this is such a primitive operation, are there established methods for clipping the line while maintaining the same shape?
In case it helps, here is a reference implementation of the algorithm - it uses int coords, which avoids int/float conversion while drawing the line.
I spent some time looking into this:
Problem is described in detail on virtual-dub's web page.
Possible solution from Alan Tiedemann... though I'd need to implement code based on the text - to see how well it works.
Bresenham's Line Generation Algorithm with Built-in Clipping(paper from 1995, couldn't find the entire document? - PDF is a single page without the C code it references, looks like its pay-walled).

Let's reframe the problem so we can see how Bresenham's algorithm really works...
Lets say you are drawing a mostly horizontal line (the method is the same for mostly vertical, but with the axes switched) from (x0,y0) to (x1,y1):
The full line can be described as a function of y in terms of x (all integers):
y = y0 + round( (x-x0) * (y1-y0) / (x1-x0) )
This describes precisely each pixel you will paint when drawing the full line, and when you clip the line consistently, it still describes precisely each pixel you will paint -- you just apply it to a smaller range of x values.
We can evaluate this function using all integer math, by calculating the divisor and remainder separately. For x1 >= x0 and y1 >= y0 (do the normal transformations otherwise):
let dx = (x1-x0);
let dy = (y1-y0);
let remlimit = (dx+1)/2; //round up
rem = (x-x0) * dy % dx;
y = y0 + (x-x0) * dy / dx;
if (rem >= remlimit)
{
rem-=dx;
y+=1;
}
Bresenham's algorithm is a just a fast way to update the result of this formula incrementally as you update x.
Here's how we can make use of incremental updates to draw the portion of the very same line from x=xs to x=xe:
let dx = (x1-x0);
let dy = (y1-y0);
let remlimit = (dx+1)/2; //round up
x=xs;
rem = (x-x0) * dy % dx;
y = y0 + (x-x0) * dy / dx;
if (rem >= remlimit)
{
rem-=dx;
y+=1;
}
paint(x,y);
while(x < xe)
{
x+=1;
rem+=dy;
if (rem >= remlimit)
{
rem-=dx;
y+=1;
}
paint(x,y);
}
If you want do to your remainder comparisons against 0, you can just offset it at the beginning:
let dx = (x1-x0);
let dy = (y1-y0);
let remlimit = (dx+1)/2; //round up
x=xs;
rem = ( (x-x0) * dy % dx ) - remlimit;
y = y0 + (x-x0) * dy / dx;
if (rem >= 0)
{
rem-=dx;
y+=1;
}
paint(x,y);
while(x < xe)
{
x+=1;
rem+=dy;
if (rem >= 0)
{
rem-=dx;
y+=1;
}
paint(x,y);
}

Bresenham's algorithm can be used, taking clipping values into account, based on the paper by Kuzmin & Yevgeny P:
For completeness, here is a working version of the algorithm, a single Python function, though its only using integer arithmetic - so can be easily ported to other languages.
def plot_line_v2v2i(
p1, p2, callback,
clip_xmin, clip_ymin,
clip_xmax, clip_ymax,
):
x1, y1 = p1
x2, y2 = p2
del p1, p2
# Vertical line
if x1 == x2:
if x1 < clip_xmin or x1 > clip_xmax:
return
if y1 <= y2:
if y2 < clip_ymin or y1 > clip_ymax:
return
y1 = max(y1, clip_ymin)
y2 = min(y2, clip_ymax)
for y in range(y1, y2 + 1):
callback(x1, y)
else:
if y1 < clip_ymin or y2 > clip_ymax:
return
y2 = max(y2, clip_ymin)
y1 = min(y1, clip_ymax)
for y in range(y1, y2 - 1, -1):
callback(x1, y)
return
# Horizontal line
if y1 == y2:
if y1 < clip_ymin or y1 > clip_ymax:
return
if x1 <= x2:
if x2 < clip_xmin or x1 > clip_xmax:
return
x1 = max(x1, clip_xmin)
x2 = min(x2, clip_xmax)
for x in range(x1, x2 + 1):
callback(x, y1)
else:
if x1 < clip_xmin or x2 > clip_xmax:
return
x2 = max(x2, clip_xmin)
x1 = min(x1, clip_xmax)
for x in range(x1, x2 - 1, -1):
callback(x, y1)
return
# Now simple cases are handled, perform clipping checks.
if x1 < x2:
if x1 > clip_xmax or x2 < clip_xmin:
return
sign_x = 1
else:
if x2 > clip_xmax or x1 < clip_xmin:
return
sign_x = -1
# Invert sign, invert again right before plotting.
x1 = -x1
x2 = -x2
clip_xmin, clip_xmax = -clip_xmax, -clip_xmin
if y1 < y2:
if y1 > clip_ymax or y2 < clip_ymin:
return
sign_y = 1
else:
if y2 > clip_ymax or y1 < clip_ymin:
return
sign_y = -1
# Invert sign, invert again right before plotting.
y1 = -y1
y2 = -y2
clip_ymin, clip_ymax = -clip_ymax, -clip_ymin
delta_x = x2 - x1
delta_y = y2 - y1
delta_x_step = 2 * delta_x
delta_y_step = 2 * delta_y
# Plotting values
x_pos = x1
y_pos = y1
if delta_x >= delta_y:
error = delta_y_step - delta_x
set_exit = False
# Line starts below the clip window.
if y1 < clip_ymin:
temp = (2 * (clip_ymin - y1) - 1) * delta_x
msd = temp // delta_y_step
x_pos += msd
# Line misses the clip window entirely.
if x_pos > clip_xmax:
return
# Line starts.
if x_pos >= clip_xmin:
rem = temp - msd * delta_y_step
y_pos = clip_ymin
error -= rem + delta_x
if rem > 0:
x_pos += 1
error += delta_y_step
set_exit = True
# Line starts left of the clip window.
if not set_exit and x1 < clip_xmin:
temp = delta_y_step * (clip_xmin - x1)
msd = temp // delta_x_step
y_pos += msd
rem = temp % delta_x_step
# Line misses clip window entirely.
if y_pos > clip_ymax or (y_pos == clip_ymax and rem >= delta_x):
return
x_pos = clip_xmin
error += rem
if rem >= delta_x:
y_pos += 1
error -= delta_x_step
x_pos_end = x2
if y2 > clip_ymax:
temp = delta_x_step * (clip_ymax - y1) + delta_x
msd = temp // delta_y_step
x_pos_end = x1 + msd
if (temp - msd * delta_y_step) == 0:
x_pos_end -= 1
x_pos_end = min(x_pos_end, clip_xmax) + 1
if sign_y == -1:
y_pos = -y_pos
if sign_x == -1:
x_pos = -x_pos
x_pos_end = -x_pos_end
delta_x_step -= delta_y_step
while x_pos != x_pos_end:
callback(x_pos, y_pos)
if error >= 0:
y_pos += sign_y
error -= delta_x_step
else:
error += delta_y_step
x_pos += sign_x
else:
# Line is steep '/' (delta_x < delta_y).
# Same as previous block of code with swapped x/y axis.
error = delta_x_step - delta_y
set_exit = False
# Line starts left of the clip window.
if x1 < clip_xmin:
temp = (2 * (clip_xmin - x1) - 1) * delta_y
msd = temp // delta_x_step
y_pos += msd
# Line misses the clip window entirely.
if y_pos > clip_ymax:
return
# Line starts.
if y_pos >= clip_ymin:
rem = temp - msd * delta_x_step
x_pos = clip_xmin
error -= rem + delta_y
if rem > 0:
y_pos += 1
error += delta_x_step
set_exit = True
# Line starts below the clip window.
if not set_exit and y1 < clip_ymin:
temp = delta_x_step * (clip_ymin - y1)
msd = temp // delta_y_step
x_pos += msd
rem = temp % delta_y_step
# Line misses clip window entirely.
if x_pos > clip_xmax or (x_pos == clip_xmax and rem >= delta_y):
return
y_pos = clip_ymin
error += rem
if rem >= delta_y:
x_pos += 1
error -= delta_y_step
y_pos_end = y2
if x2 > clip_xmax:
temp = delta_y_step * (clip_xmax - x1) + delta_y
msd = temp // delta_x_step
y_pos_end = y1 + msd
if (temp - msd * delta_x_step) == 0:
y_pos_end -= 1
y_pos_end = min(y_pos_end, clip_ymax) + 1
if sign_x == -1:
x_pos = -x_pos
if sign_y == -1:
y_pos = -y_pos
y_pos_end = -y_pos_end
delta_y_step -= delta_x_step
while y_pos != y_pos_end:
callback(x_pos, y_pos)
if error >= 0:
x_pos += sign_x
error -= delta_y_step
else:
error += delta_x_step
y_pos += sign_y
Example use:
plot_line_v2v2i(
# two points
(10, 2),
(90, 88),
# callback
lambda x, y: print(x, y),
# xy min
25, 25,
# xy max
75, 75,
)
Notes:
Clipping min/max values are inclusive(so max values should be image_width - 1, image_height - 1)
Integer divisions // is used everywhere.
Many languages (C/C++ for example) use floored rounding on division.See Fast floor of a signed integer division in C / C++ to avoid having slightly biased results with those languages.
There are some improvements over the code provided in the paper:
The line will always plot in the direction defined (from p1 to p2).
There was sometimes a subtle difference in the line gradient, so that rotating of flipping the points, calculating the line, then transforming back - would give slightly different results. The asymmetry was caused by the code swapping the X and Y axis to avoid code duplication.
For tests and more example usage, see:
The Python repository.
Rust version.

Related

How to use a line draiwng algorithm with min/max? F#

Sorry if this question is not suited for this site, but I don't know where else too ask it. I'm pretty confused, I use min and max I believe correctly. When I move the weapon with arrow keys the line seem to works find when its moving right or down. If anybody knows how to fix it or just put me on the right track I'd greatly appreciate it. For anyone interested I am creating this program for a game which I need a line of sight in for the weapon.
EDIT:
I am trying to achieve a state where I can use w,a,s,d keys to move the C (character) and arrow keys to move W (weapon). Eventually when I add enemies the line between the character and weapon will be used to see if they are in range to be attacked. Like a gun shooting in any direction. But when I move C in it's current state the line does not connect to C anymore. I'm not sure why this is.
open System
let [<Literal>] CharacterN = ConsoleKey.W
let [<Literal>] CharacterE = ConsoleKey.D
let [<Literal>] CharacterS = ConsoleKey.S
let [<Literal>] CharacterW = ConsoleKey.A
let [<Literal>] WeaponN = ConsoleKey.UpArrow
let [<Literal>] WeaponE = ConsoleKey.RightArrow
let [<Literal>] WeaponS = ConsoleKey.DownArrow
let [<Literal>] WeaponW = ConsoleKey.LeftArrow
type Point =
{ X : int
Y : int }
let p1 = { X = 0; Y = 0 }
let p2 = { X = 10; Y = 10 }
let rec main p1 p2 =
Console.Clear()
let dx = min p1.X p2.X - max p1.X p2.X
let dy = min p1.Y p2.Y - max p1.Y p2.Y
for x in min p1.X p2.X .. max p1.X p2.X do
let y = min p1.X p2.X + dy * (x - min p1.X p2.X) / dx
Console.SetCursorPosition(x, y)
printf "."
Console.SetCursorPosition(p1.X, p1.Y)
printf "C"
Console.SetCursorPosition(p2.X, p2.Y)
printf "W"
match Console.ReadKey().Key with
| CharacterN -> main { X = p1.X; Y = p1.Y - 1 } p2
| CharacterE -> main { X = p1.X + 1; Y = p1.Y } p2
| CharacterS -> main { X = p1.X; Y = p1.Y + 1 } p2
| CharacterW -> main { X = p1.X - 1; Y = p1.Y } p2
| WeaponN -> main p1 { X = p2.X; Y = p2.Y - 1 }
| WeaponE -> main p1 { X = p2.X + 1; Y = p2.Y }
| WeaponS -> main p1 { X = p2.X; Y = p2.Y + 1 }
| WeaponW -> main p1 { X = p2.X - 1; Y = p2.Y }
| _ -> ()
main p1 p2
Console.Read() |> ignore
I believe that are several issues with your code. Probably the most important one is in the logic of the line:
let y = min p1.X p2.X + dy * (x - min p1.X p2.X) / dx
Obviously it should have been something like
y = y0 + (x-x0)*dy/dx
i.e. the first term shuold be something about Y rather than X. Unfortunately with your looping logic it should be the Y of the point that has the smaller X. It is not that easy to say that. IMHO it is easier to fix looping using negative step.
Another naive assumption is that you can always draw a line as having some y for each x. This is obviously not so when dy > dx. Moreover in case of a vertical line when dx is 0, the code will fail. The most most popular Bresenham's line algorithm requires you to handle those cases as explicitly different. Here is a simple implementation that handle those cases:
let drawLine p1 p2 =
let dx = p2.X - p1.X
let dy = p2.Y - p1.Y
if((dx <> 0) || (dy <> 0)) then
if abs(dx) >= abs(dy) then
let step = if (p1.X < p2.X) then 1 else -1
for x in p1.X ..step.. p2.X do
let y = p1.Y + dy * (x - p1.X) / dx
Console.SetCursorPosition(x, y)
printf "."
else
let step = if (p1.Y < p2.Y) then 1 else -1
for y in p1.Y .. step .. p2.Y do
let x = p1.X + dx * (y - p1.Y) / dy
Console.SetCursorPosition(x, y)
printf "."
As for other issues, you probably want to restrict positions of your points to some visible area between 0 and some max value. SetCursorPosition call with a negative value or a value larger than the buffer size will crash your app.

MATLAB: Efficient way to find the feasible region of first-order polynomials x, y with unknown coefficents in [-1, 1]

As in the title, in MATLAB, I need the feasible region (bounds of all feasible solutions) of
x_0 + x_1 e_1 + ... + x_n e_n
and
y_0 + y_1 e_1 + ... + y_n e_n
where all unknown e_i are in the interval [-1, 1]. I would prefer the solution to not depend on non-standard 3rd party functions.
Below is my quick-and-dirty attempt, but the complexity grows O(2^n), where n is the number of e_i. Any thoughts?
x0 = 3;
x = [1; -3; 0];
y0 = -1;
y = [3; -2; 4];
% Get all permutations of noise symbol extremities
terms = size(x, 1);
xx = zeros(2^terms, 1);
yy = zeros(2^terms, 1);
for j = 1:2^terms
e = double(bitget(j - 1, 1:terms))';
e(e == 0) = -1;
xx(j) = x0 + sum(x .* e);
yy(j) = y0 + sum(y .* e);
end
k = convhull(xx, yy);
plot(xx(k), yy(k));
% First generate all possible permutations for [-1, 1] for n terms. This is similar to what you have done but uses a matlab function
all_e = de2bi(0:(2^terms-1), terms).';
all_e(all_e == 0) = -1;
% Multiply corresponding values of x and y with those of e
xx = x0 + arrayfun(#(z) sum(x .* all_e(:, z)), 1:(2^terms));
yy = x0 + arrayfun(#(z) sum(y .* all_e(:, z)), 1:(2^terms));
You can read more about the function de2bi here
A method to find the absolute minimum and maximum bounds is as follows:
max_e = double(x >= 0);
min_e = double(~max_e);
max_e(max_e == 0) = -1;
min_e(min_e == 0) = -1;
absMax = x0 + sum(x .* max_e);
absMin = x0 + sum(x .* min_e);
Similarly you could do for y

Rotate an image with bicubic interpolation without imrotate

I have implemented a code for image warping using bilinear interpolation:
Matlab image rotation
I would like to improve the code by using bicubic interpolation to rotate the image WITHOUT using the built-in functions like imrotate or imwarp and interp functions in MATLAB.
I successfully managed to implement a full working example.
Code is based on Anna1994's code: Matlab image rotation
Biqubic code is also based on Java (and C++) implementation posted here: http://www.paulinternet.nl/?page=bicubic
The following code applies image rotation example using biqubic interpolation:
function BicubicInterpolationTest()
close all;
% clear all;
img = 'cameraman.tif';
input_image =double(imread(img))./255;
H=size(input_image,1); % height
W=size(input_image,2); % width
th=120*pi/180; %Rotate 120 degrees
s0 = 2;
s1 = 2;
x0 = -W/2;
x1 = -H/2;
T=[1 0 x0 ; ...
0 1 x1 ; ...
0 0 1];
RST = [ (s0*cos(th)) (-s1*sin(th)) ((s0*x0*cos(th))-(s1*x1*sin(th))); ...
(s0*sin(th)) (s1*cos(th)) ((s0*x0*sin(th))+(s1*x1*cos(th))); ...
0 0 1];
M=inv(T)*RST;
N = inv(M);
output_image=zeros(H,W,size(input_image,3));
for i=1:W
for j=1:H
x = [i ; j ; 1];
y = N * x;
a = y(1)/y(3);
b = y(2)/y(3);
%Nearest neighbor
% a = round(a);
% b = round(b);
%Bilinear interpolation (applies RGB image):
% x1 = floor(a);
% y1 = floor(b);
% x2 = x1 + 1;
% y2 = y1 + 1;
% if ((x1 >= 1) && (y1 >= 1) && (x2 <= W) && (y2 <= H))
% %Load 2x2 pixels
% i11 = input_image(y1, x1, :); %Top left pixel
% i21 = input_image(y2, x1, :); %Bottom left pixel
% i12 = input_image(y1, x2, :); %Top right pixel
% i22 = input_image(y2, x2, :); %Bottom right pixel
%
% %Interpolation wieghts
% dx = x2 - a;
% dy = y2 - b;
%
% %Bi-lienar interpolation
% output_image(j, i, :) = i11*dx*dy + i21*dx*(1-dy) + i12*(1-dx)*dy + i22*(1-dx)*(1-dy);
% end
x1 = floor(a);
y1 = floor(b);
%Bicubic interpolation (applies grayscale image)
if ((x1 >= 2) && (y1 >= 2) && (x1 <= W-2) && (y1 <= H-2))
%Load 4x4 pixels
P = input_image(y1-1:y1+2, x1-1:x1+2);
%Interpolation wieghts
dx = a - x1;
dy = b - y1;
%Bi-bicubic interpolation
output_image(j, i) = bicubicInterpolate(P, dx, dy);
end
end
end
imshow(output_image);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Verify implementation by comparing with Matalb build in function imwarp:
tform = affine2d(M');
ref_image = imwarp(input_image, tform, 'OutputView', imref2d(size(input_image)), 'Interp', 'cubic');
figure;imshow(ref_image)
figure;imshow(output_image - ref_image)
max_diff = max(abs(output_image(:) - ref_image(:)));
disp(['Maximum difference from imwarp = ', num2str(max_diff)]);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%http://www.paulinternet.nl/?page=bicubic
%double cubicInterpolate (double p[4], double x) {
% return p[1] + 0.5 * x*(p[2] - p[0] + x*(2.0*p[0] - 5.0*p[1] + 4.0*p[2] - p[3] + x*(3.0*(p[1] - p[2]) + p[3] - p[0])));
%}
function q = cubicInterpolate(p, x)
q = p(2) + 0.5 * x*(p(3) - p(1) + x*(2.0*p(1) - 5.0*p(2) + 4.0*p(3) - p(4) + x*(3.0*(p(2) - p(3)) + p(4) - p(1))));
%http://www.paulinternet.nl/?page=bicubic
% double bicubicInterpolate (double p[4][4], double x, double y) {
% double arr[4];
% arr[0] = cubicInterpolate(p[0], y);
% arr[1] = cubicInterpolate(p[1], y);
% arr[2] = cubicInterpolate(p[2], y);
% arr[3] = cubicInterpolate(p[3], y);
% return cubicInterpolate(arr, x);
% }
function q = bicubicInterpolate(p, x, y)
q1 = cubicInterpolate(p(1,:), x);
q2 = cubicInterpolate(p(2,:), x);
q3 = cubicInterpolate(p(3,:), x);
q4 = cubicInterpolate(p(4,:), x);
q = cubicInterpolate([q1, q2, q3, q4], y);
I verified implementation by comparing to Matalb build in function imwarp
Result:
The following example uses the "CachedBicubicInterpolator" code version, and also supports RGB image:
function BicubicInterpolationTest2()
close all;
% clear all;
img = 'peppers.png';
input_image = double(imread(img))./255;
H=size(input_image,1); % height
W=size(input_image,2); % width
th=120*pi/180; %Rotate 120 degrees
s0 = 0.8;
s1 = 0.8;
x0 = -W/2;
x1 = -H/2;
T=[1 0 x0 ; ...
0 1 x1 ; ...
0 0 1];
RST = [ (s0*cos(th)) (-s1*sin(th)) ((s0*x0*cos(th))-(s1*x1*sin(th))); ...
(s0*sin(th)) (s1*cos(th)) ((s0*x0*sin(th))+(s1*x1*cos(th))); ...
0 0 1];
M=inv(T)*RST;
N = inv(M);
output_image=zeros(H,W,size(input_image,3));
for i=1:W
for j=1:H
x = [i ; j ; 1];
y = N * x;
a = y(1)/y(3);
b = y(2)/y(3);
x1 = floor(a);
y1 = floor(b);
%Bicubic interpolation (applies grayscale image)
if ((x1 >= 2) && (y1 >= 2) && (x1 <= W-2) && (y1 <= H-2))
%Load 4x4 pixels
P = input_image(y1-1:y1+2, x1-1:x1+2, :);
%Interpolation wieghts
dx = a - x1;
dy = b - y1;
%Bi-bicubic interpolation
output_image(j, i, :) = bicubicInterpolate(P, dx, dy);
end
end
end
imshow(output_image);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Verify implementation by comparing with Matalb build in function imwarp:
tform = affine2d(M');
ref_image = imwarp(input_image, tform, 'OutputView', imref2d(size(input_image)), 'Interp', 'cubic');
figure;imshow(ref_image)
figure;imshow(abs(output_image - ref_image), []);impixelinfo
max_diff = max(abs(output_image(:) - ref_image(:)));
disp(['Maximum difference from imwarp = ', num2str(max_diff)]);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [p0, p1, p2, p3] = list4(P)
P = squeeze(P);
p0 = P(1, :);
p1 = P(2, :);
p2 = P(3, :);
p3 = P(4, :);
%http://www.paulinternet.nl/?page=bicubic
% public void updateCoefficients (double[][] p) {
% a00 = p[1][1];
% a01 = -.5*p[1][0] + .5*p[1][2];
% a02 = p[1][0] - 2.5*p[1][1] + 2*p[1][2] - .5*p[1][3];
% a03 = -.5*p[1][0] + 1.5*p[1][1] - 1.5*p[1][2] + .5*p[1][3];
% a10 = -.5*p[0][1] + .5*p[2][1];
% a11 = .25*p[0][0] - .25*p[0][2] - .25*p[2][0] + .25*p[2][2];
% a12 = -.5*p[0][0] + 1.25*p[0][1] - p[0][2] + .25*p[0][3] + .5*p[2][0] - 1.25*p[2][1] + p[2][2] - .25*p[2][3];
% a13 = .25*p[0][0] - .75*p[0][1] + .75*p[0][2] - .25*p[0][3] - .25*p[2][0] + .75*p[2][1] - .75*p[2][2] + .25*p[2][3];
% a20 = p[0][1] - 2.5*p[1][1] + 2*p[2][1] - .5*p[3][1];
% a21 = -.5*p[0][0] + .5*p[0][2] + 1.25*p[1][0] - 1.25*p[1][2] - p[2][0] + p[2][2] + .25*p[3][0] - .25*p[3][2];
% a22 = p[0][0] - 2.5*p[0][1] + 2*p[0][2] - .5*p[0][3] - 2.5*p[1][0] + 6.25*p[1][1] - 5*p[1][2] + 1.25*p[1][3] + 2*p[2][0] - 5*p[2][1] + 4*p[2][2] - p[2][3] - .5*p[3][0] + 1.25*p[3][1] - p[3][2] + .25*p[3][3];
% a23 = -.5*p[0][0] + 1.5*p[0][1] - 1.5*p[0][2] + .5*p[0][3] + 1.25*p[1][0] - 3.75*p[1][1] + 3.75*p[1][2] - 1.25*p[1][3] - p[2][0] + 3*p[2][1] - 3*p[2][2] + p[2][3] + .25*p[3][0] - .75*p[3][1] + .75*p[3][2] - .25*p[3][3];
% a30 = -.5*p[0][1] + 1.5*p[1][1] - 1.5*p[2][1] + .5*p[3][1];
% a31 = .25*p[0][0] - .25*p[0][2] - .75*p[1][0] + .75*p[1][2] + .75*p[2][0] - .75*p[2][2] - .25*p[3][0] + .25*p[3][2];
% a32 = -.5*p[0][0] + 1.25*p[0][1] - p[0][2] + .25*p[0][3] + 1.5*p[1][0] - 3.75*p[1][1] + 3*p[1][2] - .75*p[1][3] - 1.5*p[2][0] + 3.75*p[2][1] - 3*p[2][2] + .75*p[2][3] + .5*p[3][0] - 1.25*p[3][1] + p[3][2] - .25*p[3][3];
% a33 = .25*p[0][0] - .75*p[0][1] + .75*p[0][2] - .25*p[0][3] - .75*p[1][0] + 2.25*p[1][1] - 2.25*p[1][2] + .75*p[1][3] + .75*p[2][0] - 2.25*p[2][1] + 2.25*p[2][2] - .75*p[2][3] - .25*p[3][0] + .75*p[3][1] - .75*p[3][2] + .25*p[3][3];
% }
% public double getValue (double x, double y) {
% double x2 = x * x;
% double x3 = x2 * x;
% double y2 = y * y;
% double y3 = y2 * y;
%
% return (a00 + a01 * y + a02 * y2 + a03 * y3) +
% (a10 + a11 * y + a12 * y2 + a13 * y3) * x +
% (a20 + a21 * y + a22 * y2 + a23 * y3) * x2 +
% (a30 + a31 * y + a32 * y2 + a33 * y3) * x3;
% }
function q = bicubicInterpolate(P, x, y)
[p00, p01, p02, p03] = list4(P(1, :, :));
[p10, p11, p12, p13] = list4(P(2, :, :));
[p20, p21, p22, p23] = list4(P(3, :, :));
[p30, p31, p32, p33] = list4(P(4, :, :));
a00 = p11;
a01 = -.5*p10 + .5*p12;
a02 = p10 - 2.5*p11 + 2*p12 - .5*p13;
a03 = -.5*p10 + 1.5*p11 - 1.5*p12 + .5*p13;
a10 = -.5*p01 + .5*p21;
a11 = .25*p00 - .25*p02 - .25*p20 + .25*p22;
a12 = -.5*p00 + 1.25*p01 - p02 + .25*p03 + .5*p20 - 1.25*p21 + p22 - .25*p23;
a13 = .25*p00 - .75*p01 + .75*p02 - .25*p03 - .25*p20 + .75*p21 - .75*p22 + .25*p23;
a20 = p01 - 2.5*p11 + 2*p21 - .5*p31;
a21 = -.5*p00 + .5*p02 + 1.25*p10 - 1.25*p12 - p20 + p22 + .25*p30 - .25*p32;
a22 = p00 - 2.5*p01 + 2*p02 - .5*p03 - 2.5*p10 + 6.25*p11 - 5*p12 + 1.25*p13 + 2*p20 - 5*p21 + 4*p22 - p23 - .5*p30 + 1.25*p31 - p32 + .25*p33;
a23 = -.5*p00 + 1.5*p01 - 1.5*p02 + .5*p03 + 1.25*p10 - 3.75*p11 + 3.75*p12 - 1.25*p13 - p20 + 3*p21 - 3*p22 + p23 + .25*p30 - .75*p31 + .75*p32 - .25*p33;
a30 = -.5*p01 + 1.5*p11 - 1.5*p21 + .5*p31;
a31 = .25*p00 - .25*p02 - .75*p10 + .75*p12 + .75*p20 - .75*p22 - .25*p30 + .25*p32;
a32 = -.5*p00 + 1.25*p01 - p02 + .25*p03 + 1.5*p10 - 3.75*p11 + 3*p12 - .75*p13 - 1.5*p20 + 3.75*p21 - 3*p22 + .75*p23 + .5*p30 - 1.25*p31 + p32 - .25*p33;
a33 = .25*p00 - .75*p01 + .75*p02 - .25*p03 - .75*p10 + 2.25*p11 - 2.25*p12 + .75*p13 + .75*p20 - 2.25*p21 + 2.25*p22 - .75*p23 - .25*p30 + .75*p31 - .75*p32 + .25*p33;
x2 = x * x;
x3 = x2 * x;
y2 = y * y;
y3 = y2 * y;
% q = (a00 + a01 * y + a02 * y2 + a03 * y3) +...
% (a10 + a11 * y + a12 * y2 + a13 * y3) * x +...
% (a20 + a21 * y + a22 * y2 + a23 * y3) * x2 +...
% (a30 + a31 * y + a32 * y2 + a33 * y3) * x3;
q = (a00 + a01 * x + a02 * x2 + a03 * x3) +...
(a10 + a11 * x + a12 * x2 + a13 * x3) * y +...
(a20 + a21 * x + a22 * x2 + a23 * x3) * y2 +...
(a30 + a31 * x + a32 * x2 + a33 * x3) * y3;
Result:

Matlab image rotation

I am new to image processing, I have implemented a code for image warping and it works perfectly. I would like to improve the code by using linear interpolation to rotate the image WITHOUT using the built-in function (interp). Here is my code:
close all;
clear all;
img = 'woods.jpg';
input_image =double(imread(img))./255;
H=size(input_image,1);
W=size(input_image,2);
th=pi/4;
s0 = 2;
s1 = 2;
x0 = -W/2;
x1 = -H/2;
T=[1 0 x0 ; ...
0 1 x1 ; ...
0 0 1];
RST = [ (s0*cos(th)) (-s1*sin(th)) ((s0*x0*cos(th))-(s1*x1*sin(th))); ...
(s0*sin(th)) (s1*cos(th)) ((s0*x0*sin(th))+(s1*x1*cos(th))); ...
0 0 1];
M=inv(T)*R;
N = inv(M);
output_image=zeros(H,W,3);
for i=1:W
for j=1:H
x = [i ; j ; 1];
y = N * x;
a = y(1)/y(3);
b = y(2)/y(3);
a = round(a);
b = round(b);
if (a>0 && a<=W && b>0 && b<=H)
output_image(j,i,:)=input_image(b,a,:);
end
end
end
imgshow(output_image);
Check the following solution:
I verified implementation by comparing to Matalb build in function [imwarp][1].
close all;
clear all;
img = 'peppers.png';
input_image =double(imread(img))./255;
H=size(input_image,1); % height
W=size(input_image,2); % width
th=pi/4;
s0 = 2;
s1 = 2;
x0 = -W/2;
x1 = -H/2;
T=[1 0 x0 ; ...
0 1 x1 ; ...
0 0 1];
RST = [ (s0*cos(th)) (-s1*sin(th)) ((s0*x0*cos(th))-(s1*x1*sin(th))); ...
(s0*sin(th)) (s1*cos(th)) ((s0*x0*sin(th))+(s1*x1*cos(th))); ...
0 0 1];
M=inv(T)*RST;
N = inv(M);
output_image=zeros(H,W,3);
for i=1:W
for j=1:H
x = [i ; j ; 1];
y = N * x;
a = y(1)/y(3);
b = y(2)/y(3);
%Nearest neighbor
%a = round(a);
%b = round(b);
x1 = floor(a);
y1 = floor(b);
x2 = x1 + 1;
y2 = y1 + 1;
%Bi-linear interpolation ilsutration:
%Image coordinates style (horizontal index first)
%
%(x1,y1) | (x2,y1)
% | 1-dy
% 1-dx | dx
% ------(a,b)------------
% |
% |
% |
% | dy
% |
% |
%(x1,y2) | (x2,y2)
if ((x1 >= 1) && (y1 >= 1) && (x2 <= W) && (y2 <= H))
%Load 2x2 pixels
i11 = input_image(y1, x1, :); %Top left pixel
i21 = input_image(y2, x1, :); %Bottom left pixel
i12 = input_image(y1, x2, :); %Top right pixel
i22 = input_image(y2, x2, :); %Bottom right pixel
%Interpolation wieghts
dx = x2 - a;
dy = y2 - b;
%Bi-lienar interpolation
output_image(j, i, :) = i11*dx*dy + i21*dx*(1-dy) + i12*(1-dx)*dy + i22*(1-dx)*(1-dy);
end
end
end
imshow(output_image);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Verify implementation by comparing with Matalb build in function imwarp:
tform = affine2d(M');
ref_image = imwarp(input_image, tform, 'OutputView', imref2d(size(input_image)), 'Interp', 'linear');
figure;imshow(ref_image)
figure;imshow(output_image - ref_image)
max_diff = max(abs(output_image(:) - ref_image(:)));
disp(['Maximum difference from imwarp = ', num2str(max_diff)]);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Result:
Remark:
I missed the following execelnt post that resizes an image using bilinear interpolation (bilinear interpolation is explained better):
Resize an image with bilinear interpolation without imresize

How to check if a point lies on a line between 2 other points

How would I write this function? Any examples appreciated
function isPointBetweenPoints(currPoint, point1, point2):Boolean {
var currX = currPoint.x;
var currY = currPoint.y;
var p1X = point1.x;
var p1y = point1.y;
var p2X = point2.x;
var p2y = point2.y;
//here I'm stuck
}
Assuming that point1 and point2 are different, first you check whether the point lies on the line. For that you simply need a "cross-product" of vectors point1 -> currPoint and point1 -> point2.
dxc = currPoint.x - point1.x;
dyc = currPoint.y - point1.y;
dxl = point2.x - point1.x;
dyl = point2.y - point1.y;
cross = dxc * dyl - dyc * dxl;
Your point lies on the line if and only if cross is equal to zero.
if (cross != 0)
return false;
Now, as you know that the point does lie on the line, it is time to check whether it lies between the original points. This can be easily done by comparing the x coordinates, if the line is "more horizontal than vertical", or y coordinates otherwise
if (abs(dxl) >= abs(dyl))
return dxl > 0 ?
point1.x <= currPoint.x && currPoint.x <= point2.x :
point2.x <= currPoint.x && currPoint.x <= point1.x;
else
return dyl > 0 ?
point1.y <= currPoint.y && currPoint.y <= point2.y :
point2.y <= currPoint.y && currPoint.y <= point1.y;
Note that the above algorithm if entirely integral if the input data is integral, i.e. it requires no floating-point calculations for integer input. Beware of potential overflow when calculating cross though.
P.S. This algorithm is absolutely precise, meaning that it will reject points that lie very close to the line but not precisely on the line. Sometimes this is not what's needed. But that's a different story.
Distance(point1, currPoint)
+ Distance(currPoint, point2)
== Distance(point1, point2)
But be careful if you have floating point values, things are different for them...
When concerned about the computational cost of computing "the square roots", don't:
Just compare "the squares".
This is independent of Javascript. Try the following algorithm, with points p1=point1 and p2=point2, and your third point being p3=currPoint:
v1 = p2 - p1
v2 = p3 - p1
v3 = p3 - p2
if (dot(v2,v1)>0 and dot(v3,v1)<0) return between
else return not between
If you want to be sure it's on the line segment between p1 and p2 as well:
v1 = normalize(p2 - p1)
v2 = normalize(p3 - p1)
v3 = p3 - p2
if (fabs(dot(v2,v1)-1.0)<EPS and dot(v3,v1)<0) return between
else return not between
You want to check whether the slope from point1 to currPoint is the same as the slope from currPoint to point2, so:
m1 = (currY - p1Y) / (currX - p1X);
m2 = (p2Y - currY) / (p2X - currX);
You also want to check whether currPoint is inside the box created by the other two, so:
return (m1 == m2) && (p1Y <= currY && currY <= p2Y) && (p1X <= currX && currX <= p2X);
Edit: This is not a very good method; look at maxim1000's solution for a much more correct way.
I'll use Triangle approach:
First, I'll check the Area, if the Area is close to 0, then the Point lies on the Line.
But think about the case where the length of AC is so great, then the Area increases far from 0, but visually, we still see that B is on AC: that when we need to check the height of the triangle.
To do this, we need to remember the formula we learn from first grade: Area = Base * Height / 2
Here is the code:
bool Is3PointOn1Line(IList<Vector2> arrVert, int idx1, int idx2, int idx3)
{
//check if the area of the ABC triangle is 0:
float fArea = arrVert[idx1].x * (arrVert[idx2].y - arrVert[idx3].y) +
arrVert[idx2].x * (arrVert[idx3].y - arrVert[idx1].y) +
arrVert[idx3].x * (arrVert[idx1].y - arrVert[idx2].y);
fArea = Mathf.Abs(fArea);
if (fArea < SS.EPSILON)
{
//Area is zero then it's the line
return true;
}
else
{
//Check the height, in case the triangle has long base
float fBase = Vector2.Distance(arrVert[idx1], arrVert[idx3]);
float height = 2.0f * fArea / fBase;
return height < SS.EPSILON;
}
}
Usage:
Vector2[] arrVert = new Vector2[3];
arrVert[0] = //...
arrVert[1] = //...
arrVert[2] = //...
if(Is3PointOn1Line(arrVert, 0, 1, 2))
{
//Ta-da, they're on same line
}
PS: SS.EPSILON = 0.01f and I use some function of Unity (for ex: Vector2.Distance), but you got the idea.
Ready for what seems to be infinitely simpler than some of these other solutions?
You pass it three points (three objects with an x and y property). Points 1 and 2 define your line, and point 3 is the point you are testing.
function pointOnLine(pt1, pt2, pt3) {
const dx = (pt3.x - pt1.x) / (pt2.x - pt1.x);
const dy = (pt3.y - pt1.y) / (pt2.y - pt1.y);
const onLine = dx === dy
// Check on or within x and y bounds
const betweenX = 0 <= dx && dx <= 1;
const betweenY = 0 <= dy && dy <= 1;
return onLine && betweenX && betweenY;
}
console.log('pointOnLine({x: 0, y: 0}, {x: 1, y: 1}, {x: 2, y: 2})');
console.log(pointOnLine({ x: 0, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 2 }));
console.log('pointOnLine({x: 0, y: 0}, {x: 1, y: 1}, {x: 0.5, y: 0.5})');
console.log(pointOnLine({ x: 0, y: 0 }, { x: 1, y: 1 }, { x: 0.5, y: 0.5 }));
Edit: Simplified further according to RBarryYoung's observation.
This approach is similar to Steve's approach, just shorter and improved to use as little memory and process power as possible. But first the mathematical idea:
Let a, b be the ends of the line, ab the difference between them and p the point to check. Then p is exactly then on the line, if
a + i * ab = p
with i being a number in the interval [0;1] representing the index on the line. We can write that as two separate equations (for 2D):
a.x + i * ab.x = p.x
a.y + i * ab.y = p.y
⇔
i = (p.x - a.x) / ab.x
i = (p.y - a.y) / ab.y
Which gives us to requirements for p to be on the line from a to b:
(p.x - a.x) / ab.x = (p.y - a.y) / ab.y
and
0 ≤ i ≤ 1
In code:
function onLine(a, b, p) {
var i1 = (p.x - a.x) / (b.x - a.x), i2 = (p.y - a.y) / (b.y - a.y);
return i1 == i2 && i1 <= 0 && i1 >= 1;
}
Technically you could even inline i2 but that makes it even harder to read.
private static boolean pointInLine(int2 lineBegin, int2 lineEnd, int2 point)
{ boolean result = false;
int2 b = int2(min(lineBegin.x, lineEnd.x), min(lineBegin.y, lineEnd.y));
int2 e = int2(max(lineBegin.x, lineEnd.x), max(lineBegin.y, lineEnd.y));
if (point.x >= b.x && point.x <= e.x &&
point.y >= b.y && point.y <= e.y)
{ int2 normal = lineEnd.sub(lineBegin).perp();
int2 d0 = lineBegin.sub(point);
if (d0.dot(normal) == 0x0)
{ result = true;
}
}
return result;
}
This works for any slope, even if lineBegin == lineEnd == point.
First, create two points b and e, which ensures b <= e. (This is two support lines that have a negative slope) Check if point lands on the box (inclusive) created by those two points.
Then, get the normal of the line, which is a vector perpendicular to it.
You can do this by negating the x and transposing, x,y --> y, -x.
Then you can simply create a vector that points to the line from the point
or to the point from the line, it doesn't matter and doesn't have to be from the center of the line. Once you do that check if the vector is perpendicular to the normal by getting the dot product of the normal and the point vector.
It also makes it a bit easier on you if you've got some sort of math lib or at least a struct with x,y components. But you can do this with scalar components aswell. But getting the dot product and the normal are very simple calculations.
Here's the dot product:
.dot(int2 v) = (x * v.x + y * v.y)
Here's the perpendicular:
.perp() = new int2(y, -x)
.sub() .add() Do what you'd expect and params are in the same order.

Resources