In [1], a greyscale picture is approximated by strings (lines) between points around the image. Here, I will try something similar with a formal optimization model.
A greyscale image can be represented by an m×n matrix of floating point values between zero and one. A value of zero indicates a black pixel, and a value of one a white pixel. In the discussion below, when I use the term color, this means the greyscale value.
Here is a 160×120 image. The pixels have values between 0 and 0.97. Let's call this matrix GreyScalei,j∈[0,1]. Click on the picture to enlarge it a bit.
In my experiments, I just generate N=150 random points inside the image and draw possible lines between them. (The advantage of using mathematical models is that I am not bound to some physical reality: no physical pieces of strings are needed). The pixels that are covered by the lines we draw get a greyscale equal to the sum of the greyscales of the lines. (We assume additivity here: if a pixel is covered by several lines, we add up the contribution of each line. I think this may not be the same for real-world strings.) Here is a picture of the points and possible lines:
N=150 randomly placed points |
All connecting lines |
With N=150 points, we can draw M=N(N+1)2=11,325 lines. This includes lines from a point to itself, i.e. singleton points. I am not sure if adding or discarding these N singletons makes a difference.
Integer Optimization Model |
---|
min∑i,j(∑kγ⋅LinePixelsk,i,j⋅xk−GreyScalei,j)2xk∈{0,1} |
- By adding up the greyscale levels for pixels covered by multiple lines, we can easily end up with a total greyscale value exceeding 1. We never can go below 0, so this is a bit asymmetric.
- We can skip pixels that are not covered by any line. This means using a subset of (i,j).
- Large MIQPs are difficult to solve to optimality. This is no exception. Whether we use some linearization or solve as a quadratic model, this is not very easy.
- Solvers like Cplex may linearize this automatically. This involves forming all cross-products, and then linearize xk1⋅xk2. This generates a ton of extra binary variables. In some cases, as a result, Cplex was running out of memory.
- Sometimes it is better to write: min∑i,je2i,jei,j=∑kγ⋅LinePixelsk,i,j⋅xk−GreyScalei,j This makes the quadratic part (numerically) easier.
- Using a LAD (Least Absolute Deviation) objective also gives us a linear model: min∑i,j(e+i,j+e−i,j)e+i,j−e−i,j=∑kγ⋅LinePixelsk,i,j⋅xk−GreyScalei,je+i,j,e−i,j≥0 Note that the automatic linearization discussed earlier, is exact. It will give the same solutions as the quadratic problem. Here, we change the problem (from a 2-norm to a 1-norm objective), so solutions will be somewhat different.
- We may not want to have pixel values above 1. An alternative objective could be: min∑i,j(min{1,∑kγ⋅LinePixelsk,i,j⋅xk}−GreyScalei,j)2 However, this complicates the model considerably. Something like y=min(a,x) requires a binary variable δ∈{0,1}: y≤ay≤xy≥a−M⋅δy≥x−M⋅(1−δ) unless there is something we can exploit. I don't see how to do this much smarter. If no good bounds are available to determine M, we can use a SOS1 set.
- Here, we used that colors are additive. A different approach could be to make the observed color the maximum of the underlying line colors. I.e. min∑i,j(maxk{γ⋅LinePixelsk,i,j⋅xk}−GreyScalei,j)2 This again (for the same reasons as the previous bullet) is making the model much more difficult.
- A possible extension is to make γ a variable: let the model decide the best color for all selected lines. Note that γ is not indexed by k here. So, we have a single color for all lines. Below, I discuss the case where we allow γk. I.e., a different color for each line. That can be combined with xk.
- In the discussion about the mathematics, [1] drops the integrality conditions and also the bounds on xk. Removing the bounds does not make much sense to me. Also, in [1], lines are thicker and use some kind of anti-aliasing scheme. I use very thin lines without anti-aliasing.
Continuous Optimization Model |
---|
min∑i,j(∑kLinePixelsk,i,j⋅xk−GreyScalei,j)2xk∈[0,1] |
All pixels in the solution with a color value larger than one were reset to 1.0. I.e. the plotted pixels have a value: vi,j:=min{1,∑kLinePixelsk,i,j⋅x∗k}
Einstein's wild hair is certainly a problem for this model. But we can somewhat recognize his face.
Conclusion
References
- The Mathematics of String Art, https://www.youtube.com/watch?v=WGccIFf6MF8
Appendix: GAMS Model
*---------------------------------------------------------------------------- * data *----------------------------------------------------------------------------
$include data5.inc
$ontext
Summary:
data5.inc pixels: (240, 180) nodes: 150 lines: 11325
sets i 'rows' j 'columns' k 'nodes' lines(k,k) 'line node->node' linepixels(k,k,i,j) 'pixels affected by line k1->k2' parameter greyscale(i,j) 'pixel values of greyscale image'
$offtext
set pixels(i,j) /#i.#j/;
parameter sizes(*); sizes('lines') = card(lines); sizes('linepixels') = card(linepixels); display sizes;
*---------------------------------------------------------------------------- * pixels not covered by any line? *----------------------------------------------------------------------------
sets covpix(i,j) 'pixels covered by some line' noncovpix(i,j) 'pixels not covered by any line' ;
alias (k,k1,k2); covpix(i,j) = sum(linepixels(k1,k2,i,j),1); noncovpix(i,j) = not covpix(i,j);
sizes('covpix') = card(covpix); sizes('noncovpix') = card(noncovpix); display sizes;
*---------------------------------------------------------------------------- * model m: unconstrained QP *----------------------------------------------------------------------------
scalar gamma 'color factor' /1.0/;
option miqcp=cplex,rmiqcp=cplex,limrow=0,limcol=0,threads=8;
variable z 'objective'; binary variable x(k,k) 'select lines';
equation obj; obj.. z =e= sum(covpix, sqr(gamma*sum(lines,linepixels(lines,covpix)*x(lines)) - greyscale(covpix)));
model m /obj/; solve m minimizing z using rmiqcp; |
No comments:
Post a Comment