MagickCore 7.1.2-28
Convert, Edit, Or Compose Bitmap Images
Loading...
Searching...
No Matches
matrix.c
1/*
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% Cristy %
17% August 2007 %
18% %
19% %
20% Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization %
21% 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% https://imagemagick.org/license/ %
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*/
42#include "MagickCore/studio.h"
43#include "MagickCore/blob.h"
44#include "MagickCore/blob-private.h"
45#include "MagickCore/cache.h"
46#include "MagickCore/exception.h"
47#include "MagickCore/exception-private.h"
48#include "MagickCore/image-private.h"
49#include "MagickCore/matrix.h"
50#include "MagickCore/matrix-private.h"
51#include "MagickCore/memory_.h"
52#include "MagickCore/memory-private.h"
53#include "MagickCore/nt-base-private.h"
54#include "MagickCore/pixel-accessor.h"
55#include "MagickCore/resource_.h"
56#include "MagickCore/semaphore.h"
57#include "MagickCore/thread-private.h"
58#include "MagickCore/utility.h"
59#include "MagickCore/utility-private.h"
60
61/*
62 Typedef declaration.
63*/
65{
66 CacheType
67 type;
68
69 size_t
70 columns,
71 rows,
72 stride;
73
74 MagickSizeType
75 length;
76
77 MagickBooleanType
78 mapped,
79 synchronize;
80
81 char
82 path[MagickPathExtent];
83
84 int
85 file;
86
87 void
88 *elements;
89
91 *semaphore;
92
93 size_t
94 signature;
95};
96
97/*
98%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
99% %
100% %
101% %
102% A c q u i r e M a t r i x I n f o %
103% %
104% %
105% %
106%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
107%
108% AcquireMatrixInfo() allocates the ImageInfo structure.
109%
110% The format of the AcquireMatrixInfo method is:
111%
112% MatrixInfo *AcquireMatrixInfo(const size_t columns,const size_t rows,
113% const size_t stride,ExceptionInfo *exception)
114%
115% A description of each parameter follows:
116%
117% o columns: the matrix columns.
118%
119% o rows: the matrix rows.
120%
121% o stride: the matrix stride.
122%
123% o exception: return any errors or warnings in this structure.
124%
125*/
126
127#if defined(SIGBUS)
128static void MatrixSignalHandler(int magick_unused(status))
129{
130 magick_unreferenced(status);
131 ThrowFatalException(CacheFatalError,"UnableToExtendMatrixCache");
132}
133#endif
134
135static inline MagickOffsetType WriteMatrixElements(
136 const MatrixInfo *magick_restrict matrix_info,const MagickOffsetType offset,
137 const MagickSizeType length,const unsigned char *magick_restrict buffer)
138{
139 MagickOffsetType
140 i;
141
142 ssize_t
143 count;
144
145 LockSemaphoreInfo(matrix_info->semaphore);
146 if (lseek(matrix_info->file,offset,SEEK_SET) < 0)
147 {
148 UnlockSemaphoreInfo(matrix_info->semaphore);
149 return((MagickOffsetType) -1);
150 }
151 count=0;
152 for (i=0; i < (MagickOffsetType) length; i+=count)
153 {
154 count=MagickWrite(matrix_info->file,buffer+i,(size_t) MagickMin(length-
155 (MagickSizeType) i,(MagickSizeType) MagickMaxBufferExtent));
156 if (count <= 0)
157 break;
158 }
159 UnlockSemaphoreInfo(matrix_info->semaphore);
160 return(i);
161}
162
163static MagickBooleanType SetMatrixExtent(
164 MatrixInfo *magick_restrict matrix_info,MagickSizeType length)
165{
166 MagickOffsetType
167 count,
168 extent,
169 offset;
170
171 if (length != (MagickSizeType) ((MagickOffsetType) length))
172 return(MagickFalse);
173 offset=(MagickOffsetType) lseek(matrix_info->file,0,SEEK_END);
174 if (offset < 0)
175 return(MagickFalse);
176 if ((MagickSizeType) offset >= length)
177 return(MagickTrue);
178 extent=(MagickOffsetType) length-1;
179 count=WriteMatrixElements(matrix_info,extent,1,(const unsigned char *) "");
180#if defined(MAGICKCORE_HAVE_POSIX_FALLOCATE)
181 if (matrix_info->synchronize != MagickFalse)
182 (void) posix_fallocate(matrix_info->file,offset+1,extent-offset);
183#endif
184#if defined(SIGBUS)
185 (void) signal(SIGBUS,MatrixSignalHandler);
186#endif
187 return(count != (MagickOffsetType) 1 ? MagickFalse : MagickTrue);
188}
189
190MagickExport MatrixInfo *AcquireMatrixInfo(const size_t columns,
191 const size_t rows,const size_t stride,ExceptionInfo *exception)
192{
193 char
194 *synchronize;
195
196 MagickBooleanType
197 status;
198
199 MatrixInfo
200 *matrix_info;
201
202 matrix_info=(MatrixInfo *) AcquireMagickMemory(sizeof(*matrix_info));
203 if (matrix_info == (MatrixInfo *) NULL)
204 return((MatrixInfo *) NULL);
205 (void) memset(matrix_info,0,sizeof(*matrix_info));
206 matrix_info->signature=MagickCoreSignature;
207 matrix_info->columns=columns;
208 matrix_info->rows=rows;
209 matrix_info->stride=stride;
210 matrix_info->semaphore=AcquireSemaphoreInfo();
211 synchronize=GetEnvironmentValue("MAGICK_SYNCHRONIZE");
212 if (synchronize != (const char *) NULL)
213 {
214 matrix_info->synchronize=IsStringTrue(synchronize);
215 synchronize=DestroyString(synchronize);
216 }
217 matrix_info->length=(MagickSizeType) columns*rows*stride;
218 if (matrix_info->columns != (size_t) (matrix_info->length/rows/stride))
219 {
220 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
221 "CacheResourcesExhausted","`%s'","matrix cache");
222 return(DestroyMatrixInfo(matrix_info));
223 }
224 matrix_info->type=MemoryCache;
225 status=AcquireMagickResource(AreaResource,matrix_info->length);
226 if ((status != MagickFalse) &&
227 (matrix_info->length == (MagickSizeType) ((size_t) matrix_info->length)) &&
228 ((size_t) matrix_info->length <= GetMaxMemoryRequest()))
229 {
230 status=AcquireMagickResource(MemoryResource,matrix_info->length);
231 if (status != MagickFalse)
232 {
233 matrix_info->mapped=MagickFalse;
234 matrix_info->elements=MagickAssumeAligned(AcquireAlignedMemory(1,
235 (size_t) matrix_info->length));
236 if (matrix_info->elements == NULL)
237 {
238 matrix_info->mapped=MagickTrue;
239 matrix_info->elements=MapBlob(-1,IOMode,0,(size_t)
240 matrix_info->length);
241 }
242 if (matrix_info->elements == (unsigned short *) NULL)
243 RelinquishMagickResource(MemoryResource,matrix_info->length);
244 }
245 }
246 matrix_info->file=(-1);
247 if (matrix_info->elements == (unsigned short *) NULL)
248 {
249 status=AcquireMagickResource(DiskResource,matrix_info->length);
250 if (status == MagickFalse)
251 {
252 (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
253 "CacheResourcesExhausted","`%s'","matrix cache");
254 return(DestroyMatrixInfo(matrix_info));
255 }
256 matrix_info->type=DiskCache;
257 matrix_info->file=AcquireUniqueFileResource(matrix_info->path);
258 if (matrix_info->file == -1)
259 return(DestroyMatrixInfo(matrix_info));
260 status=AcquireMagickResource(MapResource,matrix_info->length);
261 if (status != MagickFalse)
262 {
263 status=SetMatrixExtent(matrix_info,matrix_info->length);
264 if (status != MagickFalse)
265 matrix_info->elements=(void *) MapBlob(matrix_info->file,IOMode,0,
266 (size_t) matrix_info->length);
267 if (matrix_info->elements != NULL)
268 matrix_info->type=MapCache;
269 else
270 RelinquishMagickResource(MapResource,matrix_info->length);
271 }
272 }
273 return(matrix_info);
274}
275
276/*
277%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
278% %
279% %
280% %
281% A c q u i r e M a g i c k M a t r i x %
282% %
283% %
284% %
285%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
286%
287% AcquireMagickMatrix() allocates and returns a matrix in the form of an
288% array of pointers to an array of doubles, with all values pre-set to zero.
289%
290% This used to generate the two dimensional matrix, and vectors required
291% for the GaussJordanElimination() method below, solving some system of
292% simultaneous equations.
293%
294% The format of the AcquireMagickMatrix method is:
295%
296% double **AcquireMagickMatrix(const size_t number_rows,
297% const size_t size)
298%
299% A description of each parameter follows:
300%
301% o number_rows: the number pointers for the array of pointers
302% (first dimension).
303%
304% o size: the size of the array of doubles each pointer points to
305% (second dimension).
306%
307*/
308MagickExport double **AcquireMagickMatrix(const size_t number_rows,
309 const size_t size)
310{
311 double
312 **matrix;
313
314 ssize_t
315 i,
316 j;
317
318 matrix=(double **) AcquireQuantumMemory(number_rows,sizeof(*matrix));
319 if (matrix == (double **) NULL)
320 return((double **) NULL);
321 for (i=0; i < (ssize_t) number_rows; i++)
322 {
323 matrix[i]=(double *) AcquireQuantumMemory(size,sizeof(*matrix[i]));
324 if (matrix[i] == (double *) NULL)
325 {
326 for (j=0; j < i; j++)
327 matrix[j]=(double *) RelinquishMagickMemory(matrix[j]);
328 matrix=(double **) RelinquishMagickMemory(matrix);
329 return((double **) NULL);
330 }
331 for (j=0; j < (ssize_t) size; j++)
332 matrix[i][j]=0.0;
333 }
334 return(matrix);
335}
336
337/*
338%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
339% %
340% %
341% %
342% D e s t r o y M a t r i x I n f o %
343% %
344% %
345% %
346%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
347%
348% DestroyMatrixInfo() dereferences a matrix, deallocating memory associated
349% with the matrix.
350%
351% The format of the DestroyImage method is:
352%
353% MatrixInfo *DestroyMatrixInfo(MatrixInfo *matrix_info)
354%
355% A description of each parameter follows:
356%
357% o matrix_info: the matrix.
358%
359*/
360MagickExport MatrixInfo *DestroyMatrixInfo(MatrixInfo *matrix_info)
361{
362 assert(matrix_info != (MatrixInfo *) NULL);
363 assert(matrix_info->signature == MagickCoreSignature);
364 LockSemaphoreInfo(matrix_info->semaphore);
365 switch (matrix_info->type)
366 {
367 case MemoryCache:
368 {
369 if (matrix_info->mapped == MagickFalse)
370 matrix_info->elements=RelinquishAlignedMemory(matrix_info->elements);
371 else
372 {
373 (void) UnmapBlob(matrix_info->elements,(size_t) matrix_info->length);
374 matrix_info->elements=(unsigned short *) NULL;
375 }
376 RelinquishMagickResource(MemoryResource,matrix_info->length);
377 break;
378 }
379 case MapCache:
380 {
381 (void) UnmapBlob(matrix_info->elements,(size_t) matrix_info->length);
382 matrix_info->elements=NULL;
383 RelinquishMagickResource(MapResource,matrix_info->length);
384 magick_fallthrough;
385 }
386 case DiskCache:
387 {
388 if (matrix_info->file != -1)
389 (void) close_utf8(matrix_info->file);
390 (void) RelinquishUniqueFileResource(matrix_info->path);
391 RelinquishMagickResource(DiskResource,matrix_info->length);
392 break;
393 }
394 default:
395 break;
396 }
397 UnlockSemaphoreInfo(matrix_info->semaphore);
398 RelinquishSemaphoreInfo(&matrix_info->semaphore);
399 return((MatrixInfo *) RelinquishMagickMemory(matrix_info));
400}
401
402/*
403%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
404% %
405% %
406% %
407% G e t M a t r i x C o l u m n s %
408% %
409% %
410% %
411%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
412%
413% GetMatrixColumns() returns the number of columns in the matrix.
414%
415% The format of the GetMatrixColumns method is:
416%
417% size_t GetMatrixColumns(const MatrixInfo *matrix_info)
418%
419% A description of each parameter follows:
420%
421% o matrix_info: the matrix.
422%
423*/
424MagickExport size_t GetMatrixColumns(const MatrixInfo *matrix_info)
425{
426 assert(matrix_info != (MatrixInfo *) NULL);
427 assert(matrix_info->signature == MagickCoreSignature);
428 return(matrix_info->columns);
429}
430
431/*
432%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
433% %
434% %
435% %
436% G e t M a t r i x E l e m e n t %
437% %
438% %
439% %
440%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441%
442% GetMatrixElement() returns the specified element in the matrix.
443%
444% The format of the GetMatrixElement method is:
445%
446% MagickBooleanType GetMatrixElement(const MatrixInfo *matrix_info,
447% const ssize_t x,const ssize_t y,void *value)
448%
449% A description of each parameter follows:
450%
451% o matrix_info: the matrix columns.
452%
453% o x: the matrix x-offset.
454%
455% o y: the matrix y-offset.
456%
457% o value: return the matrix element in this buffer.
458%
459*/
460
461static inline ssize_t EdgeX(const ssize_t x,const size_t columns)
462{
463 if (x < 0L)
464 return(0L);
465 if (x >= (ssize_t) columns)
466 return((ssize_t) (columns-1));
467 return(x);
468}
469
470static inline ssize_t EdgeY(const ssize_t y,const size_t rows)
471{
472 if (y < 0L)
473 return(0L);
474 if (y >= (ssize_t) rows)
475 return((ssize_t) (rows-1));
476 return(y);
477}
478
479static inline MagickOffsetType ReadMatrixElements(
480 const MatrixInfo *magick_restrict matrix_info,const MagickOffsetType offset,
481 const MagickSizeType length,unsigned char *magick_restrict buffer)
482{
483 MagickOffsetType
484 i;
485
486 ssize_t
487 count;
488
489 LockSemaphoreInfo(matrix_info->semaphore);
490 if (lseek(matrix_info->file,offset,SEEK_SET) < 0)
491 {
492 UnlockSemaphoreInfo(matrix_info->semaphore);
493 return((MagickOffsetType) -1);
494 }
495 count=0;
496 for (i=0; i < (MagickOffsetType) length; i+=count)
497 {
498 count=MagickRead(matrix_info->file,buffer+i,(size_t) MagickMin(length-i,
499 (MagickSizeType) MagickMaxBufferExtent));
500 if (count <= 0)
501 break;
502 }
503 UnlockSemaphoreInfo(matrix_info->semaphore);
504 return(i);
505}
506
507MagickExport MagickBooleanType GetMatrixElement(const MatrixInfo *matrix_info,
508 const ssize_t x,const ssize_t y,void *value)
509{
510 MagickOffsetType
511 count,
512 i;
513
514 assert(matrix_info != (const MatrixInfo *) NULL);
515 assert(matrix_info->signature == MagickCoreSignature);
516 i=EdgeY(y,matrix_info->rows)*(MagickOffsetType) matrix_info->columns+
517 EdgeX(x,matrix_info->columns);
518 if (matrix_info->type != DiskCache)
519 {
520 (void) memcpy(value,(unsigned char *) matrix_info->elements+i*
521 (MagickOffsetType) matrix_info->stride,matrix_info->stride);
522 return(MagickTrue);
523 }
524 count=ReadMatrixElements(matrix_info,i*(MagickOffsetType) matrix_info->stride,
525 matrix_info->stride,(unsigned char *) value);
526 if (count != (MagickOffsetType) matrix_info->stride)
527 return(MagickFalse);
528 return(MagickTrue);
529}
530
531/*
532%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
533% %
534% %
535% %
536% G e t M a t r i x R o w s %
537% %
538% %
539% %
540%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
541%
542% GetMatrixRows() returns the number of rows in the matrix.
543%
544% The format of the GetMatrixRows method is:
545%
546% size_t GetMatrixRows(const MatrixInfo *matrix_info)
547%
548% A description of each parameter follows:
549%
550% o matrix_info: the matrix.
551%
552*/
553MagickExport size_t GetMatrixRows(const MatrixInfo *matrix_info)
554{
555 assert(matrix_info != (const MatrixInfo *) NULL);
556 assert(matrix_info->signature == MagickCoreSignature);
557 return(matrix_info->rows);
558}
559
560/*
561%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
562% %
563% %
564% %
565+ L e a s t S q u a r e s A d d T e r m s %
566% %
567% %
568% %
569%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
570%
571% LeastSquaresAddTerms() adds one set of terms and associate results to the
572% given matrix and vectors for solving using least-squares function fitting.
573%
574% The format of the AcquireMagickMatrix method is:
575%
576% void LeastSquaresAddTerms(double **matrix,double **vectors,
577% const double *terms,const double *results,const size_t rank,
578% const size_t number_vectors);
579%
580% A description of each parameter follows:
581%
582% o matrix: the square matrix to add given terms/results to.
583%
584% o vectors: the result vectors to add terms/results to.
585%
586% o terms: the pre-calculated terms (without the unknown coefficient
587% weights) that forms the equation being added.
588%
589% o results: the result(s) that should be generated from the given terms
590% weighted by the yet-to-be-solved coefficients.
591%
592% o rank: the rank or size of the dimensions of the square matrix.
593% Also the length of vectors, and number of terms being added.
594%
595% o number_vectors: Number of result vectors, and number or results being
596% added. Also represents the number of separable systems of equations
597% that is being solved.
598%
599% Example of use...
600%
601% 2 dimensional Affine Equations (which are separable)
602% c0*x + c2*y + c4*1 => u
603% c1*x + c3*y + c5*1 => v
604%
605% double **matrix = AcquireMagickMatrix(3UL,3UL);
606% double **vectors = AcquireMagickMatrix(2UL,3UL);
607% double terms[3], results[2];
608% ...
609% for each given x,y -> u,v
610% terms[0] = x;
611% terms[1] = y;
612% terms[2] = 1;
613% results[0] = u;
614% results[1] = v;
615% LeastSquaresAddTerms(matrix,vectors,terms,results,3UL,2UL);
616% ...
617% if ( GaussJordanElimination(matrix,vectors,3UL,2UL) ) {
618% c0 = vectors[0][0];
619% c2 = vectors[0][1];
620% c4 = vectors[0][2];
621% c1 = vectors[1][0];
622% c3 = vectors[1][1];
623% c5 = vectors[1][2];
624% }
625% else
626% printf("Matrix unsolvable\n");
627% RelinquishMagickMatrix(matrix,3UL);
628% RelinquishMagickMatrix(vectors,2UL);
629%
630*/
631MagickPrivate void LeastSquaresAddTerms(double **matrix,double **vectors,
632 const double *terms,const double *results,const size_t rank,
633 const size_t number_vectors)
634{
635 ssize_t
636 i,
637 j;
638
639 for (j=0; j < (ssize_t) rank; j++)
640 {
641 for (i=0; i < (ssize_t) rank; i++)
642 matrix[i][j]+=terms[i]*terms[j];
643 for (i=0; i < (ssize_t) number_vectors; i++)
644 vectors[i][j]+=results[i]*terms[j];
645 }
646}
647
648/*
649%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
650% %
651% %
652% %
653+ G a u s s J o r d a n E l i m n a t i o n %
654% %
655% %
656% %
657%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
658%
659% GaussJordanElimination() returns a matrix in reduced row echelon form,
660% while simultaneously reducing and thus solving the augmented results
661% matrix.
662%
663% The format of the GaussJordanElimination method is:
664%
665% MagickBooleanType GaussJordanElimination(double **matrix,
666% double **vectors,const size_t rank,const size_t number_vectors)
667%
668% A description of each parameter follows:
669%
670% o matrix: the matrix to be reduced, as an 'array of row pointers'.
671%
672% o vectors: the additional matrix argumenting the matrix for row reduction.
673% Producing an 'array of column vectors'.
674%
675% o rank: The size of the matrix (both rows and columns).
676% Also represents the number terms that need to be solved.
677%
678% o number_vectors: Number of vectors columns, argumenting the above matrix.
679% Usually 1, but can be more for more complex equation solving.
680%
681% Note that the 'matrix' is given as a 'array of row pointers' of rank size.
682% That is values can be assigned as matrix[row][column] where 'row' is
683% typically the equation, and 'column' is the term of the equation.
684% That is the matrix is in the form of a 'row first array'.
685%
686% However 'vectors' is a 'array of column pointers' which can have any number
687% of columns, with each column array the same 'rank' size as 'matrix'.
688%
689% This allows for simpler handling of the results, especially is only one
690% column 'vector' is all that is required to produce the desired solution.
691%
692% For example, the 'vectors' can consist of a pointer to a simple array of
693% doubles. when only one set of simultaneous equations is to be solved from
694% the given set of coefficient weighted terms.
695%
696% double **matrix = AcquireMagickMatrix(8UL,8UL);
697% double coefficients[8];
698% ...
699% GaussJordanElimination(matrix,&coefficients,8UL,1UL);
700%
701% However by specifying more 'columns' (as an 'array of vector columns', you
702% can use this function to solve a set of 'separable' equations.
703%
704% For example a distortion function where u = U(x,y) v = V(x,y)
705% And the functions U() and V() have separate coefficients, but are being
706% generated from a common x,y->u,v data set.
707%
708% Another example is generation of a color gradient from a set of colors at
709% specific coordinates, such as a list x,y -> r,g,b,a.
710%
711% You can also use the 'vectors' to generate an inverse of the given 'matrix'
712% though as a 'column first array' rather than a 'row first array'. For
713% details see http://en.wikipedia.org/wiki/Gauss-Jordan_elimination
714%
715*/
716MagickPrivate MagickBooleanType GaussJordanElimination(double **matrix,
717 double **vectors,const size_t rank,const size_t number_vectors)
718{
719#define GaussJordanSwap(x,y) \
720{ \
721 double temp = (x); \
722 (x)=(y); \
723 (y)=temp; \
724}
725#define GaussJordanSwapLD(x,y) \
726{ \
727 long double temp = (x); \
728 (x)=(y); \
729 (y)=temp; \
730}
731#define ThrowGaussJordanException() \
732{ \
733 for (i=0; i < (ssize_t) rank; i++) \
734 hp_matrix[i]=(long double *) RelinquishMagickMemory(hp_matrix[i]); \
735 hp_matrix=(long double **) RelinquishMagickMemory(hp_matrix); \
736 if (pivots != (ssize_t *) NULL) \
737 pivots=(ssize_t *) RelinquishMagickMemory(pivots); \
738 if (rows != (ssize_t *) NULL) \
739 rows=(ssize_t *) RelinquishMagickMemory(rows); \
740 if (columns != (ssize_t *) NULL) \
741 columns=(ssize_t *) RelinquishMagickMemory(columns); \
742 return(MagickFalse); \
743}
744
745 long double
746 **hp_matrix = (long double **) NULL,
747 scale;
748
749 ssize_t
750 column,
751 *columns = (ssize_t *) NULL,
752 i,
753 j,
754 *pivots = (ssize_t *) NULL,
755 row,
756 *rows = (ssize_t *) NULL;
757
758 /*
759 Allocate high precision matrix.
760 */
761 hp_matrix=(long double **) AcquireQuantumMemory(rank,sizeof(*hp_matrix));
762 if (hp_matrix == (long double **) NULL)
763 return(MagickFalse);
764 for (i=0; i < (ssize_t) rank; i++)
765 {
766 hp_matrix[i]=(long double *) AcquireQuantumMemory(rank,
767 sizeof(*hp_matrix[i]));
768 if (hp_matrix[i] == (long double *) NULL)
769 ThrowGaussJordanException();
770 for (j=0; j < (ssize_t) rank; j++)
771 hp_matrix[i][j]=(long double)matrix[i][j];
772 }
773 columns=(ssize_t *) AcquireQuantumMemory(rank,sizeof(*columns));
774 rows=(ssize_t *) AcquireQuantumMemory(rank,sizeof(*rows));
775 pivots=(ssize_t *) AcquireQuantumMemory(rank,sizeof(*pivots));
776 if ((columns == (ssize_t *) NULL) || (rows == (ssize_t *) NULL) ||
777 (pivots == (ssize_t *) NULL))
778 ThrowGaussJordanException();
779 (void) memset(columns,0,rank*sizeof(*columns));
780 (void) memset(rows,0,rank*sizeof(*rows));
781 (void) memset(pivots,0,rank*sizeof(*pivots));
782 for (i=0; i < (ssize_t) rank; i++)
783 {
784 long double
785 max = 0.0;
786
787 ssize_t
788 k;
789
790 /*
791 Partial pivoting: find the largest absolute value in the unreduced
792 submatrix.
793 */
794 column=(-1);
795 row=(-1);
796 for (j=0; j < (ssize_t) rank; j++)
797 if (pivots[j] != 1)
798 for (k=0; k < (ssize_t) rank; k++)
799 if ((pivots[k] == 0) && (fabsl(hp_matrix[j][k]) > max))
800 {
801 max=fabsl(hp_matrix[j][k]);
802 row=j;
803 column=k;
804 }
805 if ((column == -1) || (row == -1) || (fabsl(max) < LDBL_MIN))
806 ThrowGaussJordanException(); /* Singular or nearly singular matrix */
807 pivots[column]++;
808 if (row != column)
809 {
810 for (k=0; k < (ssize_t) rank; k++)
811 GaussJordanSwapLD(hp_matrix[row][k],hp_matrix[column][k]);
812 for (k=0; k < (ssize_t) number_vectors; k++)
813 GaussJordanSwap(vectors[k][row],vectors[k][column]);
814 }
815 rows[i]=row;
816 columns[i]=column;
817 if (fabsl(hp_matrix[column][column]) < LDBL_MIN)
818 ThrowGaussJordanException(); /* Singular matrix */
819 scale=1.0L/hp_matrix[column][column];
820 hp_matrix[column][column]=1.0;
821 for (j=0; j < (ssize_t) rank; j++)
822 hp_matrix[column][j]*=scale;
823 for (j=0; j < (ssize_t) number_vectors; j++)
824 vectors[j][column]*=(double) scale;
825 for (j=0; j < (ssize_t) rank; j++)
826 if (j != column)
827 {
828 scale=hp_matrix[j][column];
829 hp_matrix[j][column]=0.0;
830 for (k=0; k < (ssize_t) rank; k++)
831 hp_matrix[j][k]-=scale*hp_matrix[column][k];
832 for (k=0; k < (ssize_t) number_vectors; k++)
833 vectors[k][j]-=(double)(scale*(long double) vectors[k][column]);
834 }
835 }
836 for (j=(ssize_t) rank-1; j >= 0; j--)
837 if (columns[j] != rows[j])
838 for (i=0; i < (ssize_t) rank; i++)
839 GaussJordanSwapLD(hp_matrix[i][columns[j]],hp_matrix[i][rows[j]]);
840 /*
841 Copy back the result to the original matrix.
842 */
843 for (i=0; i < (ssize_t) rank; i++)
844 for (j=0; j < (ssize_t) rank; j++)
845 matrix[i][j]=(double)hp_matrix[i][j];
846 /*
847 Free resources.
848 */
849 for (i=0; i < (ssize_t) rank; i++)
850 hp_matrix[i]=(long double *) RelinquishMagickMemory(hp_matrix[i]);
851 hp_matrix=(long double **) RelinquishMagickMemory(hp_matrix);
852 pivots=(ssize_t *) RelinquishMagickMemory(pivots);
853 rows=(ssize_t *) RelinquishMagickMemory(rows);
854 columns=(ssize_t *) RelinquishMagickMemory(columns);
855 return(MagickTrue);
856}
857
858/*
859%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
860% %
861% %
862% %
863% M a t r i x T o I m a g e %
864% %
865% %
866% %
867%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
868%
869% MatrixToImage() returns a matrix as an image. The matrix elements must be
870% of type double otherwise nonsense is returned.
871%
872% The format of the MatrixToImage method is:
873%
874% Image *MatrixToImage(const MatrixInfo *matrix_info,
875% ExceptionInfo *exception)
876%
877% A description of each parameter follows:
878%
879% o matrix_info: the matrix.
880%
881% o exception: return any errors or warnings in this structure.
882%
883*/
884MagickExport Image *MatrixToImage(const MatrixInfo *matrix_info,
885 ExceptionInfo *exception)
886{
887 CacheView
888 *image_view;
889
890 double
891 max_value,
892 min_value,
893 scale_factor;
894
895 Image
896 *image;
897
898 MagickBooleanType
899 status;
900
901 ssize_t
902 y;
903
904 assert(matrix_info != (const MatrixInfo *) NULL);
905 assert(matrix_info->signature == MagickCoreSignature);
906 assert(exception != (ExceptionInfo *) NULL);
907 assert(exception->signature == MagickCoreSignature);
908 if (matrix_info->stride < sizeof(double))
909 return((Image *) NULL);
910 /*
911 Determine range of matrix.
912 */
913 (void) GetMatrixElement(matrix_info,0,0,&min_value);
914 max_value=min_value;
915 for (y=0; y < (ssize_t) matrix_info->rows; y++)
916 {
917 ssize_t
918 x;
919
920 for (x=0; x < (ssize_t) matrix_info->columns; x++)
921 {
922 double
923 value;
924
925 if (GetMatrixElement(matrix_info,x,y,&value) == MagickFalse)
926 continue;
927 if (value < min_value)
928 min_value=value;
929 else
930 if (value > max_value)
931 max_value=value;
932 }
933 }
934 if ((min_value == 0.0) && (max_value == 0.0))
935 scale_factor=0;
936 else
937 if (min_value == max_value)
938 {
939 scale_factor=(double) QuantumRange/min_value;
940 min_value=0;
941 }
942 else
943 scale_factor=(double) QuantumRange/(max_value-min_value);
944 /*
945 Convert matrix to image.
946 */
947 image=AcquireImage((ImageInfo *) NULL,exception);
948 image->columns=matrix_info->columns;
949 image->rows=matrix_info->rows;
950 image->colorspace=GRAYColorspace;
951 status=MagickTrue;
952 image_view=AcquireAuthenticCacheView(image,exception);
953#if defined(MAGICKCORE_OPENMP_SUPPORT)
954 #pragma omp parallel for schedule(static) shared(status) \
955 magick_number_threads(image,image,image->rows,2)
956#endif
957 for (y=0; y < (ssize_t) image->rows; y++)
958 {
959 double
960 value;
961
962 Quantum
963 *q;
964
965 ssize_t
966 x;
967
968 if (status == MagickFalse)
969 continue;
970 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
971 if (q == (Quantum *) NULL)
972 {
973 status=MagickFalse;
974 continue;
975 }
976 for (x=0; x < (ssize_t) image->columns; x++)
977 {
978 if (GetMatrixElement(matrix_info,x,y,&value) == MagickFalse)
979 continue;
980 value=scale_factor*(value-min_value);
981 *q=ClampToQuantum(value);
982 q+=(ptrdiff_t) GetPixelChannels(image);
983 }
984 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
985 status=MagickFalse;
986 }
987 image_view=DestroyCacheView(image_view);
988 if (status == MagickFalse)
989 image=DestroyImage(image);
990 return(image);
991}
992
993/*
994%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
995% %
996% %
997% %
998% N u l l M a t r i x %
999% %
1000% %
1001% %
1002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1003%
1004% NullMatrix() sets all elements of the matrix to zero.
1005%
1006% The format of the memset method is:
1007%
1008% MagickBooleanType *NullMatrix(MatrixInfo *matrix_info)
1009%
1010% A description of each parameter follows:
1011%
1012% o matrix_info: the matrix.
1013%
1014*/
1015MagickExport MagickBooleanType NullMatrix(MatrixInfo *matrix_info)
1016{
1017 ssize_t
1018 x;
1019
1020 ssize_t
1021 count,
1022 y;
1023
1024 unsigned char
1025 value;
1026
1027 assert(matrix_info != (const MatrixInfo *) NULL);
1028 assert(matrix_info->signature == MagickCoreSignature);
1029 if (matrix_info->type != DiskCache)
1030 {
1031 (void) memset(matrix_info->elements,0,(size_t)
1032 matrix_info->length);
1033 return(MagickTrue);
1034 }
1035 value=0;
1036 (void) lseek(matrix_info->file,0,SEEK_SET);
1037 for (y=0; y < (ssize_t) matrix_info->rows; y++)
1038 {
1039 for (x=0; x < (ssize_t) matrix_info->length; x++)
1040 {
1041 count=MagickWrite(matrix_info->file,&value,sizeof(value));
1042 if (count != (ssize_t) sizeof(value))
1043 break;
1044 }
1045 if (x < (ssize_t) matrix_info->length)
1046 break;
1047 }
1048 return(y < (ssize_t) matrix_info->rows ? MagickFalse : MagickTrue);
1049}
1050
1051/*
1052%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1053% %
1054% %
1055% %
1056% R e l i n q u i s h M a g i c k M a t r i x %
1057% %
1058% %
1059% %
1060%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1061%
1062% RelinquishMagickMatrix() frees the previously acquired matrix (array of
1063% pointers to arrays of doubles).
1064%
1065% The format of the RelinquishMagickMatrix method is:
1066%
1067% double **RelinquishMagickMatrix(double **matrix,
1068% const size_t number_rows)
1069%
1070% A description of each parameter follows:
1071%
1072% o matrix: the matrix to relinquish
1073%
1074% o number_rows: the first dimension of the acquired matrix (number of
1075% pointers)
1076%
1077*/
1078MagickExport double **RelinquishMagickMatrix(double **matrix,
1079 const size_t number_rows)
1080{
1081 ssize_t
1082 i;
1083
1084 if (matrix == (double **) NULL )
1085 return(matrix);
1086 for (i=0; i < (ssize_t) number_rows; i++)
1087 matrix[i]=(double *) RelinquishMagickMemory(matrix[i]);
1088 matrix=(double **) RelinquishMagickMemory(matrix);
1089 return(matrix);
1090}
1091
1092/*
1093%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1094% %
1095% %
1096% %
1097% S e t M a t r i x E l e m e n t %
1098% %
1099% %
1100% %
1101%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1102%
1103% SetMatrixElement() sets the specified element in the matrix.
1104%
1105% The format of the SetMatrixElement method is:
1106%
1107% MagickBooleanType SetMatrixElement(const MatrixInfo *matrix_info,
1108% const ssize_t x,const ssize_t y,void *value)
1109%
1110% A description of each parameter follows:
1111%
1112% o matrix_info: the matrix columns.
1113%
1114% o x: the matrix x-offset.
1115%
1116% o y: the matrix y-offset.
1117%
1118% o value: set the matrix element to this value.
1119%
1120*/
1121
1122MagickExport MagickBooleanType SetMatrixElement(const MatrixInfo *matrix_info,
1123 const ssize_t x,const ssize_t y,const void *value)
1124{
1125 MagickOffsetType
1126 count,
1127 i;
1128
1129 assert(matrix_info != (const MatrixInfo *) NULL);
1130 assert(matrix_info->signature == MagickCoreSignature);
1131 i=y*(MagickOffsetType) matrix_info->columns+x;
1132 if ((i < 0) ||
1133 (((MagickSizeType) i*matrix_info->stride) >= matrix_info->length))
1134 return(MagickFalse);
1135 if (matrix_info->type != DiskCache)
1136 {
1137 (void) memcpy((unsigned char *) matrix_info->elements+i*
1138 (MagickOffsetType) matrix_info->stride,value,matrix_info->stride);
1139 return(MagickTrue);
1140 }
1141 count=WriteMatrixElements(matrix_info,i*(MagickOffsetType)
1142 matrix_info->stride,matrix_info->stride,(unsigned char *) value);
1143 if (count != (MagickOffsetType) matrix_info->stride)
1144 return(MagickFalse);
1145 return(MagickTrue);
1146}