Algorithm for drawing profiles using long thin rectangles - algorithm

I want to draw profiles similar to the ones at https://steeldoor.org like:
programmatically. (Probably in FreeSCAD, but possibly Python).
I want to define a global thickness and then specify the profiles with a vector of directions and another one of lengths (specifically the lengths of the outer edges). The vectors for the second drawing in the example might be ['N', 'E', 'S', 'W', 'S', 'W', 'N'] and [10,100,80,20,70,80,10].
Given a primitive for drawing a rectangle starting at (x,y) with length l and width (corresponding to line thickness) t, say, rect(x,y,l,t), which would draw a horizontal rectangle to the right with l and t positive, is there a generic algorithm that would take the two vectors as input and draw the profile using the primitive? I can solve for a specific profile, but not for the general case.

Perhaps you need something like this approach.
I assume that Y axis is up, and base line (current coordinate x,y) goes through middle of thick lines (E-F-K at the picture), rectangle is defined by left-down corner (points A and H), width and height.
dirs = ['N', 'E', 'S', 'W', 'S', 'W', 'N']
lens = [10,100,80,20,70,80,10]
half_thickness = 3
x = 0
y = 0
for i in range(dirs):
if dirs == 'N':
rect(x-half_thickness, y, 2*half_thickness, lens[i])
y += lens[i]
elif dirs == 'S':
rect(x-half_thickness, y - lens[i], 2*half_thickness, lens[i])
y -= lens[i]
elif dirs == 'E':
rect(x, y-half_thickness, lens[i], 2*half_thickness)
x += lens[i]
else:
rect(x - lens[i], y-half_thickness, lens[i], 2*half_thickness)
x -= lens[i]

I ended up using the answer above (fixing a mistake in the else clause). The Python code below generates the OpenSCAD file that in turn produces the desired profile.
def rect(x,y,w,h,hh):
print("translate([", x,",",y,"])")
print("\tlinear_extrude(height=",hh,")")
print("\t\tsquare([", w,",",h,"]);")
dirs = ['W', 'S', 'E', 'S', 'E', 'N', 'E', 'N', 'E', 'N', 'W']
lens = [14,68,14,6,90,13,35,7,13,68,14]
thickness = 4
height=20
ht = thickness/2;
x = 0
y = 0
li=0;
for dir in dirs:
len = lens[li]
li += 1
if dir == 'N':
rect(x-ht, y, 2*ht, len, height)
y += len
elif dir == 'S':
rect(x-ht, y - len, 2*ht, len, height)
y -= len
elif dir == 'E':
rect(x, y-ht, len, 2*ht, height)
x += len
else:
rect(x - len, y-ht, len, 2*ht, height)
x -= len

Related

Showing result of template matching

I ran into another problem while making template matching in Halide (original link with resolved issue: output shifted in template matching)
Now I'm trying to draw a rectangle at the position with the lowest score (which indicates the best match).
template matching part:
Image<float> source = load_image("C:\\Users\\Admin\\Desktop\\templateMatchingOpenCV\\clip\\clip2.png");
Image<float> templ = load_image("C:\\Users\\Admin\\Desktop\\templateMatchingOpenCV\\clip\\object3.png");
Var x, y, xt, yt, x_outer, y_outer, x_inner, y_inner, tile_index;
RDom r(0, templ.width(), 0, templ.height());
Func limit, comparesqdiff, comparesqdiffnorm, compareccorr;
limit = BoundaryConditions::constant_exterior(source, 1.0f);
Expr function = sum(Halide::pow(templ(r.x, r.y) - limit(x + r.x, y + r.y), 2)) / (templ.width()*templ.height());
comparesqdiff(x, y) = sum(Halide::pow(templ(r.x, r.y) - limit(x + r.x, y + r.y), 2)) / (templ.width()*templ.height());
Image<float> outputsqdiff;
comparesqdiff.tile(x, y, x_outer, y_outer, x_inner, y_inner, 64,64).fuse(x_outer, y_outer, tile_index).vectorize(x_inner).unroll(y_inner).parallel(tile_index);
comparesqdiff.compile_jit();
Now it's clear to me that the function I should be using to find the position of the lowest score is argmin but i don't quite understand how it's used. Also I am aware that the drawing method would cover everything from that's below and right of the pixel but I didn't get to that part yet.
Drawing the rectangle part:
RDom wholeImage(0, source.width() - templ.width(), 0, source.height() - templ.height());
Tuple coords = argmin(r, function, "argmin");
Func show;
show(x, y) = select(x >= coords[0] && y >= coords[1] && x <= coords[0] + templ.width() && y <= coords[1] + templ.height(), 0, limit(x, y));
Image<float> test(source.width(), source.height());
test = show.realize(source.width(), source.height());
Thank you in advance.
The argmin reduction iterates over your RDom, compares the values and keeps the location of the lowest value and the value.
Halide::RDom matchDom
( 0, templ.width ()
, 0, templ.height()
);
Halide::RDom searchDom
( 0, source.width () - templ.width ()
, 0, source.height() - templ.height()
);
Halide::Expr score = Halide::sum
( matchDom
, Halide::pow
( templ( matchDom.x, matchDom.y )
- limit( searchDom.x + matchDom.x
, searchDom.y + matchDom.y)
, 2
)
)
/ ( templ.width() * templ.height() );
Halide::Tuple searchBest = Halide::argmin( searchDom, score );
Halide::Func best;
best(_) = searchBest(_);
Then you can call best.realize() to get a Halide::Realization. That realization will contain 3 buffers, each of a single value: the x coordinate of the lowest value, the y coordinate of the lowest value and the lowest value.
Halide is not the best tool for drawing geometric shapes. For my money, it would just be easier to write pixels into the image with a for loop.
ASKER'S EDIT: added marking the best result which uses the answer's method for finding best score
Realization re = best.realize();
Func draw("draw");
Image<int> x_coordinate(re[0]);
Image<int> y_coordinate(re[1]);
Image<float> s(re[2]);
draw(x,y) = select(x == x_coordinate(0, 0) || y == y_coordinate(0,0) || x == (x_coordinate(0,0) + templ.width()) || y == (y_coordinate(0,0) + templ.height()), 0.0f, source(x,y));
Image<float> drawTest;
drawTest = draw.realize(source.width(), source.height());
save_image(drawTest, path);
Example of the drawing:
Source:
Template:
Result:
ASKER'S EDIT2:
made it so it draws only the rectangle around the match
draw(x, y) = select((x == x_coordinate(0, 0) && (y >= y_coordinate(0, 0) && y <= y_coordinate(0, 0) + templ.height())) ||
((x == x_coordinate(0, 0) + templ.width()) && (y >= y_coordinate(0, 0) && y <= y_coordinate(0, 0) + templ.height())) ||
(y == y_coordinate(0, 0) && (x >= x_coordinate(0, 0) && x <= x_coordinate(0, 0) + templ.width())) ||
((y == y_coordinate(0, 0) + templ.height()) && (x >= x_coordinate(0, 0) && x <= x_coordinate(0, 0) + templ.width())), 0.0f, limit(x, y));
draw.tile(x, y, x_outer, y_outer, x_inner, y_inner, 64, 64).fuse(x_outer, y_outer, tile_index).vectorize(x_inner).unroll(y_inner).parallel(tile_index);
Result:

Calculate minimum distance in a straight line using BFS

Trying to solve this problem using BFS.
Problem Gist: Initial and final position is given for a rook placed in a matrix. You are required to find out minimum number of steps for rook to reach final position. Some position marked with "X" shouldn't be crossed, whereas "." are the allowable positions.
Matrix:
.X.
.X.
...
source position: 0,0
target position: 0,2
answer:3 (0,0) -> (2,0) - > (2,2) -> (0,2)
My solution basically does below:
I start doing BFS from source node and after I dequeue the node I am adding all the vertical and horizontal nodes in memory with the current distance of that node plus 1. After that I am checking if the destination node is present in memory and if it is present I am returning that distance.
This below solution is not working in some of the cases. Any suggestions?
def update_distance(memoize_dist, current, n, count, matrix):
directions = [1, -1, 1, -1]
current_x, current_y = current
temp_x, temp_y = current
for direction in directions:
while temp_x < n and temp_x >= 0:
temp_x += direction
temp = (temp_x, current_y)
if temp_x >= n or temp_x < 0 or matrix[temp_x][current_y] == 'X':
temp_x, temp_y = (current_x, current_y)
break
if temp not in memoize_dist.keys():
memoize_dist[temp] = count
for direction in directions:
while temp_y < n and temp_y >= 0:
temp_y += direction
temp = (current_x, temp_y)
if temp_y >= n or temp_y < 0 or matrix[current_x][temp_y] == 'X':
temp_x, temp_y = (current_x, current_y)
break
if temp not in memoize_dist.keys():
memoize_dist[temp] = count
def get_shortest(n, src, target, matrix):
queue, memoize, memoize_dist = [], {}, {}
queue.append((src[0], src[1], 0))
memoize_dist[(src[0], src[1])] = 0
while len(queue):
x, y, count = queue.pop(0)
cur = (x, y)
if cur in memoize.keys() and memoize[cur] != -1:
continue
memoize[cur] = 1
update_distance(memoize_dist, cur, n, count+1, matrix)
if target in memoize_dist.keys():
return memoize_dist[target]
directions = [1, -1, 1, -1]
for direction in directions:
if cur[0]+direction < n and cur[0]+direction >= 0 and matrix[cur[0]+direction][cur[1]] != 'X':
queue.append((cur[0]+direction, cur[1], memoize_dist[(cur[0]+direction, cur[1])]))
if cur[1]+direction < n and cur[1]+direction >= 0 and matrix[cur[0]][cur[1]+direction] != 'X':
queue.append((cur[0], cur[1]+direction, memoize_dist[(cur[0], cur[1]+direction)]))
n = int(input())
matrix = []
for i in range(n):
matrix.append(input())
start_x, start_y, dest_x, dest_y = map(int, input().split(" "))
print(get_shortest(n, (start_x, start_y), (dest_x, dest_y), matrix))

Getting pixel location given its value

is it possible in matlab to get the location of a pixel(rows and column) if the value at that pixel location is known?
Thanks in advance.
Regards
You can use find to get the coordinates of the pixel
[y x] = find( grayImg == val, 1 ); %// find one pixel that has intensity val
For RGB image, you need three values
[y x] = find( rgbImg(:,:,1) == r_val & rgbImg(:,:,2) == g_val & rgbImg(:,:,3) == b_val, 1 )
In case of single precision image, one might find the comparison == too strict (see, e.g. this thread). Therefore, a relaxed version can be applied:
thresh = 1e-5;
[row col] = find( abs( grayImg - val ) < thresh, 1 );
To find a pixel within thresh tolerance of val.
You may also try and find the pixel with value closest to val:
[~, lidx] = min( abs( grayImg(:) - val ) );
[row col] = ind2sub( size(grayImg), lidx );

Cartesian/combination algorithm (while maintaining order)

Since I don't quite know the language of these types of algorithms (i.e. how to google this), I'll just demonstrate what I'm looking for:
I have a three arrays (source arrays are of not equal lengths):
$array1 = array('A', 'B', 'C', 'D');
$array2 = array('x', 'y', 'z');
$array3 = array('1', '2', '3');
I would like all possible combinations of these arrays where:
No more than one element from each source array is taken.
The order of array1, array2, array3 is never broken (ABC always comes before xyz always comes before 123).
So the result would be:
array(
array('A', 'x', '1'),
array('A', 'x', '2'),
array('A', 'x', '3'),
array('A', 'y', '1'),
// etc ...
// But I also need all the partial sets, as long as the rule about
// ordering isn't broken i.e.:
array('B'),
array('B', 'x'),
array('B', 'x', '1'),
array('x'),
array('x', '1'),
array('1'),
);
The order of the results doesn't matter to me.
Working in php, but similar language or pseudo code is fine of course. Or I'd just take a tip on what specific types of permutation/combination algorithms I should be looking at.
I'd say these are Cartesian products. Generating them is quite easy.
for fixed number of arrays (in Perl):
for my $a(#arrayA) {
for my $b(#arrayB) {
push #result, [$a, $b];
}
}
general procedure: Assume #partial is an array for Cartesian product of A1 x A2 x ... x An and we want A1 x ... x An x An+1
for my $a(#partial) {
for my $b(#An_plus_1) {
push #result, [#$a, $b];
}
}
This would obviously need to iterate over all the arrays.
Now, that you want also to omit some of the elements in the sets, you just twist it a little. In the first method, you can just add another element to each of the arrays (undef is obvious choice, but anything will do) and then filter out these elements in the result sets. In the second method, it is even easier: You just add #partial and map { [$_] } #An_plus_1 to the result (or, in English, all the sets resulting from the partial Cartesian product of A1 x ... x An plus the single element sets made form the elements of the new set).
With RBarryYoung's hint, this is the shortest way to produce them, bash (and sed, to remove D, w, and 4):
echo {A..D}{w..z}{1..4} | sed 's/[Dw4]//g'
A1 A2 A3 A Ax1 Ax2 Ax3 Ax Ay1 Ay2 Ay3 Ay Az1 Az2 Az3 Az
B1 B2 B3 B Bx1 Bx2 Bx3 Bx By1 By2 By3 By Bz1 Bz2 Bz3 Bz
C1 C2 C3 C Cx1 Cx2 Cx3 Cx Cy1 Cy2 Cy3 Cy Cz1 Cz2 Cz3 Cz
1 2 3 x1 x2 x3 x y1 y2 y3 y z1 z2 z3 z
Another, easy way, is SQL, which does it by default:
SELECT upper, lower, num
FROM uppers, lowers, numbers
WHERE upper in ('A', 'B', 'C', ' ')
AND lower in (' ', 'x', 'y', 'z')
AND (number in (1, 2, 3) OR number IS NULL);
If your tables only contain 'A,B,C, ,' and 'x,y,z, ,' and '1,2,3, ' it is much shorter:
SELECT upper, lower, num
FROM uppers, lowers, numbers;
Another word, beside cartesian product, for this combinations is cross product.
For an unknown number of unknown size of Lists/Sequences/other collections, I would recommend an Iterator - if PHP has such things. Here is an implementation in Scala:
class CartesianIterator (val ll: Seq[Seq[_]]) extends Iterator [Seq[_]] {
var current = 0
def size = ll.map (_.size).product
lazy val last: Int = len
def get (n: Int, lili: Seq[Seq[_]]): List[_] = lili.length match {
case 0 => List ()
case _ => {
val inner = lili.head
inner (n % inner.size) :: get (n / inner.size, lili.tail)
}
}
override def hasNext () : Boolean = current != last
override def next (): Seq[_] = {
current += 1
get (current - 1, ll)
}
}
val ci = new CartesianIterator (List(List ('A', 'B', 'C', 'D', ' '), List ('x', 'y', 'z', ' '), List (1, 2, 3, 0)))
for (c <- ci) println (c)
List(A, x, 1)
List(B, x, 1)
List(C, x, 1)
List(D, x, 1)
List( , x, 1)
List(A, y, 1)
List(B, y, 1)
...
List( , z, 0)
List(A, , 0)
List(B, , 0)
List(C, , 0)
List(D, , 0)
List( , , 0)
A wrapper could be used to remove the '0' and ' ' from the output.

Sparse Matrix: ValueError: matrix type must be 'f', 'd', 'F', or 'D'

I want to do SVD on a sparse matrix by using scipy:
from svd import compute_svd
print("The size of raw matrix: "+str(len(raw_matrix))+" * "+str(len(raw_matrix[0])))
from scipy.sparse import dok_matrix
dok = dok_matrix(raw_matrix)
matrix = compute_svd( dok )
The function compute_svd is my customized module like this:
def compute_svd( matrix ):
from scipy.sparse import linalg
from scipy import dot, mat
# e.g., matrix = [[2,1,0,0], [4,3,0,0]]
# matrix = mat( matrix );
# print "Original matrix:"
# print matrix
U, s, V = linalg.svds( matrix )
print "U:"
print U
print "sigma:"
print s
print "VT:"
print V
dimensions = 1
rows,cols = matrix.shape
#Dimension reduction, build SIGMA'
for index in xrange(dimensions, rows):
s[index]=0
print "reduced sigma:"
print s
#Reconstruct MATRIX'
# from scipy import dot
reconstructedMatrix= dot(dot(U,linalg.diagsvd(s,len(matrix),len(V))),V)
#Print transform
print "reconstructed:"
print reconstructedMatrix
return reconstructedMatrix
I get an exception:
Traceback (most recent call last):
File "D:\workspace\PyQuEST\src\Practice\baseline_lsi.py", line 96, in <module>
matrix = compute_svd( dok )
File "D:\workspace\PyQuEST\src\Practice\svd.py", line 13, in compute_svd
U, s, V = linalg.svds( matrix )
File "D:\Program\Python26\lib\site-packages\scipy\sparse\linalg\eigen\arpack\arpack.py", line 1596, in svds
eigvals, eigvec = eigensolver(XH_X, k=k, tol=tol ** 2)
File "D:\Program\Python26\lib\site-packages\scipy\sparse\linalg\eigen\arpack\arpack.py", line 1541, in eigsh
ncv, v0, maxiter, which, tol)
File "D:\Program\Python26\lib\site-packages\scipy\sparse\linalg\eigen\arpack\arpack.py", line 519, in __init__
ncv, v0, maxiter, which, tol)
File "D:\Program\Python26\lib\site-packages\scipy\sparse\linalg\eigen\arpack\arpack.py", line 326, in __init__
raise ValueError("matrix type must be 'f', 'd', 'F', or 'D'")
ValueError: matrix type must be 'f', 'd', 'F', or 'D'
This is my first time to do this. How should I fix it? Any ideas? Thank you!
Adding to Anycorn's answer, yes you need to upcast your matrix to float or double. This can be done using the function:
asfptype() from scipy.sparse.coo_matrix
Add this line to upcast it before you call linalg.svds:
matrix = matrix.asfptype()
U, s, V = linalg.svds( matrix )
you have to use float or doubles. you seem to be using unsupported matrix type DOK of ints?.
sparse svd: http://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.linalg.svds.html
ValueError: matrix type must be 'f', 'd', 'F', or 'D'
This error can be removed by changing Datatype from int to float like this: matrix = matrix.astype(float)
...then this will work

Resources