I need an algorithm to convert a closed bezier curve (perhaps self-crossing) to a binary bitmap: 0 for inside pixels and 1 for outside. I'm writing a code that needs to implement some operations on bezier curves, could anybody give me some resources or tutorials about beziere? Wikipedia and others didn't say anything about optimization, subtracting, union, knot insertion and deletion and other operations :-)
alt text http://www.imagechicken.com/uploads/1271001073057545100.jpg
This paper by Charles Loop and Jim Blinn goes into lots of detail on your question.
Another option is to tessellate the Bezier curve into line segments and then use your favorite polygon fill algorithm.
I'd like to add that the tessellation option is very efficient and gives excellent results. It sounds wrong to approximate a Bezier curve by line segments, because you think the output will look like a polygon. The trick is to make the line segments short enough so that the error is very small (say, less than 1/10 of a pixel).
Here is a formula that I have used to compute the step size, to ensure that the maximum error (i.e., deviation of line segments from true curve) is less than delta:
Let (x1,y1), (x2,y2), (x3,y3), (x4,y4) be the control points of the Bezier curve, in pixel coordinates.j
dd0 = square(x1-2*x2+x3) + square(y1-2*y2+y3);
dd1 = square(x2-2*x3+x4) + square(y2-2*y3+y4);
dd = 6*sqrt(max(dd0, dd1));
Then dd is the maximal value of the 2nd derivative over the curve - because the 2nd derivative of a cubic is a linear function, this maximum must occur at an endpoint. Here I have used square(x) as an abbreviation for x*x.
if (8*delta > dd) {
epsilon = 1;
} else {
epsilon = sqrt(8*delta/dd);
}
Then epsilon is your step size: if you choose the endpoints of your line segments at t=0, t=epsilon, t=2*epsilon, ..., (and the last endpoint at t=1), then the line segments will be within delta of the original curve.
Choose delta = 0.1 and you get bitmap output that is visually indistinguishable from the original curve.
Related
Suppose I have an image and I want to find a subarray with shape 3x3 that contains the maximum sum compared to other subarrays.
How do I do that in python efficiently (run as fast as possible)? If you can provide a sample code that would be great.
My specific problem:
I want to extract the location of the center of the blob in this heatmap
I don't want to just get the maximum point because that would cause the coordinate to not be very precise. The true center of the blob could actually be between 2 pixels. Thus, it's better to do weighted average between many points to obtain subpixel precision. For example, if there are 2 points (x1,y1) and (x2,y2) with values 200 and 100. Then the average coordinate will be x=(200*x1+100*x2)/300 y=(200*y1+100*y2)/300
One of my solution is to do a convolution operation. But I think it's not efficient enough because it requires multiplication to the kernel (which contains only ones). I'm looking for a fast implementation so I cannot do looping myself because I'm not sure if it will be fast.
I want to do this algorithm to 50 images every few milliseconds. (Image come in as a batch). Concretely, think of these images as output of a machine learning model that output heatmaps. In order to obtain the coordinate from these heatmaps, I need to do some kind of weighted average between the coordinates with high intensity. My idea is to do a weighted average around 3x3 area on the image. I am also open to other approaches that can be faster or more elegant.
Looking for the "subarray of shape 3x3 with the maximum sum" is the same as looking for the maximum of an image after it has been filtered with an un-normalized 3x3 box filter. So it boils down to finding efficiently the maximum of an image, which you assume is a (perhaps "noisy") discrete sample of an underlying continuous and smooth signal - hence your desire to find a sub-pixel location.
You really need to split the problem in 2 parts:
Find the pixel location m=(xm, ym) of the maximum value of the image. This requires no more than a visit of every pixel in the image, and one comparison per pixel, so it's O(N) and hence optimal as long as you are operating at the native image resolution. In OpenCv it is done using
the minMaxLoc function.
Apply whatever model of the image you are using to find its (subpixel-interpolated) maximum in a neighborhood of m.
To clarify point (2): you write
I don't want to just get the maximum point because that would cause the coordinate to not be very precise. The true center of the blob could actually be between 2 pixels
While intuitively plausible, this assertion needs to be made more precise in order to be computable. That is, you need to express mathematically what assumptions you make about the image, that bring you to search for a "true" maximum between pixel-sampled location.
A simple example for such assumptions is quadratic smoothness. In this scenario you assume that, in a small (say, 3x3, of 5x5) neighborhood of the "true" maximum location, the image signal z is well approximated by a quadratic:
z = A00 dx^2 + A01 dx dy + A11 dy^2 + A02 dx + A12 dy + A22
where:
dx = x - xm; dy = y - ym
This assumption makes sense if the underlying signal is expected to be at least 3rd order continuous and differentiable, because of the Taylor series theorem. Geometrically, it means that you assume (hope?) that the signal looks like a quadric (a paraboloid, or an ellipsoid) near its maximum.
You can then evaluate the above equation for each of the pixels in a neighborhood of m, replacing the actual image values for z, and thus obtain a linear system in the unknown Aij, with as many equations as there are neighbor pixels (so even a 3x3 neighborhood will yield an over-constrained system). Solving the system in the least-squares sense gives you the "optimal" coefficients Aij. The theoretical maximum as predicted by this model is where the first partial derivatives vanish:
del z / del dx = 2 A00 dx + A01 dy = 0
del z / del dy = A01 dx + 2 A11 dy = 0
This is a linear system in the two unknown (dx, dy), and solving it yields the estimated location of the maximum and, through the above equation for z, the predicted image value at the maximum.
In terms of computational cost, all such model estimations are extremely fast, compared with traversing an image of even moderate size.
I am sorry I did not exactly understand the meaning of your last paragraph so I have just stopped at a point where I got all the coordinates having the maximum value. I have used cv2.filter2D for convolution on a thresholded image and then using np.amax and np.where have found the coordinates having the maximum value.
import cv2
import numpy as np
from timeit import default_timer as timer
img = cv2.imread('blob.png', 0)
start = timer()
_, thresh = cv2.threshold(img, 240, 1, cv2.THRESH_BINARY)
mask = np.ones((3, 3), np.uint8)
res = cv2.filter2D(thresh, -1, mask)
result = np.where(res == np.amax(res))
end = timer()
print(end - start)
I don't whether it as efficient as you want or not but the output was 0.0013461999999435648 s
P.S. The image you have provided had a white border which I had to crop out for this method.
One way is to sub-sampling the image and find the neighborhood of the desired point. You can make it by doing a loop not on all the pixels but on e.g. every 5 pixels (row=row+5andcol=col+5 in the loop). After finding the near location, consider a specific neighborhood around that location and do a loop on whole pixels of that specific crop to find the exact location.
Based on my knowledge of image processing, to get a reliable result that works for any one blob, follow these steps:
Make the image greyscale if it isn’t already (pixel values 0-255)
Normalise the image so that pixel intensities cover the full range of 0-255
Convert image to binary (a pixel is either 0 or 1) - this can be achieved by thresholding, such as applying the rule that any pixel less than or equal to 127 in intensity is given an intensity of 0 and anything else is given an intensity of 1
Find the weighted average of all the pixels that hold the value of “1”
or
Apple an erosion to the image until you are left with either 2 pixels or 1 pixel.
Case 1
If you have two pixels then you need to find the u and v co-ordinates if both pixels. The centre of the blob will be the halfway point between the u and v coordinates of the pixels.
Case 2
If you have one pixel left then that pixel’s co-ordinates is the centre point.
—————
You mentioned about achieving this quickly in Python:
Python by design is an interpreted language, so it executed line by line, making it less suitable for highly iterative tasks like image processing. However, you can make use of libraries like OpenCV (https://docs.opencv.org/2.4/index.html), which is written in C, to mitigate this apart from making the task at hand a lot easier for you.
OpenCV also provides solutions for all the steps I listed above in this capacity, therefore you should be able to achieve a reliable solution fairly quickly, though I can’t say for sure if it will hit your target of 50 images every few milliseconds. Other factors to take into account is the size of the image you are processing. That will increase the processing load exponentially.
UPDATE
I just found a good article that practically echoes my step-process:
https://www.learnopencv.com/find-center-of-blob-centroid-using-opencv-cpp-python/
More importantly it also denotes the formula for finding the centroid mathematically as:
c = (1/n)sigma(n, i = 1, x_i)
but this is better written in the article than I can do so here.
I'm trying to plot XY graph in real time using Java. Functions that only rely on X are easy. Just iterate over x0...xn, get value and draw lines between the points. There are a lot of guides on it and it's intuitive.
But there is literally no guide on plotting graphs with x AND y being a variable.
Consider this equation: sin(x^3 * y^2) = cos(x^2 * y^3)
Using online Graph plotter I get this:
While my best result plotting the same function is this:
I just iterate over every pixel on screen and pass pixel positions as parameters to the function. If function's output is close to 0, I color the pixel. As you can see it's bad. It also takes huge amount of processing power. It only redraws once every couple of seconds. And if I try to increase precision, all lines just become thicker. Especially around intersections.
My question is how can I make my program faster and make it produce better looking graphs. Maybe there are some algorithms for that purpose?
The challenge is to chose the correct threshold. Pixels where abs(f(x,y)) is below the threshold should be colored. Pixels above the threshold should be white.
The problem is that if the threshold is too low, gaps appear in places where no pixel is exactly on the line. On the other hand, if the threshold is too high, the lines widen in places where the function is near zero, and the function is changing slowly.
So what's the correct threshold? The answer is the magnitude of the gradient, multiplied by the radius of a pixel. In other words, the pixel should be colored when
abs(f(x,y)) < |g(x,y)| * pixelRadius
The reason is that the magnitude of the gradient is equal to the maximum slope of the surface (at a given point). So a zero crossing occurs within a pixel if the slope is large enough to reduce the function to zero, inside the pixel.
That of course is only rough approximation. It assumes that the gradient doesn't change significantly within the area bounded by the pixel. The function in the question conforms to that assumption reasonably well, except in the upper right corner. Notice that in the graph below, there are Moiré patterns in the upper right. I believe that those are due to the failure in my antialiasing calculation: I don't compensate for a rapidly changing gradient.
In the graph below, pixels are white if
abs(f(x,y)) > |g(x,y)| * pixelRadius
Otherwise the pixel intensity is a number from 0 to 1, with 0 being black and 1 being white:
intensity = abs(f(x,y)) / (|g(x,y)| * pixelRadius)
I don't how exactly the online plotter did, but here are some suggestions.
Simplify your equation, as to this specific one, you can easily have x^2 * y^2 * (x ± y) = (2 * n + 1 / 2) * pi where n for any integer. It's much clearer than the original one.
Draw lines rather than points. Every n here stands for 4 curves, you can now loop over x and figure out y and draw a line between adjacent points.
Hope it helps!
Using SVG and cubic beziers, I'm attempting to generate the outline of S-curve shapes of various thicknesses, widths and heights, such as these:
I'm stuck on figuring out where the Bezier handle for each of the four corner nodes should be positioned in order to give the curve formed by their in-between area a constant thickness (or at least approximately).
I can see that the horizontal distance between the inner and outer handle at each end is dependent on the thickness of the curve and the overall curve width and height, but I'm stumped trying to get something that links them all together.
Is there a formula that would give me the horizontal position for each of the handles?
(I'm using d3js, so if there's a plugin or function I've missed that would help with this, that'd be even better).
I recommend the Tiller-Hanson algorithm used in FreeType's ftstroke module. It is normally used for converting a zero-width line into a stroke with some finite width; you can use the way it creates one of the two envelope lines (one on either side of the original line) to get your parallel curve. It handles any line made from straight segments and quadratic and cubic Bézier splines.
For more information see my answer to this question: How to get the outline of a stroke?.
Unfortunately, unless your curve has a uniform curvature, you can't get a fixed distance offset curve simply by linear scaling the original curve.
In order to get an offset curve for a Bezier, you have to do a bit of calculus to figure out how to split up the curve into "safe" segments, which can be offset by scaling, and then scale each piece.
More details over on http://pomax.github.io/bezierinfo/#offsetting, but basically what you want is something that usually you have someone else implement for you because it's a chore to get right.
You can actually offset the control polygon of one cubic Bezier curve to generate the control polygon for the "offset" curve, which will still be a cubic Bezier curve.
The following picture shall make it clear about what does "offset control polygon" mean. Given the cubic Bezier curve defined by P0, P1, P2 and P3, we can offset the 3 infinite lines L01 (defined by P0 and P1), L12 (defined by P1 and P2) and L23 (defined by P2 and P3). The resulting lines are denoted as L01*, L12* and L23*. The intersection points of L01* and L12* will be Q1 and that between L12* and L23* will be Q2. Q0 and Q3 will be the direct projected point of P0 and P3 onto L01* and L23*. The offset curve will be the cubic Bezier curve defined by Q0, Q1, Q2 and Q3.
Of course, the "offset" curve created in this way is not the exact offset but just an approximation. But as long as the offset distance is not too big and you are not too picky about the accuracy, the result is generally good enough. There are two advantages of this approach (besides its simplicity):
the approximate offset curve will honor the true offset curve's tangent direction at the start and end of the curve.
if you offset the offset curve in the opposite direction by the same distance, you will get back the original cubic Bezier curve.
Suppose that there is a parabola Y = aX^2 + bX + c, and it might be rotated as follow:
X = x.sin(phi) + y.cos(phi)
Y = x.cos(phi) - y.sin(phi)
phi = rotation angle
We wish to fit it on a border (e.g. inner border of an eyelid, figure below). The problem is that how we can change the parabola in each iteration such that it minimizes a cost function. We know that the parabola can be in different rotation and its origin may vary in the search region. Note that the there are two given points which the fitted parabola should passes through them (e.g. the white squares in fig below). So, In each iteration we can compute a, b and c by the two given points and the origin point (three equations and three variables).
The question is how we can reach the goal in minimum iteration (not to test all the possibilities, i.e. all angles and all positions in the search region).
Any idea will be appreciated.
#woodchips: I think this is a programming problem, and he asked a solution for the implementation.I definitely disagree with you.
A possible solution would be to first search along the vertical line which is orthogonal to the line between the two given points. And also you can vary the angle in this interval. As the nature of your problem (the border of eyelid), you can limit the angle variation between -pi/4 and pi/4. After you find the minimum cost for a position in this vertical line, you can search along the horizontal line and do similar tasks.
Why not use regression to fit a parabola to several points in the target shape? Then you could use which ever algorithm you wanted to get an approximate solution. Newton's method converges pretty fast. The optimization here is on the coefficients in the approximate parabolas.
I'm looking for an algorithm to insert a new control point on a Bézier curve, without deforming.
Does anybody know a library or reference for Bézier algorithms (insertion, optimize, de Casteljau ...)?
This is called the "knot insertion problem". For Bézier curves, the de Casteljau algorithm will give you the right answer. Here is the simple algorithm for a degree 3 Bézier.
Say you want to insert a knot at a fraction t of the parameter space inside the Bézier curve defined by P0, P1, P2, P3. Here's what you do:
P0_1 = (1-t)*P0 + t*P1
P1_2 = (1-t)*P1 + t*P2
P2_3 = (1-t)*P2 + t*P3
P01_12 = (1-t)*P0_1 + t*P1_2
P12_23 = (1-t)*P1_2 + t*P2_3
P0112_1223 = (1-t)*P01_12 + t*P12_23
Then your first Bézier will be defined by: P_0, P0_1, P01_12, P0112_1223; your second Bézier is defined by: P0112_1223, P12_23, P2_3, P3.
The geometrical interpretation is simple: you split each segment of the Bézier polygon at fraction t, then connect these split points in a new polygon and iterate. When you're left with 1 point, this point lies on the curve and the previous/next split points form the previous/next Bézier polygon. The same algorithm also works for higher degree Bézier curves.
Now it can get trickier if you want to insert the control point not at a specific value of t but at a specific location in space. Personally, what I would do here is simply a binary search for a value of t that falls close to the desired split point... But if performance is critical, you can probably find a faster analytic solution.
You could also take the mathematical approach.
A qubic Bézier curve with control points can be written as:
Its derivative w.r.t. is
To limit the curve from to , you get new control points :
Proof
Substituting
We get
The first and last points of the sub-curve are the first and last new control points
And the tangent at those points is
So
Adding this for completeness.
An open-source implementation of many Bézier path operations can be found inside GIMP source code, in gimpbezierstroke.c. For reference on inserting a new anchor, search for gimp_bezier_stroke_anchor_insert.