GeoML is a Java library for constrained geometry, it allows to describe a geometric model in XML, as an example an ellips can be described as follows:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE model SYSTEM "GeoML-v01.dtd"> <model name="ellipse"> <!-- Free parameters --> <number name="A" value="1"/> <number name="B" value="2"/> <number name="phi" value="0"/> <number name="dx" value="0"/> <number name="dy" value="0"/>
<!-- The center: --> <point name="c" x="0" y="0" visible="false"/>
<!-- The circle: --> <circle name="ellipse" center="c" radius="1"/>
<!-- The transformation: --> <rototranslation name="rototransl" scalex="A" scaley="B" x="dx" y="dy" angle="phi"/> <transform name="ellipse" transformation="rototransl"/> </model>
In a GoML model there are: parameter elements: numbers ad plain numbers, ranges, etc. geometrical elements: points, lines, circles, curves, etc. computation elements: transformations, equations, ecc. each element has a set of attributes describing how it must be created. For a better documentation see libs/ActiveTemplates/doc... or read the source code libs/ActiveTemplates/src ;)
Once a model is defined, put it in a reachable directory (with the DTD GeoML-v01.dtd) and use it by the given Matlab functions "GeoML*".
A geometric model can be used to generate curves and to discretize them in sets of points, these points can be used to compute an energy function on an image; the minimization of the energy function depending on the values of the parameters (the number elements) morphs the model adapting it to the objects in the image. Let's try it!!!
Note.. that a simple model viewer and "deformer" can be found in "lib/ActiveTemplates" in "src" and "classes" as the class "com.dsi.libs.geoml.terminals.GeoMLModelViewer". For unix/linux users, in the directory "lib" a BASH executable script "GeoMLViewer.sh" can be found, for Windows users the file is "GeoMLViewer.bat".
To let the code work properly, take the files "*.jar" placed in the directory "lib", and put them in a reachable directory (as an example "/usr/java/lib"), type in Matlab:
> edit classpath.txt
and add the references to the libs:
... /usr/java/lib/jep.jar /usr/java/lib/djep.jar /usr/java/lib/Jama.jar /usr/java/lib/GeoML.jar
then restart Matlab so that it can load the listed Java libs.
The following code adds to the matlab path the M-Files directories only if required.
% Olny if not jet done: if exist('GeoMLParseModel','file')~=2 % Load the 'mtools' M-Files: addpath(genpath('mtools')); end
At first, load a GeoML model using "GeoMLParseModel". This gives you two things: the GeoML model as a Java instance of the class "com.dsi.libs.geoml.Group", and a set of free variables in an array and in a struct where the fields have the same names of the variables.
% Loading a model: [model,free,freeMap] = GeoMLParseModel('spirals'); % The free variables: disp(fields(freeMap)');
'x1' 'y1' 'x2' 'y2' 'x3' 'y3' 'x4' 'y4' 'dist'
The model is useful only if points can be generated on an image, to do that the function "GeoMLIterate" is provided.
% Iterating on the model to generate the points: pts = GeoMLIterate(model); % Take a look! figure; plotpoints(pts,'r.');
The model is useful only if it can be morphed by changing the free parameters and updating the whole geometry (including all levels of dependency of the gemometrical elements one from each other). This can be done using the "setValueUpdate" on the variables: this method changes the value and then updates the whole model. Another possibility is to use the method "setValue" for all the variables that must be updated, and then to call "model.update" on the model to force the updating. These two solutions are, from the computational complexity point of view, the same. In the example the two parameters "true,'real'" are passed to "GeoMLIterate". The first one say that the obtained points must be joined in a single sequence (true), in fact it is possible to split them among the geometrical structure of th model (the Group class instances). The second one say that the discretization of points must generate the real curves' coordinates, not the integer approximations generated to fit the model in the pixels discretization of the image, this can change the number of generated points.
% Changing the free variables: freeMap.x4.setValueUpdate(20); freeMap.y4.setValueUpdate(-20); % Take a look! pts = GeoMLIterate(model,true,'real'); figure; plotshape(pts,false,'r');
Let's work on model fitting: at first a test image is synthetized by loading the model, setting the parameters, generating the points, setting the corresponding pixels to white, filling the ellipse, and then adding noise.
% Loading: img = zeros(256,256); [model,free,freeMap] = GeoMLParseModel('ellipse'); % Real image synthesis: freeMap.dx.setValue(125); freeMap.dy.setValue(125); freeMap.A.setValue(100); freeMap.B.setValue(50); freeMap.phi.setValue(1); model.update; pts = GeoMLIterate(model); ptsIdx = sub2ind(size(img),pts(2,:),pts(1,:)); img(ptsIdx) = 1; img = bwfill(bwmorph(bwmorph(img,'dilate'),'thin',Inf),'holes'); img = imscale(gaussianFilter(img + randn(size(img))*0.1),[0.6,0.8]); % Take a look! figure; imshow(img);
Let's set the initial parameters of the model to an acceptable initial value, they can be obtained by image processing techniques, its only a rough initialization that allows the optimization process to start from a "good" position in the energy function.
% Setting parameters freeMap.dx.setValue(120); freeMap.dy.setValue(120); freeMap.A.setValue(90); freeMap.B.setValue(60); freeMap.phi.setValue(1.1); model.update; % Take a look! pts = GeoMLIterate(model,true,'real'); figure; imshow(img); hold on; plotshape(pts,false,'r');
The optimization process is based on an optimization algorithm (by default fminsearch) and an energy function (here ATE_GradientNorm based on the gradient's norm of the given image). In this example an iterative optimization process is used with a little bit of randomizzation to prevent to be stuch in local minima of the energy function, this is only a rough algorithm, choosing carefully the optimization algorithm may be better (from the performance point of view).
% Iterating on optimizations: for i=1:10 % A random noise in the optimization process prevents to be stuch in % local minima positions of the energy function: freeMap.dx.setValueUpdate(freeMap.dx.getValue+randn); freeMap.dy.setValueUpdate(freeMap.dx.getValue+randn); % Optimizing: opt = ATOptimize(img,model,free,@ATE_GradientNorm); end % Take a look! pts = GeoMLIterate(model,true,'real'); figure; imshow(img); hold on; plotshape(pts,false,'r');
Very complex models can be defined with equations, transformations as R2->R2 maps, geometrical constructions, locuses, procedures, recursion and more!
% A very well-known fractal, the Koch's snowflake: model = GeoMLParseModel('fractal'); pts = GeoMLIterate(model,true,'real'); figure; plotshape(pts,false,'r'); axis equal;