In the last few days, I’ve been thinking a lot about how I could introduce a new function, as “generic” as possible, in the G’MIC mathematical expression evaluator, to do vector-to-vector transformation, including the stuffs that we don’t really have, such as:
- Value or color mapping (like what command
mapdoes). - Image warping, when the vector is seen as an image (like what command
warpdoes). - Permutation of image axes (somehow like what command
permutedoes).
Reimplementing all commands permute, map and warp directly in the math parser seemed a bit overkill to me, so I tried to figure out what generic function could do all this at once (even if there are still very specific things that can’t be done).
I ended up implementing the function:
map(X,P,_nb_channelsX,_nb_channelsP,_boundary_conditions)
which is simple enough to use and does indeed allow very generic transformations on vectors.
Basically, this function does the same as command map:
It considers that vector X is an input image of size (size(X)/nb_channelsX,1,1,nb_channelsX), and P is a palette, i.e., another image of size (size(P)/nb_channelsP,1,1,nb_channelsP), and it returns an image Y of size (size(X)/nb_channelsX,1,1,nb_channelsX*nb_channelsP) such that
Y[k] = P[X[x]], for each channel of Y.
Let me show how map() can handles the three mentioned cases:
- Value or color mapping.
Obviously, map() can be used just as the map command. This is the simplest example:
map_for_map :
sp david,256 luminance n 0,255
palette hot
eval "
X = crop(#0);
P = crop(#1);
Y = map(X,P,1,3);
store('out',Y,w#0,h#0,1,s#1);
"
$out
map() works exactly the same as command map, so there are no limitations of using map() compared the corresponding command.
- Image warping:
Amusingly, it turns out that you can also use map() to warp images as well.
The idea is to convert the warping map into an offset map, which will be the X image that we send to map():
map_for_warp :
sp david,256 # [0]: Color image to warp
100%,100%,1,2 rand. -1,1 b. 10%,0 n. -30,30 +. '[x,y]' # [1]: Random 2D warping map
+warp[0] [1] # [2]: Result using 'warp' command.
# Apply warp in math evaluator, using `map()` command:
eval "
P = crop(#0);
# Convert warp map into offset map.
X = round(crop(#1,0,0,0,1,w#1,h#1,d#1,1))*w#0 + round(crop(#1,0,0,0,0,w#1,h#1,d#1,1));
Y = map(X,P,1,3);
store('out',Y,w#0,h#0,1,s#0)"
$out
Here, there are some limitations of using map() compared to the warp command:
- Interpolation is restricted to the
nearest neighborsmode. - boundary conditions works a bit differently, as it is just handled for vector indices that go outside
[0,size(P)], while commandwarpis a bit smarter for managing boundary conditions.
- Permutation of images axes:
Same idea as for warp here, we define an offset map corresponding to the desired permutation, and we are ready to go with map().
map_for_permute :
sp david,256
{[h,w]},1,1,"y + x*w#0 + c*wh#0" # Define offset map for permutation (invert x/y axes)
eval "
P = crop(#0);
X = crop(#1);
Y = map(X,P,1,3);
store('out',Y,w#1,h#1,1,s#0)"
$out
At this point, I can say that map() is clearly a great addition to the G’MIC math evaluator!
It doesn’t require zillions of parameters, and it is capable of doing quite complicated things on vectors. Of course, it’s still more convenient to use the classical commands map, warp and permute, but if you really need to perform such actions inside the math evaluator, you won’t have to write some slow loops to do so.


