blob: 1cf2e407d004af46dea5bf88677cc95454f05372 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% M M AAA TTTTT RRRR IIIII X X %
7% MM MM A A T R R I X X %
8% M M M AAAAA T RRRR I X %
9% M M A A T R R I X X %
10% M M A A T R R IIIII X X %
11% %
12% %
13% MagickCore Matrix Methods %
14% %
15% Software Design %
16% John Cristy %
17% August 2007 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/matrix.h"
cristyd1dd6e42011-09-04 01:46:08 +000044#include "MagickCore/matrix-private.h"
cristy4c08aed2011-07-01 19:47:50 +000045#include "MagickCore/memory_.h"
cristy3ed852e2009-09-05 21:47:34 +000046
47/*
48%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
49% %
50% %
51% %
52% A c q u i r e M a g i c k M a t r i x %
53% %
54% %
55% %
56%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
57%
58% AcquireMagickMatrix() allocates and returns a matrix in the form of an
59% array of pointers to an array of doubles, with all values pre-set to zero.
60%
anthonyfd706f92012-01-19 04:22:02 +000061% This used to generate the two dimensional matrix, that can be referenced
62% using the simple C-code of the form "matrix[y][x]".
63%
64% This matrix is typically used for perform for the GaussJordanElimination()
65% method below, solving some system of simultanious equations.
cristy3ed852e2009-09-05 21:47:34 +000066%
67% The format of the AcquireMagickMatrix method is:
68%
cristybb503372010-05-27 20:51:26 +000069% double **AcquireMagickMatrix(const size_t number_rows,
70% const size_t size)
cristy3ed852e2009-09-05 21:47:34 +000071%
72% A description of each parameter follows:
73%
cristy74908cf2010-05-17 19:43:54 +000074% o number_rows: the number pointers for the array of pointers
cristy1ad491d2010-05-17 19:45:27 +000075% (first dimension).
cristy3ed852e2009-09-05 21:47:34 +000076%
cristy1ad491d2010-05-17 19:45:27 +000077% o size: the size of the array of doubles each pointer points to
78% (second dimension).
cristy3ed852e2009-09-05 21:47:34 +000079%
80*/
cristybb503372010-05-27 20:51:26 +000081MagickExport double **AcquireMagickMatrix(const size_t number_rows,
82 const size_t size)
cristy3ed852e2009-09-05 21:47:34 +000083{
84 double
cristy74908cf2010-05-17 19:43:54 +000085 **matrix;
cristy3ed852e2009-09-05 21:47:34 +000086
cristybb503372010-05-27 20:51:26 +000087 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000088 i,
89 j;
90
cristy74908cf2010-05-17 19:43:54 +000091 matrix=(double **) AcquireQuantumMemory(number_rows,sizeof(*matrix));
cristy3ed852e2009-09-05 21:47:34 +000092 if (matrix == (double **) NULL)
cristy26295322011-09-22 00:50:36 +000093 return((double **) NULL);
cristybb503372010-05-27 20:51:26 +000094 for (i=0; i < (ssize_t) number_rows; i++)
cristy3ed852e2009-09-05 21:47:34 +000095 {
96 matrix[i]=(double *) AcquireQuantumMemory(size,sizeof(*matrix[i]));
97 if (matrix[i] == (double *) NULL)
98 {
99 for (j=0; j < i; j++)
100 matrix[j]=(double *) RelinquishMagickMemory(matrix[j]);
101 matrix=(double **) RelinquishMagickMemory(matrix);
102 return((double **) NULL);
103 }
cristybb503372010-05-27 20:51:26 +0000104 for (j=0; j < (ssize_t) size; j++)
cristy74908cf2010-05-17 19:43:54 +0000105 matrix[i][j]=0.0;
cristy3ed852e2009-09-05 21:47:34 +0000106 }
107 return(matrix);
108}
109
110/*
111%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
112% %
113% %
114% %
115% G a u s s J o r d a n E l i m i n a t i o n %
116% %
117% %
118% %
119%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
120%
121% GaussJordanElimination() returns a matrix in reduced row echelon form,
122% while simultaneously reducing and thus solving the augumented results
123% matrix.
124%
125% See also http://en.wikipedia.org/wiki/Gauss-Jordan_elimination
126%
127% The format of the GaussJordanElimination method is:
128%
cristy26295322011-09-22 00:50:36 +0000129% MagickBooleanType GaussJordanElimination(double **matrix,
130% double **vectors,const size_t rank,const size_t number_vectors)
cristy3ed852e2009-09-05 21:47:34 +0000131%
132% A description of each parameter follows:
133%
134% o matrix: the matrix to be reduced, as an 'array of row pointers'.
135%
136% o vectors: the additional matrix argumenting the matrix for row reduction.
137% Producing an 'array of column vectors'.
138%
anthonyfd706f92012-01-19 04:22:02 +0000139% o rank: The size of the square matrix (both rows and columns).
cristy3ed852e2009-09-05 21:47:34 +0000140% Also represents the number terms that need to be solved.
141%
cristy74908cf2010-05-17 19:43:54 +0000142% o number_vectors: Number of vectors columns, argumenting the above matrix.
cristy3ed852e2009-09-05 21:47:34 +0000143% Usally 1, but can be more for more complex equation solving.
144%
145% Note that the 'matrix' is given as a 'array of row pointers' of rank size.
146% That is values can be assigned as matrix[row][column] where 'row' is
147% typically the equation, and 'column' is the term of the equation.
148% That is the matrix is in the form of a 'row first array'.
149%
150% However 'vectors' is a 'array of column pointers' which can have any number
151% of columns, with each column array the same 'rank' size as 'matrix'.
anthonyfd706f92012-01-19 04:22:02 +0000152% It is assigned vector[column][row] where 'column' is the specific
153% 'result' and 'row' is the 'values' for that answer. After processing
154% the same vector array contains the 'weights' (answers) for each of the
155% 'separatable' results.
cristy3ed852e2009-09-05 21:47:34 +0000156%
157% This allows for simpler handling of the results, especially is only one
anthonyfd706f92012-01-19 04:22:02 +0000158% column 'vector' is all that is required to produce the desired solution
159% for that specific set of equations.
cristy3ed852e2009-09-05 21:47:34 +0000160%
161% For example, the 'vectors' can consist of a pointer to a simple array of
162% doubles. when only one set of simultanious equations is to be solved from
163% the given set of coefficient weighted terms.
164%
165% double **matrix = AcquireMagickMatrix(8UL,8UL);
166% double coefficents[8];
167% ...
168% GaussJordanElimination(matrix, &coefficents, 8UL, 1UL);
anthony34364f42011-10-21 05:31:53 +0000169%
anthonyb16e1432011-10-21 05:06:56 +0000170% However by specifing more 'columns' (as an 'array of vector columns'),
171% you can use this function to solve multiple sets of 'separable' equations.
cristy3ed852e2009-09-05 21:47:34 +0000172%
173% For example a distortion function where u = U(x,y) v = V(x,y)
174% And the functions U() and V() have separate coefficents, but are being
175% generated from a common x,y->u,v data set.
176%
177% Another example is generation of a color gradient from a set of colors
178% at specific coordients, such as a list x,y -> r,g,b,a
anthonyfd706f92012-01-19 04:22:02 +0000179%
180% See LeastSquaresAddTerms() below for such an example.
cristy3ed852e2009-09-05 21:47:34 +0000181%
182% You can also use the 'vectors' to generate an inverse of the given 'matrix'
anthonyb16e1432011-10-21 05:06:56 +0000183% though as a 'column first array' rather than a 'row first array' (matrix
anthonyfd706f92012-01-19 04:22:02 +0000184% is transposed).
185%
186% For details of this process see...
anthony34364f42011-10-21 05:31:53 +0000187% http://en.wikipedia.org/wiki/Gauss-Jordan_elimination
cristy3ed852e2009-09-05 21:47:34 +0000188%
189*/
cristyd1dd6e42011-09-04 01:46:08 +0000190MagickPrivate MagickBooleanType GaussJordanElimination(double **matrix,
cristybb503372010-05-27 20:51:26 +0000191 double **vectors,const size_t rank,const size_t number_vectors)
cristy3ed852e2009-09-05 21:47:34 +0000192{
193#define GaussJordanSwap(x,y) \
194{ \
195 if ((x) != (y)) \
196 { \
197 (x)+=(y); \
198 (y)=(x)-(y); \
199 (x)=(x)-(y); \
200 } \
201}
202
203 double
204 max,
205 scale;
206
cristy9d314ff2011-03-09 01:30:28 +0000207 register ssize_t
208 i,
209 j,
210 k;
211
cristybb503372010-05-27 20:51:26 +0000212 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000213 column,
214 *columns,
215 *pivots,
216 row,
217 *rows;
218
cristybb503372010-05-27 20:51:26 +0000219 columns=(ssize_t *) AcquireQuantumMemory(rank,sizeof(*columns));
220 rows=(ssize_t *) AcquireQuantumMemory(rank,sizeof(*rows));
221 pivots=(ssize_t *) AcquireQuantumMemory(rank,sizeof(*pivots));
222 if ((rows == (ssize_t *) NULL) || (columns == (ssize_t *) NULL) ||
223 (pivots == (ssize_t *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000224 {
cristybb503372010-05-27 20:51:26 +0000225 if (pivots != (ssize_t *) NULL)
226 pivots=(ssize_t *) RelinquishMagickMemory(pivots);
227 if (columns != (ssize_t *) NULL)
228 columns=(ssize_t *) RelinquishMagickMemory(columns);
229 if (rows != (ssize_t *) NULL)
230 rows=(ssize_t *) RelinquishMagickMemory(rows);
cristy3ed852e2009-09-05 21:47:34 +0000231 return(MagickFalse);
232 }
233 (void) ResetMagickMemory(columns,0,rank*sizeof(*columns));
234 (void) ResetMagickMemory(rows,0,rank*sizeof(*rows));
235 (void) ResetMagickMemory(pivots,0,rank*sizeof(*pivots));
236 column=0;
237 row=0;
cristybb503372010-05-27 20:51:26 +0000238 for (i=0; i < (ssize_t) rank; i++)
cristy3ed852e2009-09-05 21:47:34 +0000239 {
240 max=0.0;
cristybb503372010-05-27 20:51:26 +0000241 for (j=0; j < (ssize_t) rank; j++)
cristy3ed852e2009-09-05 21:47:34 +0000242 if (pivots[j] != 1)
243 {
cristybb503372010-05-27 20:51:26 +0000244 for (k=0; k < (ssize_t) rank; k++)
cristy3ed852e2009-09-05 21:47:34 +0000245 if (pivots[k] != 0)
246 {
247 if (pivots[k] > 1)
248 return(MagickFalse);
249 }
250 else
251 if (fabs(matrix[j][k]) >= max)
252 {
253 max=fabs(matrix[j][k]);
254 row=j;
255 column=k;
256 }
257 }
258 pivots[column]++;
259 if (row != column)
260 {
cristybb503372010-05-27 20:51:26 +0000261 for (k=0; k < (ssize_t) rank; k++)
cristy3ed852e2009-09-05 21:47:34 +0000262 GaussJordanSwap(matrix[row][k],matrix[column][k]);
cristybb503372010-05-27 20:51:26 +0000263 for (k=0; k < (ssize_t) number_vectors; k++)
cristy3ed852e2009-09-05 21:47:34 +0000264 GaussJordanSwap(vectors[k][row],vectors[k][column]);
265 }
266 rows[i]=row;
267 columns[i]=column;
268 if (matrix[column][column] == 0.0)
anthony34364f42011-10-21 05:31:53 +0000269 return(MagickFalse); /* singularity */
cristy3ed852e2009-09-05 21:47:34 +0000270 scale=1.0/matrix[column][column];
271 matrix[column][column]=1.0;
cristybb503372010-05-27 20:51:26 +0000272 for (j=0; j < (ssize_t) rank; j++)
cristy3ed852e2009-09-05 21:47:34 +0000273 matrix[column][j]*=scale;
cristybb503372010-05-27 20:51:26 +0000274 for (j=0; j < (ssize_t) number_vectors; j++)
cristy3ed852e2009-09-05 21:47:34 +0000275 vectors[j][column]*=scale;
cristybb503372010-05-27 20:51:26 +0000276 for (j=0; j < (ssize_t) rank; j++)
cristy3ed852e2009-09-05 21:47:34 +0000277 if (j != column)
278 {
279 scale=matrix[j][column];
280 matrix[j][column]=0.0;
cristybb503372010-05-27 20:51:26 +0000281 for (k=0; k < (ssize_t) rank; k++)
cristy3ed852e2009-09-05 21:47:34 +0000282 matrix[j][k]-=scale*matrix[column][k];
cristybb503372010-05-27 20:51:26 +0000283 for (k=0; k < (ssize_t) number_vectors; k++)
cristy3ed852e2009-09-05 21:47:34 +0000284 vectors[k][j]-=scale*vectors[k][column];
285 }
286 }
cristybb503372010-05-27 20:51:26 +0000287 for (j=(ssize_t) rank-1; j >= 0; j--)
cristy3ed852e2009-09-05 21:47:34 +0000288 if (columns[j] != rows[j])
cristybb503372010-05-27 20:51:26 +0000289 for (i=0; i < (ssize_t) rank; i++)
cristy3ed852e2009-09-05 21:47:34 +0000290 GaussJordanSwap(matrix[i][rows[j]],matrix[i][columns[j]]);
cristybb503372010-05-27 20:51:26 +0000291 pivots=(ssize_t *) RelinquishMagickMemory(pivots);
292 rows=(ssize_t *) RelinquishMagickMemory(rows);
293 columns=(ssize_t *) RelinquishMagickMemory(columns);
cristy3ed852e2009-09-05 21:47:34 +0000294 return(MagickTrue);
295}
296
297/*
298%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
299% %
300% %
301% %
302% L e a s t S q u a r e s A d d T e r m s %
303% %
304% %
305% %
306%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
307%
308% LeastSquaresAddTerms() adds one set of terms and associate results to the
309% given matrix and vectors for solving using least-squares function fitting.
310%
311% The format of the AcquireMagickMatrix method is:
312%
313% void LeastSquaresAddTerms(double **matrix,double **vectors,
cristybb503372010-05-27 20:51:26 +0000314% const double *terms,const double *results,const size_t rank,
315% const size_t number_vectors);
cristy3ed852e2009-09-05 21:47:34 +0000316%
317% A description of each parameter follows:
318%
319% o matrix: the square matrix to add given terms/results to.
320%
321% o vectors: the result vectors to add terms/results to.
322%
323% o terms: the pre-calculated terms (without the unknown coefficent
cristy26295322011-09-22 00:50:36 +0000324% weights) that forms the equation being added.
cristy3ed852e2009-09-05 21:47:34 +0000325%
326% o results: the result(s) that should be generated from the given terms
cristy26295322011-09-22 00:50:36 +0000327% weighted by the yet-to-be-solved coefficents.
cristy3ed852e2009-09-05 21:47:34 +0000328%
cristy4c08aed2011-07-01 19:47:50 +0000329% o rank: the rank or size of the dimentions of the square matrix.
cristy26295322011-09-22 00:50:36 +0000330% Also the length of vectors, and number of terms being added.
cristy3ed852e2009-09-05 21:47:34 +0000331%
cristy1ad491d2010-05-17 19:45:27 +0000332% o number_vectors: Number of result vectors, and number or results being
333% added. Also represents the number of separable systems of equations
334% that is being solved.
cristy3ed852e2009-09-05 21:47:34 +0000335%
336% Example of use...
337%
glennrp2489f532011-06-25 03:02:43 +0000338% 2 dimensional Affine Equations (which are separable)
cristy3ed852e2009-09-05 21:47:34 +0000339% c0*x + c2*y + c4*1 => u
340% c1*x + c3*y + c5*1 => v
341%
342% double **matrix = AcquireMagickMatrix(3UL,3UL);
343% double **vectors = AcquireMagickMatrix(2UL,3UL);
344% double terms[3], results[2];
345% ...
346% for each given x,y -> u,v
347% terms[0] = x;
348% terms[1] = y;
349% terms[2] = 1;
350% results[0] = u;
351% results[1] = v;
352% LeastSquaresAddTerms(matrix,vectors,terms,results,3UL,2UL);
353% ...
354% if ( GaussJordanElimination(matrix,vectors,3UL,2UL) ) {
355% c0 = vectors[0][0];
anthonyfd706f92012-01-19 04:22:02 +0000356% c2 = vectors[0][1]; %* weights to calculate u from any given x,y *%
cristy3ed852e2009-09-05 21:47:34 +0000357% c4 = vectors[0][2];
358% c1 = vectors[1][0];
anthonyfd706f92012-01-19 04:22:02 +0000359% c3 = vectors[1][1]; %* weights for calculate v from any given x,y *%
cristy3ed852e2009-09-05 21:47:34 +0000360% c5 = vectors[1][2];
361% }
362% else
363% printf("Matrix unsolvable\n);
364% RelinquishMagickMatrix(matrix,3UL);
365% RelinquishMagickMatrix(vectors,2UL);
366%
anthonyfd706f92012-01-19 04:22:02 +0000367% More examples can be found in "distort.c"
368%
cristy3ed852e2009-09-05 21:47:34 +0000369*/
cristyd1dd6e42011-09-04 01:46:08 +0000370MagickPrivate void LeastSquaresAddTerms(double **matrix,double **vectors,
cristybb503372010-05-27 20:51:26 +0000371 const double *terms,const double *results,const size_t rank,
372 const size_t number_vectors)
cristy3ed852e2009-09-05 21:47:34 +0000373{
cristybb503372010-05-27 20:51:26 +0000374 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000375 i,
376 j;
377
cristybb503372010-05-27 20:51:26 +0000378 for (j=0; j < (ssize_t) rank; j++)
cristy74908cf2010-05-17 19:43:54 +0000379 {
cristybb503372010-05-27 20:51:26 +0000380 for (i=0; i < (ssize_t) rank; i++)
cristy74908cf2010-05-17 19:43:54 +0000381 matrix[i][j]+=terms[i]*terms[j];
cristybb503372010-05-27 20:51:26 +0000382 for (i=0; i < (ssize_t) number_vectors; i++)
cristy74908cf2010-05-17 19:43:54 +0000383 vectors[i][j]+=results[i]*terms[j];
cristy3ed852e2009-09-05 21:47:34 +0000384 }
cristy3ed852e2009-09-05 21:47:34 +0000385}
386
387/*
388%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
389% %
390% %
391% %
392% R e l i n q u i s h M a g i c k M a t r i x %
393% %
394% %
395% %
396%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
397%
398% RelinquishMagickMatrix() frees the previously acquired matrix (array of
399% pointers to arrays of doubles).
400%
401% The format of the RelinquishMagickMatrix method is:
402%
403% double **RelinquishMagickMatrix(double **matrix,
cristybb503372010-05-27 20:51:26 +0000404% const size_t number_rows)
cristy3ed852e2009-09-05 21:47:34 +0000405%
406% A description of each parameter follows:
407%
408% o matrix: the matrix to relinquish
409%
cristy1ad491d2010-05-17 19:45:27 +0000410% o number_rows: the first dimension of the acquired matrix (number of
411% pointers)
cristy3ed852e2009-09-05 21:47:34 +0000412%
413*/
414MagickExport double **RelinquishMagickMatrix(double **matrix,
cristybb503372010-05-27 20:51:26 +0000415 const size_t number_rows)
cristy3ed852e2009-09-05 21:47:34 +0000416{
cristybb503372010-05-27 20:51:26 +0000417 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000418 i;
419
420 if (matrix == (double **) NULL )
421 return(matrix);
cristybb503372010-05-27 20:51:26 +0000422 for (i=0; i < (ssize_t) number_rows; i++)
cristy3ed852e2009-09-05 21:47:34 +0000423 matrix[i]=(double *) RelinquishMagickMemory(matrix[i]);
424 matrix=(double **) RelinquishMagickMemory(matrix);
cristy3ed852e2009-09-05 21:47:34 +0000425 return(matrix);
426}