I'm trying to write a ridge detection algorithm, and all of the sources I've found seem to conflate edge detection with ridge detection. Right now, I've implemented the Canny edge detection algorithm, but it's not what I want: for example, given a single line in the image, it will effectively translate it to a double line of edges (since it will record both sides of the line) - I just want it to read the one line.
The wikipedia article about ridge detection has a bunch of math, but this kind of this doesn't help me as a programmer (not that I'm averse to math, but it's not my field, and I don't understand how to translate their differential equations into code). Is there a good source for actually implementing this? Or, for that matter, is there a good open source implementation?
Edit: here's the simple example. We start with a simple line:
http://img24.imageshack.us/img24/8112/linez.th.png
and run the Canny Algorithm to get:
http://img12.imageshack.us/img12/1317/canny.th.png
(you can see that it's thicker here - if you click on the image, you'll see that it really is two adjacent lines with a blank in between)
Also, I'm writing in C++, but that shouldn't really matter. But I want to code the algorithm, not just write SomePackage::findRidges() and be done with it.
Maybe you need to think in terms of cleaning up the line you already have, rather than a Canny-like edge detection. It feels like you should be able to do something with image morphology, in particular I'm thinking of the skeletonize and ultimate eroded points type operations. Used appropriately these should remove from your image any features which are not 'lines' - I believe they're implemented in Intel's OpenCV library.
You can recover a single line from your double line generated using the Canny filter using one dilate operation followed by 3 erodes (I tried it out in ImageJ) - this should also remove any edges.
I was going to suggest cleaning up your lines like Ian said, but if you don't want to do that, you might also look into doing some variant of a hough transform.
http://en.wikipedia.org/wiki/Hough_transform
You should be able to get the actual equation for the line from this, so you can make it as thin or as thick as you like. The only tricky part is figuring out where the line ends.
Here's the code I wrote for a hough transform a few years ago, written in MATLAB. I'm not sure how well it works anymore, but it should give you a general idea. It will find all the lines (not segments) in an image
im = imread('cube.tif');
[bin1,bin2,bin3] = canny(im);
%% define constants
binary = bin1;
distStep = 10; % in pixels
angStep = 6; % in degrees
thresh = 50;
%% vote
maxDist = sqrt((size(binary,1))^2+(size(binary,2))^2);
angLoop = 0:angStep*pi/180:pi;
origin = size(binary)/2;
accum = zeros(ceil(maxDist/distStep)+1,ceil(360/angStep)+1);
for y=1:size(binary,2)
for x=1:size(binary,1)
if binary(x,y)
for t = angLoop
dx = x-origin(1);
dy = y-origin(2);
r = x*cos(t)+y*sin(t);
if r < 0
r = -r;
t = t + pi;
end
ri = round(r/distStep)+1;
ti = round(t*180/pi/angStep)+1;
accum(ri,ti) = accum(ri,ti)+1;
end
end
end
end
imagesc(accum);
%% find local maxima in accumulator
accumThresh = accum - thresh;
accumThresh(logical(accumThresh<0)) = 0;
accumMax = imregionalmax(accumThresh);
imagesc(accumMax);
%% calculate radius & angle of lines
dist = [];
ang = [];
for t=1:size(accumMax,2)
for r=1:size(accumMax,1)
if accumMax(r,t)
ang = [ang;(t-1)*angStep/180*pi];
dist = [dist;(r-1)*distStep];
end
end
end
scatter(ang,dist);
If anyone is still interested in this, here is an implementation of the ridges/valleys algorithm: C++ source code. Look for a function called get_ridges_or_valleys(). This implementation is a 3D version of the algorithm proposed by Linderhed (2009). See page 8 of the paper for the ridges/valleys algorithm.
Related
I am trying to write a Minecraft Datapack, which will plot a full armorstand circle around whatever runs the particular command. I am using a 3rd party mathematics datapack to use Sin and Cos. However, when running the command, the resulting plot was... not good. As you can see here: 1. Broken Circle., rather than have each vertex evenly placed in a circular line, I find a strange mess instead.
I would have thought loosing precision in Cos and Sin would simply make the circle more angular, I didn't expect it to spiral. What confuses me, is that +z (the red square) and -x (the purple one) are all alone. You can see on the blue ring (Which was made with a smaller radius) the gap between them persists.
My main issue is; How did my maths go from making a circle to a shredded mushroom, and is there a way to calculate the vertices with a greater precision?
Going into the project I knew I could simply spin the centre entity, and summon an armorstand x blocks in front using ^5 ^ ^, however I wanted to avoid this, due to my desire to be able to change the radius without needing to edit the datapack. To solve this, I used the Sin and Cos components to plot a new point, using a radius defined with scoreboards.
I first tested this using Scratch, in order to check my maths. You can see my code here: 2. Scratch code.
With an addition of the pen blocks, I was able to produce a perfect circle, which you can see here:
. Scratch visual proof.
With my proof of concept working, I looked online and found a Mathematical Functions datapack by yosho27, since the Cos and Sin functions are not built into the game. However, due to how Minecraft scoreboards are only Integers, Yosho27 multiplied the result of Cos and Sin by 100 to preserve 2 decimal places.
To start with, I am using a central armorstand with the tag center, which is at x: 8.5 z: 8.5. The scoreboards built into yosho's datapack that I am using is math_in for the values I want converted and math_out, which is where the final value is dumped.
Using signs, I keep track of the important values I am working with, as seen here: 4. Sign maths.
As I was writing this, I decided to actually compare both numbers to find this: 5. Image comparison, which shows me that somewhere in this calculation process, the maths has gone wrong. I modified the scratch side to match the minecraft conditions as much as possible, such as x100 and adding 850 to the result. From this result, I can see a disparity between x and z, even though they should be equal. Where Minecraft says 1: x= 864 z= 1487, Scratch says 1: x= 862.21668448: z= 1549.89338664. I assume this means the datapack's Cos and Sin are not accurate enough?
In light of this , I looked in yosho's datapack, I found this: 6. Yosho's code., which I just modified to be *= 10 instead of divide, in the hope of getting more precision. Modifying the rest of my code to match, I couldn't see any improvement in the numbers, although the armorstand vertices were a few pixels off the original circle, although I couldn't find a discernible pattern to this shift.
While this doesn't answer your full question, I'd like to point out two different ways you can solve the original issue at hand, no need to rely on some foreign math library:
^ ^ ^
Use Math, but let the game do it for you.
You can use the fact that the game is doing those rotational conversions for you already when using local coordinates. So, if you (or any entity) go to 0 0 0 and look / rotate in the angle that you want to calculate, then move forward by ^ ^ ^1, the position you're at now is basically <sin> 0 <cos>.
You can now take those numbers with your desired precision using data get and continue using them in whatever way you see fit.
Use recursive functions to move in incremenets
You point out in your question that
Going into the project I knew I could simply spin the centre entity, and summon an armorstand x blocks in front using ^5 ^ ^, however I wanted to avoid this, due to my desire to be able to change the radius without needing to edit the datapack. To solve this, I used the Sin and Cos components to plot a new point, using a radius defined with scoreboards.
So, to go back to that original idea, you could fairly easily (at least easier than trying to calculate the SIN/COS manually) find a solution that works for (almost) arbitrary radii and steps: By making the datapack configurable through e.g. scores, you can set it up to for example move forward by ^^^0.1 blocks for every point in a score, that way you can change that score to 50 to get a distance of ^^^5 and to 15 to get a distance of ^^^1.5.
Similarly you could set the "minimum" rotation between summons to be 0.1 degrees, then repeating said rotation for however many times you desire.
Both of these things can be achieved with recursive functions. Here is a quick example where you can control the rotational angle using the #rot steps score and the distance using the #dist steps score as described above (you might want to limit how often this runs with a score, too, like 360/rotation or whatever if you want to do one full circle). This example technically recurses twice, as I'm not using an entity to store the rotation. If there is an entity, you don't need to call the forward function from the rotate function but can call it from step (at the entity).
step.mcfunction
# copy scores over so we can use them
scoreboard players operation #rot_steps steps = #rot steps
scoreboard players operation #dist_steps steps = #dist steps
execute rotated ~ ~0.1 function foo:rotate
rotate.mcfunction
scoreboard players remove #rot_steps steps 1
execute if score #rot_steps matches ..0 positioned ^ ^ ^.1 run function foo:forward
execute if score #rot_steps matches 1.. rotated ~ ~0.1 run function foo:rotate
forward.mcfunction
scoreboard players remove #dist_steps steps 1
execute if score #dist_steps matches ..0 run summon armor_stand
execute if score #dist_steps matches 1.. positioned ^ ^ ^.1 run function foo:forward
I'm trying to achieve the ramp effect as seen here:
(source: splashdamage.com)
Blending the textures based on a distribution pattern is easy. Basically, just this (HLSL):
Result = lerp(SampleA, SampleB, DistributionPatternSample);
Which works, but without the ramp.
http://aaronm.nuclearglory.com/private/stackoverflow/result1.png
My first guess was that to incorporate "Ramp Factor" I could just do this:
Result = lerp(A, B, (1.0f - Ramp)*Distribution);
However, that does not work because if Ramp is also 1.0 the result would be zero, causing just 'A' to be used. This is what I get when Ramp is 1.0f with that method:
http://aaronm.nuclearglory.com/private/stackoverflow/result2.png
I've attempted to just multiply the ramp with the distribution, which is obviously incorrect. (Figured it's worth a shot to try and discover interesting effects. No interesting effect was discovered.)
I've also attempted subtracting the Ramp from the Distribution, like so:
Result = lerp(A, B, saturate(Distribution - Ramp));
But the issue with that is that the ramp is meant to control sharpness of the blend. So, that doesn't really do anything either.
I'm hoping someone can inform me what I need to do to accomplish this, mathematically. I'm trying to avoid branching because this is shader code. I can simulate branching by multiplying out results, but I'd prefer not to do this. I am also hoping someone can fill me in on why the math is formulated the way it is for the sharpness. Throwing around math without knowing how to use it can be troublesome.
For context, that top image was taken from here:
http://wiki.splashdamage.com/index.php/A_Simple_First_Megatexture
I understand how MegaTextures (the clip-map approach) and Virtual Texturing (the more advanced approach) work just fine. So I don't need any explanation on that. I'm just trying to implement this particular blend in a shader.
For reference, this is the distribution pattern texture I'm using.
http://aaronm.nuclearglory.com/private/stackoverflow/distribution.png
Their ramp width is essentially just a contrast change on the distribution map. A brute version of this is a simple rescaling and clamp.
Things we want to preserve are that 0.5 maps to 0.5, and that the texture goes from 0 to 1 over a region of width w.
This gives
x = 0.5 + (x-0.5)/w
This means the final HLSL will look something like this:
Result = lerp(A, B, clamp( 0.5 + (Distribution-0.5)/w, 0, 1) );
Now if this ends up looking jaggy at the edges you can switch to using a smoothstep. In shich case you'd get
Result = lerp(A, B, smoothstep( 0.5 + (Distribution-0.5)/w, 0, 1) );
However, one thing to keep in mind here is that this type of thresholding works best with smoothish distribution patters. I'm not sure if yours is going to be smooth enough (unless that is a small version of a mega texture in which case you're probabbly OK.)
I'm writing a solution for the Usaco problem "Electric Fences".
In the problem you have to find the optimal location for a point among a large amount of linesegments, so the sum of point-linesegment distances is smallest possible.
I had an idea, that it might be possible to do a hillclimb, and it worked for all testcases. The given analysis used a similar method, but it did not explain why this would work.
Thus I'm still unable to either prove or disprove the existence of local optimums in the given tasks. I had an idea that it could be done using induction, but I haven't been able to make it work. Can you help me?
Updated definition
Given a set of (x1,y1,x2,y2) linesegments find the (x,y) point P, that minimizes the function:
def Val(x,y):
d = 0
for x1,y1,x2,y2 in LineSegments:
if triangle (x1,y1,x2,y2,x,y) is not obtuse in (x1,y1) or (x2,y2):
d += DistPointToLine(x,y,x1,y1,x2,y2)
else:
d += min(DistPointToPoint(x,y,x1,y1), DistPointToPoint(x,y,x2,y2))
return d
By some reason the problem contains only one local optima, and thus the following procedure can be used to solve it:
precision = ((-0.1,0), (0.1,0), (0,-0.1), (0,0.1))
def Solve(precision=0.1):
x = 0; y = 0
best = Val(x,y)
while True:
for dx,dy in precision:
if Val(x+dx, y+dy) > best:
x += dx; y += dy
best = Val(x,y)
break
else:
break
return (x,y)
The questions is: Why does this not get stuck somewhere on the way to the global optimum? Why is there no local hilltops to bring this naive procedure to its knees?
It is easy to prove the algorithm's correctness if we notice that the distance function for a single line segment is a convex function. Convex in this case means that if we think of the distance function as a surface z=f(x,y), then if we filled in the volume above the surface, we'd have a convex solid. In the case of the distance from a single line segment, the solid would look like a triangular wedge with conical ends.
Since the sum of convex functions is also convex, then the sum of distances from multiple line segments will also be a convex function. Therefore, any local minimum you find must also be a global minimum by virtue of the function being convex.
See also: Why is my image rotation algorithm not working?
This question isn't language specific, and is a math problem. I will however use some C++ code to explain what I need as I'm not experienced with the mathematic equations needed to express the problem (but if you know about this, I’d be interested to learn).
Here's how the image is composed:
ImageMatrix image;
image[0][0][0] = 1;
image[0][1][0] = 2;
image[0][2][0] = 1;
image[1][0][0] = 0;
image[1][1][0] = 0;
image[1][2][0] = 0;
image[2][0][0] = -1;
image[2][1][0] = -2;
image[2][2][0] = -1;
Here's the prototype for the function I'm trying to create:
ImageMatrix rotateImage(ImageMatrix image, double angle);
I'd like to rotate only the first two indices (rows and columns) but not the channel.
The usual way to solve this is by doing it backwards. Instead of calculating where each pixel in the input image ends up in the output image, you calculate where each pixel in the output image is located in the input image (by rotationg the same amount in the other direction. This way you can be sure that all pixels in the output image will have a value.
output = new Image(input.size())
for each pixel in input:
{
p2 = rotate(pixel, -angle);
value = interpolate(input, p2)
output(pixel) = value
}
There are different ways to do interpolation. For the formula of rotation I think you should check https://en.wikipedia.org/wiki/Rotation_matrix#In_two_dimensions
But just to be nice, here it is (rotation of point (x,y) angle degrees/radians):
newX = cos(angle)*x - sin(angle)*y
newY = sin(angle)*x + cos(angle)*y
To rotate an image, you create 3 points:
A----B
|
|
C
and rotate that around A. To get the new rotated image you do this:
rotate ABC around A in 2D, so this is a single euler rotation
traverse in the rotated state from A to B. For every pixel you traverse also from left to right over the horizontal line in the original image. So if the image is an image of width 100, height 50, you'll traverse from A to B in 100 steps and from A to C in 50 steps, drawing 50 lines of 100 pixels in the area formed by ABC in their rotated state.
This might sound complicated but it's not. Please see this C# code I wrote some time ago:
rotoZoomer by me
When drawing, I alter the source pointers a bit to get a rubber-like effect, but if you disable that, you'll see the code rotates the image without problems. Of course, on some angles you'll get an image which looks slightly distorted. The sourcecode contains comments what's going on so you should be able to grab the math/logic behind it easily.
If you like Java better, I also have made a java version once, 14 or so years ago ;) ->
http://www.xs4all.nl/~perseus/zoom/zoom.java
Note there's another solution apart from rotation matrices, that doesn't loose image information through aliasing.
You can separate 2D image rotation into skews and scalings, which preserve the image quality.
Here's a simpler explanation
It seems like the example you've provided is some edge detection kernel. So if what you want to is detect edges of different angles you'd better choose some continuous function (which in your case might be a parametrized gaussian of x1 multiplied by x2) and then rotate it according to formulae provided by kigurai. As a result you would be able to produce a diskrete kernel more efficiently and without aliasing.
I have seen 3d surface plots of data before but i do not know what software i could use to make it.
I have 3 series of data (X, Y, Z) basically i want each of the rows on the table to be a point in 3d space, all joined as a mesh. The data is currently csv, but i can change the format, as it is data i generated myself.
Can anyone help
If your x & y points topologically lie on a grid, then you can use MESH. They don't need to have even spacing; they just need to be organized so that x(r:r+1,c:c+1) and y(r:r+1,c:c+1) define a quadrilateral on your mesh, for each row r and column c.
If your data do not lie on a grid, but you know what the faces should be, look at the PATCH function.
If you only have points and you don't know anything about the surface, you need to first solve the surface reconstruction problem. I've used cocone; there are other good packages there too. Once you have the reconstructed surface, then you can use PATCH to display it.
Have you looked at using vtk? If you have Matlab then you should be able to use plot3d or surf with meshgrid and griddata to generate 3D surface plots or patch as suggested by Mr. Fooz.
gnuplot or scilab
Below is a script for SciLab that I wrote awhile back. It reads in three columns separated by tabs. You can easily change this to fit your needs, pretty self-explanatory. Here is a quick guide to reading/writing in scilab and the one I reference below is here:
function plot_from_file(datafile)
//
// Make a simple x-y-z plot based on values read from a datafile.
// We assume that the datafile has three columns of floating-point
// values seperated by tabs.
// set verbose = 1 to see lots of diagnostics
verbose = 1;
// open the datafile (quit if we can't)
fid = mopen(datafile, 'r');
if (fid == -1)
error('cannot open datafile');
end
// loop over all lines in the file, reading them one at a time
num_lines = 0;
while (true)
// try to read the line ...
[num_read, val(1), val(2), val(3)] = mfscanf(fid, "%f\t%f\t%f");
if (num_read <= 0)
break
end
if (verbose > 0)
fprintf(1, 'num_lines %3d num_read %4d \n', num_lines, num_read);
end
if (num_read ~= 3)
error('didn''t read three points');
end
// okay, that line contained valid data. Store in arrays
num_lines = num_lines + 1;
x_array(num_lines) = val(1);
y_array(num_lines) = val(2);
z_array(num_lines) = val(3);
end
// now, make the plot
plot3d2(x_array, y_array, z_array);
// close the datafile
mclose(fid);
endfunction