I need help regarding DDA algorithm , i'm confused by the tutorial which i found online on DDA Algo , here is the link to that tutorial
http://i.thiyagaraaj.com/tutorials/computer-graphics/basic-drawing-techniques/1-dda-line-algorithm
Example:
xa,ya=>(2,2)
xb,yb=>(8,10)
dx=6
dy=8
xincrement=6/8=0.75
yincrement=8/8=1
1) for(k=0;k<8;k++)
xincrement=0.75+0.75=1.50
yincrement=1+1=2
1=>(2,2)
2) for(k=1;k<8;k++)
xincrement=1.50+0.75=2.25
yincrement=2+1=3
2=>(3,3)
Now i want to ask that , how this line came xincrement=0.75+0.75=1.50 , when it is written in theory that
"If the slope is greater than 1 ,the roles of x any y at the unit y intervals Dy=1 and compute each successive y values.
Dy=1
m= Dy / Dx
m= 1/ ( x2-x1 )
m = 1 / ( xk+1 – xk )
xk+1 = xk + ( 1 / m )
"
it should be xincrement=x1 (which is 2) + 0.75 = 2.75
or i am understanding it wrong , can any one please teach me the how it's done ?
Thanks a lot)
There seems to be a bit of confusion here.
To start with, let's assume 0 <= slope <= 1. In this case, you advance one pixel at a time in the X direction. At each X step, you have a current Y value. You then figure out whether the "ideal" Y value is closer to your current Y value, or to the next larger Y value. If it's closer to the larger Y value, you increment your current Y value. Phrased slightly differently, you figure out whether the error in using the current Y value is greater than half a pixel, and if it is you increment your Y value.
If slope > 1, then (as mentioned in your question) you swap the roles of X and Y. That is, you advance one pixel at a time in the Y direction, and at each step determine whether you should increment your current X value.
Negative slopes work pretty much the same, except you decrement instead of incrementing.
Pixels locations are integer values. Ideal line equations are in real numbers. So line drawing algorithms convert the real numbers of a line equation into integer values. The hard and slow way to draw a line would be to evaluate the line equation at each x value on your array of pixels. Digital Differential Analyzers optimize that process in a number of ways.
First, DDAs take advantage of the fact that at least one pixel is known, the start of the line. From that pixel, the DDAs calculate the next pixel in the line, until they reach the end point of the line.
Second, DDAs take advantage of the fact that along either the x or y axis, the next pixel in the line is always the next integer value towards the end of the line. DDA's figure out which axis by evaluating the slope. Positive slopes between 0 and 1 will increment the x value by 1. Positive slopes greater than one will increment the y value by 1. Negative slopes between -1 and 0 will increment the x value by -1, and negative slopes less than -1 will increment the y value by -1.
Thrid, DDAs take advantage of the fact that if the change in one direction is 1, the change in the other direction is a function of the slope. Now it becomes much more difficult to explain in generalities. Therefore I'll just consider positive slopes between 0 and 1. In this case, to find the next pixel to plot, x is incremented by 1, and the change in y is calculated. One way to calculate the change in y is just add the slope to the previous y, and round to the integer value. This doesn't work unless you maintain the y value as a real number. Slopes greater than one can just increment y by 1, and calculate the change in x.
Fourth, some DDAs further optimize the algorithm by avoiding floating point calculations. For example, Bresenham's line algorithm is a DDA optimized to use integer arithmetic.
In this example, a line from (2, 2) to (8, 10), the slope is 8/6, which is greater than 1. The first pixel is at (2, 2). The next pixel is calculated by incrementing the y value by 1, and adding the change in x (the inverse slope, of dx/dy = 6/8 = .75) to x. The value of x would be 2.75 which is rounded to 3, and (3, 3) is plotted. The third pixel would increment y again, and then add the change in x to x (2.75 + .75 = 3.5). Rounding would plot the third pixel at (4, 4). The fourth pixel would then plot (5, 4), since y would be incremented by 1, but x would be incremented by .75, and equal 4.25.
From this example, can you see the problem with your code?
Related
I have measured points for fitting lines. The number of points I have measured in x-direction is the first value of the Point (e.g. X1) and the second value of this point are the measured points in y-direction.
X1(2,9)
X2(9,3)
X3(5,4)
X4(6,4)
This means,that e.g. in X3 I have measured 5 points in x-direction and in y-direction I have measured 4 points. With the points in x-direction I will fit a line in x-direction and with the points in y-direction, I will fit a line in y-direction. To get better results, more points in x direction and y-direction are better then less. For example it is better to choose X4, then X1, because in X1 I have in y-direction 9-Points, but in x-direction only 2 points, which will give me a poor result in x-direction. In X4 I have both: a high number of points in x and in y-direction. So I can be sure, that the lines will be good enough. So I want to find this X#, where I have a (the) high(est) number of points in x and in y direction
Have you tried checking your code? It seems like your X is on an ascending order and your Y is using the opposite order. Maybe like insert both x and y values on a variable then sort them with the same order as you want them to be.
You would need to write a function that ranks the 'fitness' of a point.
def fitness(point):
'''
ranks a point by the total number of points,
minus the difference between the number of x and y points.
uses total number of points as a secondary rank
'''
num_points = sum(point)
xydiff = max(point) - min(point)
return num_points - xydiff, num_points
The above function should be adjusted, depending on how you want to want to weight the points.
>>> points = [(2,9), (9,3), (5,4), (6,4)]
>>> sorted(points, key=fitness, reverse=True)
[(6, 4), (5, 4), (9, 3), (2, 9)]
>>> max(points, key=fitness) # the most fit point
(6, 4)
Let's say I have this matrix with n=4 and m=5
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
Let's say I have a diagonal from the (1,2) point to the (4,5) point. And I have a point P(3,4). How can I check in my algorithm that P is on the diagonal?
TL;DR
Instead of an n-by-m matrix, think about it like a x-y grid. You can get the equation of a line on that grid, and once you have that equation, you put the x coordinate of the point you are interested in checking into your equation. If the y value you calculate from the equation matches the y coordinate of the point you are checking, the point lies on the line.
But How Do I Maths?
First some quick terminology. We have 3 points of interest in this case - the two points that define the line (or "diagonal", as the OP calls it), and the one point that we want to check. I'm going to designate the coordinates of the "diagonal" points with the numbers 1 and 2, and the point we want to check with the letter i. Additionally, for the math we need to do later, I need to treat the horizontal and vertical coordinates of the points separately, and I'll use your n-by-m convention to do so. So when I write n1 in an equation below, that is the n coordinate of the first point used to define the diagonal (so the 1 part of the point (1,2) that you give in your example).
What we are looking for is the equation of a line on our grid. This equation will have the form n = (slope) * m + (intercept).
Okay, now that we have the definitions taken care of, we can write the equations. The first step to solving the problem is finding the slope of your line. This will be the change in the vertical coordinate divided by the change in the horizontal component between the two points that define the line (so (n2 - n1) / (m2 - m1)). Using the values from your example, this will be (4 - 1) / (5 - 2) = 3 / 3 = 1. Note that since you are doing a division here, it is possible that your answer will not be a whole number, so make sure you keep that in mind when declaring your variables in whatever programming language you end up using - unintentional rounding in this step can really mess things up later.
Once we have our slope, the next step is calculating our intercept. We can do this by plugging our slope and the m and n coordinates into the equation for the line we are trying to get. So we start with the equation n1 = (slope) * m1 + (intercept). We can rearrange this equation to (intercept) = n1 - (slope) * m1. Plugging in the values from our example, we get (intercept) = 1 - (1 * 2) = -1.
So now we have the general equation of our line, which for our example is n = (1) * m + (-1).
Now that we have the (slope) and (intercept), we can plug in the coordinates of any point we want to check and see if the numbers match up. Our example point has a m coordinate of 4, so we can plug that into our equation.
n = (1) * (4) + (-1) = 3
Since the n coordinate we calculated using our equation matches the n coordinate of our point in our example, we can say that the sample point DOES fall on the line.
Suppose we wanted to also check to see if the point (2,5) was also on the line. When we plug that point's m coordinate into our equation, we get...
n = (1) * (5) + (-1) = 4
Since the n coordinate we calculated with our equation (4) doesn't match the n coordinate of the point we were checking (2), we know this point DOES NOT fall on the line.
Consider points Y given in increasing order from [0,T). We are to consider these points as lying on a circle of circumference T. Now consider points X also from [0,T) and also lying on a circle of circumference T.
We say the distance between X and Y is the sum of the absolute distance between the each point in X and its closest point in Y recalling that both are considered to be lying in a circle. Write this distance as Delta(X, Y).
I am trying to find a quick way of determining a rotation of X which makes this distance as small as possible.
My code for making some data to test with is
#!/usr/bin/python
import random
import numpy as np
from bisect import bisect_left
def simul(rate, T):
time = np.random.exponential(rate)
times = [0]
newtime = times[-1]+time
while (newtime < T):
times.append(newtime)
newtime = newtime+np.random.exponential(rate)
return times[1:]
For each point I use this function to find its closest neighbor.
def takeClosest(myList, myNumber, T):
"""
Assumes myList is sorted. Returns closest value to myNumber in a circle of circumference T.
If two numbers are equally close, return the smallest number.
"""
pos = bisect_left(myList, myNumber)
before = myList[pos - 1]
after = myList[pos%len(myList)]
if after - myNumber < myNumber - before:
return after
else:
return before
So the distance between two circles is:
def circle_dist(timesY, timesX):
dist = 0
for t in timesX:
closest_number = takeClosest(timesY, t, T)
dist += np.abs(closest_number - t)
return dist
So to make some data we just do
#First make some data
T = 5000
timesX = simul(1, T)
timesY = simul(10, T)
Finally to rotate circle timesX by offset we can
timesX = [(t + offset)%T for t in timesX]
In practice my timesX and timesY will have about 20,000 points each.
Given timesX and timesY, how can I quickly find (approximately) which rotation of timesX gives
the smallest distance to timesY?
Distance along the circle between a single point and a set of points is a piecewise linear function of rotation. The critical points of this function are the points of the set itself (zero distance) and points midway between neighbouring points of the set (local maximums of distance). Linear coefficients of such function are ±1.
Sum of such functions is again piecewise linear, but now with a quadratic number of critical points. Actually all these functions are the same, except shifted along the argument axis. Linear coefficients of the sum are integers.
To find its minimum one would have to calculate its value in all critical points.
I don'see a way to significantly reduce the amount of work needed, but 1,600,000,000 points is not such a big deal anyway, especially if you can spread the work between several processors.
To calculate sum of two such functions, represent the summands as sequences of critical points and associated coefficients to the left and to the right of each critical point. Then just merge the two point sequences while adding the coefficients.
You can solve your (original) problem with a sweep line algorithm. The trick is to use the right "discretization". Imagine cutting your circle up into two strips:
X: x....x....x..........x................x.........x...x
Y: .....x..........x.....x..x.x...........x.............
Now calculate the score = 5+0++1+1+5+9+6.
The key observation is that if we rotate X very slightly (right say), some of the points will improve and some will get worse. We can call this the "differential". In the above example the differential would be 1 - 1 - 1 + 1 + 1 - 1 + 1 because the first point is matched to something on its right, the second point is matched to something under it or to its left etc.
Of course, as we move X more, the differential will change. However only as many times as the matchings change, which is never more than |X||Y| but probably much less.
The proposed algorithm is thus to calculate the initial score and the time (X position) of the next change in differential. Go to that next position and calculate the score again. Continue until you reach your starting position.
This is probably a good example for the iterative closest point (ICP) algorithm:
It repeatedly matches each point with its closest neighbor and moves all points such that the mean squared distance is minimized. (Note that this corresponds to minimizing the sum of squared distances.)
import pylab as pl
T = 10.0
X = pl.array([3, 5.5, 6])
Y = pl.array([1, 1.5, 2, 4])
pl.clf()
pl.subplot(1, 2, 1, polar=True)
pl.plot(X / T * 2 * pl.pi, pl.ones(X.shape), 'r.', ms=10, mew=3)
pl.plot(Y / T * 2 * pl.pi, pl.ones(Y.shape), 'b+', ms=10, mew=3)
circDist = lambda X, Y: (Y - X + T / 2) % T - T / 2
while True:
D = circDist(pl.reshape(X, (-1, 1)), pl.reshape(Y, (1, -1)))
closestY = pl.argmin(D**2, axis = 1)
distance = circDist(X, Y[closestY])
shift = pl.mean(distance)
if pl.absolute(shift) < 1e-3:
break
X = (X + shift) % T
pl.subplot(1, 2, 2, polar=True)
pl.plot(X / T * 2 * pl.pi, pl.ones(X.shape), 'r.', ms=10, mew=3)
pl.plot(Y / T * 2 * pl.pi, pl.ones(Y.shape), 'b+', ms=10, mew=3)
Important properties of the proposed solution are:
The ICP is an iterative algorithm. Thus it depends on an initial approximate solution. Furthermore, it won't always converge to the global optimum. This mainly depends on your data and the initial solution. If in doubt, try evaluating the ICP with different starting configurations and choose the most frequent result.
The current implementation performs a directed match: It looks for the closest point in Y relative to each point in X. It might yield different matches when swapping X and Y.
Computing all pair-wise distances between points in X and points in Y might be intractable for large point clouds (like 20,000 points, as you indicated). Therefore, the line D = circDist(...) might get replaced by a more efficient approach, e.g. not evaluating all possible pairs.
All points contribute to the final rotation. If there are any outliers, they might distort the shift significantly. This can be overcome with a robust average like the median or simply by excluding points with large distance.
I was studying a book explaining DDA algorithm and got stuck at a point .According to the rule the points should be rounded up so here in this case It should be (4,6) at the place of (4,5) isn't it .Check the picture below I've encircled the points I feel incorrect in the book ,so Am I taking it wrong or the book got a misprint here ?
In order to draw a continuous line on a discreet plan (x,y), x, y integer, that algorithm, based on the slope of the line, makes either x or y the "carrier" (always incremented by one) and the other coordinate is interpolated. Reason are:
the drawing should be as close as possible to the line equation
there should be no "hole" in the drawn line
there is no need to calculate more values than necessary (eg, having x "carrier", x+0.1, x+0.2 etc...)
The document seems to be this PDF, where you can see that the continuous line, is not always in the center of the pixels it crosses. Thanks to that algorithm, a dot will have an immediate neighbor, either at x+1 or y+1 depending on the slope (the interpolated coordinate could be twice (or more) in a row the same rounded value. Eg if y is interpolated, you could have (10,20), (11, 20), (12, 21) having twice y=20).
Considering only the quarter [0, 90] degrees, the line start from coordinates (0,0). If the line slope is below 45 degrees, it is better to have x as "carrier" (incremented by 1), and y interpolated. Example
+++
+++
+++
here x is always incremented by one, but y takes sometimes the same value as for the previous x (eg, for x=0, x=1, x=2 we have the same y)
Back to rounding
In the same document, it is said page 47
in order to plot a pixel on the screen, we need to round off the coordinates to a nearest integer
which is usually the case when doing an interpolation. It is better to take the nearest integer. That means, usually,
take integer part + 1 if first decimal is >= .5 (eg. 4.71 => 5)
take only integer part if 1st decimal is < .5 (eg 5.42 => 5)
so that:
the integer pixel coordinates are closer to the equation value with decimals
the sum of the rounded value (eg for x) is more likely to be close to the sum of the calculated value from the equation.
In this particular case, (4, 38/7) ~= (4, 5.43), 5.43 is rounded to the nearest integer, ie 5 and not 6.
I need to implement a function which normalizes coordinates. I define normalize as (please suggest a better term if Im wrong):
Mapping entries of a data set from their natural range to values between 0 and 1.
Now this was easy in one dimension:
static List<float> Normalize(float[] nums)
{
float max = Max(nums);
float min = Min(nums);
float delta = max - min;
List<float> li = new List<float>();
foreach (float i in nums)
{
li.Add((i - min) / delta);
}
return li;
}
I need a 2D version as well and that one has to keep the aspect ratio intact. But Im having some troubles figuring out the math.
Although the code posted is in C# the answers need not to be.
Thanks in advance. :)
I am posting my response as an answer because I do not have enough points to make a comment.
My interpretation of the question: How do we normalize the coordinates of a set of points in 2 dimensional space?
A normalization operation involves a "shift and scale" operation. In case of 1 dimensional space this is fairly easy and intuitive (as pointed out by #Mizipzor).
normalizedX=(originalX-minX)/(maxX-minX)
In this case we are first shifing the value by a distance of minX and then scaling it by the range which is given by (maxX-minX). The shift operation ensures that the minimum moves to 0 and the scale operation squashes the distribution such that the distribution has an upper limit of 1
In case of 2d , simply dividing by the largest dimension is not enought. Why?
Consider the simplified case with just 2 points as shown below.
The maximum value of any dimension is the Y value of point B and this 10000.
Coordinates of normalized A=>5000/10000,8000/10000 ,i.e 0.5,0.8
Coordinates of normalized A=>7000/10000,10000/10000 ,i.e 0.7,1.0
The X and Y values are all with 0 and 1. However, the distribution of the normalized values is far from uniform. The minimum value is just 0.5. Ideally this should be closer to 0.
Preferred approach for normalizing 2d coordinates
To get a more even distribution we should do a "shift" operation around the minimum of all X values and minimum of all Y values. This could be done around the mean of X and mean of Y as well. Considering the above example,
the minimum of all X is 5000
the minimum of all Y is 8000
Step 1 - Shift operation
A=>(5000-5000,8000-8000), i.e (0,0)
B=>(7000-5000,10000-8000), i.e. (2000,2000)
Step 2 - Scale operation
To scale down the values we need some maximum. We could use the diagonal AB whose length is 2000
A=>(0/2000,0/2000), i.e. (0,0)
B=>(2000/2000,2000/2000)i.e. (1,1)
What happens when there are more than 2 points?
The approach remains similar. We find the coordinates of the smallest bounding box which fits all the points.
We find the minimum value of X (MinX) and minimum value of Y (MinY) from all the points and do a shift operation. This changes the origin to the lower left corner of the bounding box.
We find the maximum value of X (MaxX) and maximum value of Y (MaxY) from all the points.
We calculate the length of the diagonal connecting (MinX,MinY) and (MaxX,MaxY) and use this value to do a scale operation.
.
length of diagonal=sqrt((maxX-minX)*(maxX-minX) + (maxY-minY)*(maxY-minY))
normalized X = (originalX - minX)/(length of diagonal)
normalized Y = (originalY - minY)/(length of diagonal)
How does this logic change if we have more than 2 dimensions?
The concept remains the same.
- We find the minimum value in each of the dimensions (X,Y,Z)
- We find the maximum value in each of the dimensions (X,Y,Z)
- Compute the length of the diagonal as a scaling factor
- Use the minimum values to shift the origin.
length of diagonal=sqrt((maxX-minX)*(maxX-minX)+(maxY-minY)*(maxY-minY)+(maxZ-minZ)*(maxZ-minZ))
normalized X = (originalX - minX)/(length of diagonal)
normalized Y = (originalY - minY)/(length of diagonal)
normalized Z = (originalZ - minZ)/(length of diagonal)
It seems you want each vector (1D, 2D or ND) to have length <= 1.
If that's the only requirement, you can just divide each vector by the length of the longest one.
double max = maximum (|vector| for each vector in 'data');
foreach (Vector v : data) {
li.add(v / max);
}
That will make the longest vector in result list to have length 1.
But this won't be equivalent of your current code for 1-dimensional case, as you can't find minimum or maximum in a set of points on the plane. Thus, no delta.
Simple idea: Find out which dimension is bigger and normalize in this dimension. The second dimension can be computed by using the ratio. This way the ratio is kept and your values are between 0 and 1.