Drawing concentric tiling circles with even diameter - algorithm

I need to draw circles using pixels with these constraints:
the total of pixels across the diameter is an even number,
there is no empty pixels between two circles of radius R and R+1 (R is an integer).
The midpoint algorithm can’t be used but I found out that Eric Andres wrote the exact thing I want. The algorithm can be found in this article under the name of “half integer centered circle”. For those who don’t have access to it, I put the interesting part is at the end of the question.
I encounter difficulties to implement the algorithm. I copied the algorithm in Processing using the Python syntax (for the ease of visualisation):
def half_integer_centered_circle(xc, yc, R):
x = 1
y = R
d = R
while y >= x:
point(xc + x, yc + y)
point(xc + x, yc - y + 1)
point(xc - x + 1, yc + y)
point(xc - x + 1, yc - y + 1)
point(xc + y, yc + x)
point(xc + y, yc - x + 1)
point(xc - y + 1, yc + x)
point(xc - y + 1, yc - x + 1)
if d > x:
d = d - x
x = x + 1
elif d < R + 1 - y:
d = d + y - 1
y = y - 1
else:
d = d + y - x - 1
x = x + 1
y = y - 1
The point() function just plot a pixel at the given coordinates. Please also note that in the article, x is initialised as S, which is strange because there is no S elsewhere (it’s not explained at all), however it is said that the circle begins at (x, y) = (1, R), so I wrote x = 1.
There is the result I get for a radii between 1 pixel and 20 pixels:
As you can see, there are holes between circles and the circle with R = 3 is different from the given example (see below). Also, the circles are not really round compared to what you get with the midpoint algorithm.
How can I get the correct result?
Original Eric Andres’ algorithm:

I don't understand the way in which the algorithm has been presented in that paper. As I read it the else if clause associated with case (b) doesn't have a preceding if. I get the same results as you when transcribing it as written
Looking at the text, rather than the pseudocode, the article seems to be suggesting an algorithm of the following form:
x = 1
y = R
while x is less than or equal to y:
draw(x, y)
# ...
if the pixel to the right has radius between R - 1/2 and R + 1/2:
move one pixel to the right
if the pixel below has radius between R - 1/2 and R + 1/2:
move one pixel down
else:
move one pixel diagonally down and right
Which seems plausible. In python:
#!/usr/bin/python3
import numpy as np
import matplotlib.pyplot as pp
fg = pp.figure()
ax = fg.add_subplot(111)
def point(x, y, c):
xx = [x - 1/2, x + 1/2, x + 1/2, x - 1/2, x - 1/2 ]
yy = [y - 1/2, y - 1/2, y + 1/2, y + 1/2, y - 1/2 ]
ax.plot(xx, yy, 'k-')
ax.fill_between(xx, yy, color=c, linewidth=0)
def half_integer_centered_circle(R, c):
x = 1
y = R
while y >= x:
point(x, y, c)
point(x, - y + 1, c)
point(- x + 1, y, c)
point(- x + 1, - y + 1, c)
point(y, x, c)
point(y, - x + 1, c)
point(- y + 1, x, c)
point(- y + 1, - x + 1, c)
def test(x, y):
rSqr = x**2 + y**2
return (R - 1/2)**2 < rSqr and rSqr < (R + 1/2)**2
if test(x + 1, y):
x += 1
elif test(x, y - 1):
y -= 1
else:
x += 1
y -= 1
for i in range(1, 5):
half_integer_centered_circle(2*i - 1, 'r')
half_integer_centered_circle(2*i, 'b')
pp.axis('equal')
pp.show()
This seems to work as intended. Note that I removed the circle centre for simplicity. It should be easy enough to add in again.
Edit Realised I could match the radius 3 image if I tweaked the logic a bit.

I have been looking into this matter and observed three issues in the original paper:
The arithmetic circle copied here (Figure 10.a in the paper) is not consistent with the formal definition of the "half integer centered circle". In one case the distance to the center must be between R-1/2 and R+1/2 and in the other between integer values. The consequence is that this specific algorithm, if properly implemented, can never generate the circle of Figure 10.a.
There is a mistake in one of the inequalities of the algorithm pseudo code: the test for case (b) should be d <= (R + 1 - y) instead of d < (R + 1 - y).
All those pixels that satisfy x==y have only 4-fold symmetry (not 8-fold) and are generated twice by the algorithm. Although producing duplicated pixels may not be a problem for a drawing routine, it is not acceptable for the application that I am interested in. However this can be easily fixed by adding a simple check of the x==y condition and skipping the four duplicated pixels.
The python code of the original question includes the inequality error mentioned above and an additional mistake due to missing parenthesis in one of the expressions that should read d = d + (y - x - 1).
The following implementation fixes all this and is compatible with python2 and python3 (no integer division issues in the point() function):
import numpy as np
import matplotlib.pyplot as pp
fg = pp.figure()
ax = fg.add_subplot(111)
def point(x, y, c):
xx = [x - 0.5, x + 0.5, x + 0.5, x - 0.5, x - 0.5 ]
yy = [y - 0.5, y - 0.5, y + 0.5, y + 0.5, y - 0.5 ]
ax.plot(xx, yy, 'k-')
ax.fill_between(xx, yy, color=c, linewidth=0)
def half_integer_centered_circle(R, c):
x = 1
y = R
d = R
while y >= x:
point(x, y, c)
point(x, - y + 1, c)
point(- x + 1, y, c)
point(- x + 1, - y + 1, c)
if y != x:
point(y, x, c)
point(y, - x + 1, c)
point(- y + 1, x, c)
point(- y + 1, - x + 1, c)
if d > x:
d = d - x
x = x + 1
elif d <= R + 1 - y:
d = d + y - 1
y = y - 1
else:
d = d + (y - x - 1)
x = x + 1
y = y - 1
for i in range(1, 5):
half_integer_centered_circle(2*i - 1, 'r')
half_integer_centered_circle(2*i, 'b')
pp.axis('equal')
pp.show()

Related

Is there a way to specifiy a Halide computation that operates on quartets of pixels?

In Halide, is there a way to split up an input image into 2x2 quartets of pixels and implement a unique computation in each pixel of the quartet?
For example, I want to implement the following computations for each pixel in the quartet:
Upper left: (x + 1, y) + (x - 1, y) + (x, y + 1) + (x, y - 1)
Upper right: (x + 1, y) + (x - 1, y)
Lower left: (x, y + 1) + (x, y - 1)
Lower right: (x - 1, y - 1) + (x + 1, y - 1) + (x - 1, y + 1) + (x + 1, y + 1)
And I want this computational pattern to extend across the entire input image.
There are a number of ways to do this. You can do it perhaps most directly using a select on x%2==0 and y%2==0. Something like:
// Sub-terms
Expr ul = in(x+1,y) + in(x-1,y) + in(x,y+1) + in(x,y-1);
Expr ur = in(x+1,y) + in(x-1,y);
Expr ll = in(x,y+1) + in(x,y-1);
Expr ul = in(x-1,y-1) + in(x+1,y-1) + in(x-1,y+1) + in(x+1,y+1);
Expr ix = x%2==0;
Expr iy = y%2==0;
out(x,y) = select(iy,
select(ix, ul, ur),
select(ix, ll, lr));
(There’s also a multi-condition version of select into which you could pack this.)
If you then unroll the x and y dimensions of out each by 2, you'll get a tight loop over quartets with no control flow:
out.unroll(x,2).unroll(y,2);
This is quite similar to the patterns you see in a demosaicing algorithm, of which you can find one here in the official Halide reference apps. Inspired by that, you may also find it natural to pack your data from 2D into 3D, with the 3rd dimension being the 4 elements of a quartet:
packed(x,y,c) = in(x+c%2, y+c/2);
which you may find easier to work with in some cases.

pow(X,Y,Z) <=> Z = X^Y with add

Would it be possible to do "pow" with "add" predicate (or just X is Y + Z )?
I make this:
pow(0,1,1).
pow(_,0,1).
pow(X,Y,Z) :- Y1 is Y - 1, pow(X,Y1,Z1), Z is Z1 * X.
But I want also make it with " + " (just for practise) like 3^2 = 3 * 3 = 3 + 3 + 3
You can write the multiplication (mul/3) in terms of addition. Like:
pow(0,1,1).
pow(_,0,1).
pow(X,Y,Z) :-
Y > 1,
Y1 is Y - 1,
pow(X,Y1,Z1),
mul(Z1,X,Z). %% originally: Z is Z1 * X.
mul(0,_,0).
mul(I,A,R) :-
I > 0,
I1 is I-1,
mul(I1,A,R1),
R is R1 + A.
Usually a basic exercise is to write addition, multiplication, and power predictates with the Peano number representation. In that case addition is written with the successor functor.

Invariant induction over horn-clauses with Z3py

I am currently using Z3py to to deduce some invariants which are encoded as a conjunction of horn-clauses whilst also providing a template for the invariant. I'm starting with a simple example first if you see the code snippet below.
x = 0;
while(x < 5){
x += 1
}
assert(x == 5)
This translates into the horn clauses
x = 0 => Inv(x)
x < 5 /\ Inv(x) => Inv(x +1)
Not( x < 5) /\ Inv(x) => x = 5
The invariant here is x <= 5.
I have provided a template for the invariant of the form a*x + b <= c
so that all the solver has to do is guess a set of values for a,b and c that can reduce to x <= 5.
However when I encode it up I keep getting unsat. If try to assert Not (x==5) I get a=2 , b = 1/8 and c = 2 which makes little sense to me as a counterexample.
I provide my code below and would be grateful for any help on correcting my encoding.
x = Real('x')
x_2 = Real('x_2')
a = Real('a')
b = Real('b')
c = Real('c')
s = Solver()
s.add(ForAll([x],And(
Implies(x == 0 , a*x + b <= c),
Implies(And(x_2 == x + 1, x < 5, a*x + b <= c), a*x_2 + b <= c),
Implies(And(a*x + b <= c, Not(x < 5)), x==5)
)))
if (s.check() == sat):
print(s.model())
Edit: it gets stranger for me. If I remove the x_2 definition and just replace x_2 with (x + 1) in the second horn clause as well as delete the x_2 = x_2 + 1, I get unsat whether I write Not( x==5) or x==5 in the final horn clause.
There were two things preventing your original encoding from working:
1) It's not possible to satisfy x_2 == x + 1 for all x for a single value of x_2. Thus, if you're going to write x_2 == x + 1, both x and x_2 need to be universally quantified.
2) Somewhat surprisingly, this problem is satisfiable in the integers but not in the reals. You can see the problem with the clause x < 5 /\ Inv(x) => Inv(x + 1). If x is an integer, then this is satisfied by x <= 5. However, if x is allowed to be any real value, then you could have x == 4.5, which satisfies both x < 5 and x <= 5, but not x + 1 <= 5, so Inv(x) = (x <= 5) does not satisfy this problem in the reals.
Also, you might find it helpful to define Inv(x), it cleans up the code quite a bit. Here is the encoding of your problem with those changes:
from z3 import *
# Changing these from 'Int' to 'Real' changes the problem from sat to unsat.
x = Int('x')
x_2 = Int('x_2')
a = Int('a')
b = Int('b')
c = Int('c')
def Inv(x):
return a*x + b <= c
s = Solver()
# I think this is the simplest encoding for your problem.
clause1 = Implies(x == 0 , Inv(x))
clause2 = Implies(And(x < 5, Inv(x)), Inv(x + 1))
clause3 = Implies(And(Inv(x), Not(x < 5)), x == 5)
s.add(ForAll([x], And(clause1, clause2, clause3)))
# Alternatively, if clause2 is specified with x_2, then x_2 needs to be
# universally quantified. Note the ForAll([x, x_2]...
#clause2 = Implies(And(x_2 == x + 1, x < 5, Inv(x)), Inv(x_2))
#s.add(ForAll([x, x_2], And(clause1, clause2, clause3)))
# Print result all the time, to avoid confusing unknown with unsat.
result = s.check()
print result
if (result == sat):
print(s.model())
One more thing: it's a bit strange to me to write a*x + b <= c as a template, because this is the same as a*x <= d for some integer d.

Finding major axis/image orientation of binary image in R

I have a high res binary image which looks something like:
I'm trying to compute the major axis which should be slightly rotated to the right and eventually get the axis of orientation of the object
A post here (in matlab) suggests a way of doing this is computing the covariance matrix for the datapoints and finding their eigenvalues/eigenvectors
I am trying to implement something similar in R
%% MATLAB CODE Calculate axis and draw
[M N] = size(Ibw);
[X Y] = meshgrid(1:N,1:M);
%Mass and mass center
m = sum(sum(Ibw));
x0 = sum(sum(Ibw.*X))/m;
y0 = sum(sum(Ibw.*Y))/m;
#R code
d = dim(im)
M = d[1]
N = d[2]
t = meshgrid(M,N)
X = t[[2]]
Y = t[[1]]
m = sum(im);
x0 = sum(im %*% X)/m;
y0 = sum(im %*% Y)/m;
meshgrid <-function(r,c){
return(list(R=matrix(rep(1:r, r), r, byrow=T),
C=matrix(rep(1:c, c), c)))
}
However, computing m , x0 and y0 takes too long in R.
Does anyone know of an implementation in R?
Computing the variance matrix directly, with var, takes 1/3 of a second.
# Sample data
M <- 2736
N <- 3648
im <- matrix( FALSE, M, N );
y <- as.vector(row(im))
x <- as.vector(col(im))
im[ abs( y - M/2 ) < M/3 & abs( x - N/2 ) < N/3 ] <- TRUE
#image(im)
theta <- runif(1, -pi/12, pi/12)
xy <- cbind(x+1-N/2,y+1-M/2) %*% matrix(c( cos(theta), sin(theta), -sin(theta), cos(theta) ), 2, 2)
#plot(xy[,1]+N/2-1, xy[,2]+M/2-1); abline(h=c(1,M),v=c(1,N))
f <- function(u, lower, upper) pmax(lower,pmin(round(u),upper))
im[] <- im[cbind( f(xy[,2] + M/2 - 1,1,M), f(xy[,1] + N/2 - 1,1,N) )]
image(1:N, 1:M, t(im), asp=1)
# Variance matrix of the points in the rectangle
i <- which(im)
V <- var(cbind( col(im)[i], row(im)[i] ))
# Their eigenvectors
u <- eigen(V)$vectors
abline( M/2-N/2*u[2,1]/u[1,1], u[2,1]/u[1,1], lwd=5 )
abline( M/2-N/2*u[2,2]/u[1,2], u[2,2]/u[1,2] )
Try replacing the default Rblas.dll with a suitable one from this link.

Given an increasing polynomial, how do you efficiently find x values for fixed intervals of y?

Problem: Given a polynomial of degree n (with coefficients a0 through an-1) that is guaranteed to be increasing from x = 0 to xmax, what is the most efficient algorithm to find the first m points with equally-spaced y values (i.e. yi - yi-1 == c, for all i)?
Example: If I want the spacing to be c = 1, and my polynomial is f(x) = x^2, then the first three points would be at y=1 (x=1), y=2 (x~=1.4142), and y=3 (x~=1.7321).
I'm not sure if it will be significant, but my specific problem involves the cube of a polynomial with given coefficients. My intuition tells me that the most efficient solution should be the same, but I'm not sure.
I'm encountering this working through the problems in the ACM's problem set for the 2012 World Finals (problem B), so this is mostly because I'm curious.
Edit: I'm not sure if this should go on the Math SE?
You can find an X for a given Y using a binary search. It's logarithmic time complexity, proportional to the size of the range of x values, divided by your error tolerance.
def solveForX(polyFunc, minX, maxX, y, epsilon):
midX = (minX + maxX) / 2.0
if abs(polyFunc(midX) - y) < epsilon:
return midX
if polyFunc(midX) > y:
return solveForX(polyFunc, minX, midX, y, epsilon)
else:
return solveForX(polyFunc, midX, maxX, y, epsilon)
print solveForX(lambda x: x*x, 0, 100, 2, 0.01)
output:
1.416015625
Edit: to expand on an idea in the comments, if you know you will be searching for multiple X values, it's possible to narrow down the [minX, maxX] search range.
def solveForManyXs(polyFunc, minX, maxX, ys, epsilon):
if len(ys) == 0:
return []
midIdx = len(ys) / 2
midY = ys[midIdx]
midX = solveForX(polyFunc, minX, maxX, midY, epsilon)
lowYs = ys[:midIdx]
highYs = ys[midIdx+1:]
return solveForManyXs(polyFunc, minX, midX, lowYs, epsilon) + \
[midX] + \
solveForManyXs(polyFunc, midX, maxX, highYs, epsilon)
ys = [1, 2, 3]
print solveForManyXs(lambda x: x*x, 0, 100, ys, 0.01)
output:
[1.0000884532928467, 1.41448974609375, 1.7318960977718234]

Resources