1 /* Parse ".desc" files from mosaiced images to generate (x,y) offsets for
2 * every sub-image. Find all overlap stats and solve balancing with LMS.
3 * Regenerate mosaic, with balancing fixed.
4 *
5 * 1/12/93 JC
6 * - first version, unfinished!
7 * 6/9/95 JC
8 * - LMS fixed, now works, more or less
9 * 12/9/95 JC
10 * - now does positions correctly too
11 * - ignores trivial overlaps
12 * 19/9/95 JC
13 * - prints correct number of balance factors!
14 * 10/11/95 JC
15 * - now tracks im_copy() calls too, so you can save sub-images
16 * 12/1/96 JC
17 * - slightly clearer diagnostics
18 * - better centre of factors around 1.0 with log() average
19 * 1/3/96 JC
20 * - new im_global_balance_float variant lets our caller adjust factor
21 * range if output has burn-out
22 * - im_global_balance_search uses the above to produce scaled output ...
23 * very slow!
24 * 11/3/96 JC
25 * - now tries current directory too for input files
26 * 22/3/96 JC
27 * - horrible bug in position finding! now fixed
28 * 1/8/97 JC
29 * - revised for new mosaic functions and non-square images
30 * 12/9/97 JC
31 * - code for im_lrmosaic1() support
32 * - output type == input type, so works for short images too
33 * 6/1/99 JC
34 * - new gamma parameter, do scale in linear space
35 * - removed _search version, as can now be done with ip
36 * - renamed _float to f suffix, in line with im_conv()/im_convf()
37 * 15/2/00 JC
38 * - balancef() did not scale in linear space
39 * 2/2/01 JC
40 * - added tunable max blend width
41 * 7/11/01 JC
42 * - global_balance.h broken out for im_remosaic()
43 * 25/02/02 JC
44 * - better transform function scheme
45 * 21/3/01 JC
46 * - quicker bailout on error
47 * 8/11/02 JC
48 * - add <> around file names so you can have spaces :(
49 * 9/12/02 JC
50 * - track original params and always reuse them ... makes us proof
51 * against geo reconstruct errors
52 * 10/3/03 JC
53 * - weed out overlaps which contain only transparent pixels
54 * 4/1/07
55 * - switch to new history thing, switch im_errormsg() too
56 * 24/1/11
57 * - gtk-doc
58 * 12/7/12
59 * - always allocate local to an output descriptor ... stops ref cycles
60 * with the new base class
61 * 18/6/20 kleisauke
62 * - convert to vips8
63 */
64
65 /*
66
67 This file is part of VIPS.
68
69 VIPS is free software; you can redistribute it and/or modify
70 it under the terms of the GNU Lesser General Public License as published by
71 the Free Software Foundation; either version 2 of the License, or
72 (at your option) any later version.
73
74 This program is distributed in the hope that it will be useful,
75 but WITHOUT ANY WARRANTY; without even the implied warranty of
76 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
77 GNU Lesser General Public License for more details.
78
79 You should have received a copy of the GNU Lesser General Public License
80 along with this program; if not, write to the Free Software
81 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
82 02110-1301 USA
83
84 */
85
86 /*
87
88 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
89
90 */
91
92 /* Strategy: build a tree describing the file
93 * relationships in the desc file, then walk that passing constraints
94 * back up to the root. Look up file names in symbol_table.
95 */
96
97 /* Define for debug output.
98 #define DEBUG
99 */
100
101 #ifdef HAVE_CONFIG_H
102 #include <config.h>
103 #endif /*HAVE_CONFIG_H*/
104 #include <vips/intl.h>
105
106 #include <stdio.h>
107 #include <stdlib.h>
108 #include <string.h>
109 #include <stdarg.h>
110 #include <math.h>
111
112 #include <vips/vips.h>
113 #include <vips/transform.h>
114 #include <vips/internal.h>
115
116 #include "pmosaicing.h"
117 #include "global_balance.h"
118
119 #define MAX_ITEMS (50)
120
121 /* How pix an overlap has to be (in pixels) before we think it's trivial and
122 * we ignore it.
123 */
124 #define TRIVIAL (20 * 20)
125
126 /* Break a string into a list of strings. Write '\0's into the string. out
127 * needs to be MAX_FILES long. -1 for error, otherwise number of args found.
128
129 "<fred> <jim poop> <sn aff le>"
130
131 out[0] = "fred"
132 out[1] = "jim poop"
133 out[2] = "sn aff le"
134
135 */
136 static int
break_items(char * line,char ** out)137 break_items( char *line, char **out )
138 {
139 int i;
140 char *p;
141
142 for( i = 0; i < MAX_ITEMS; i++ ) {
143 /* Skip to first '<'.
144 */
145 if( !(p = strchr( line, '<' )) )
146 break;
147
148 out[i] = line = p + 1;
149
150 if( !(p = strchr( line, '>' )) ) {
151 vips_error( "break_files", "%s", _( "no matching '>'" ) );
152 return( -1 );
153 }
154
155 *p = '\0';
156 line = p + 1;
157 }
158
159 if( i == MAX_ITEMS ) {
160 vips_error( "break_files", "%s", _( "too many items" ) );
161 return( -1 );
162 }
163
164 return( i );
165 }
166
167 /* Try to open a file. If full path fails, try the current directory.
168 */
169 VipsImage *
vips__global_open_image(SymbolTable * st,char * name)170 vips__global_open_image( SymbolTable *st, char *name )
171 {
172 char *basename;
173 VipsImage *image;
174
175 if( !(image = vips_image_new_from_file( name, NULL ))) {
176 /* TODO(kleisauke): Is this behavior the same as im_skip_dir?
177 * i.e. could we open a filename which came
178 * from a win32 (`\\`) on a *nix machine?
179 */
180 basename = g_path_get_basename( name );
181
182 if( !(image = vips_image_new_from_file( basename, NULL ))) {
183 g_free( basename );
184 return( NULL );
185 }
186
187 g_free( basename );
188 }
189
190 vips_object_local( st->im, image );
191
192 return( image );
193 }
194
195 static void
junk_node(VipsImage * image,JoinNode * node)196 junk_node( VipsImage *image, JoinNode *node )
197 {
198 VIPS_FREEF( g_slist_free, node->overlaps );
199 }
200
201 /* Hash from a filename to an index into symbol_table.
202 */
203 static int
hash(char * n)204 hash( char *n )
205 {
206 int i;
207 int r = 0;
208 int l = strlen( n );
209
210 for( i = 0; i < l; i++ )
211 r = ((r + n[i]) * 43) & 0xffffff;
212
213 return( r % SYM_TAB_SIZE );
214 }
215
216 /* Make a leaf for a file.
217 */
218 static JoinNode *
build_node(SymbolTable * st,char * name)219 build_node( SymbolTable *st, char *name )
220 {
221 JoinNode *node = VIPS_NEW( st->im, JoinNode );
222 int n = hash( name );
223
224 /* Fill fields.
225 */
226 if( !node || !(node->name =
227 vips_strdup( VIPS_OBJECT( st->im ), name )) )
228 return( NULL );
229
230 node->type = JOIN_LEAF;
231 node->dirty = 0;
232 node->mwidth = -2;
233 node->st = st;
234 vips__transform_init( &node->cumtrn );
235 node->trnim = NULL;
236 node->arg1 = NULL;
237 node->arg2 = NULL;
238 node->overlaps = NULL;
239 node->im = NULL;
240 node->index = 0;
241
242 g_signal_connect( st->im, "close",
243 G_CALLBACK( junk_node ), node );
244
245 /* Try to open.
246 */
247 if( (node->im = vips__global_open_image( st, name )) ) {
248 /* There is a file there - set width and height.
249 */
250 node->cumtrn.oarea.width = node->im->Xsize;
251 node->cumtrn.oarea.height = node->im->Ysize;
252 }
253 else {
254 /* Clear the error buffer to lessen confusion.
255 */
256 vips_error_clear();
257 }
258
259 st->table[n] = g_slist_prepend( st->table[n], node );
260
261 return( node );
262 }
263
264 /* Make a new overlap struct.
265 */
266 static OverlapInfo *
build_overlap(JoinNode * node,JoinNode * other,VipsRect * overlap)267 build_overlap( JoinNode *node, JoinNode *other, VipsRect *overlap )
268 {
269 OverlapInfo *lap = VIPS_NEW( node->st->im, OverlapInfo );
270
271 if( !lap )
272 return( NULL );
273
274 lap->node = node;
275 lap->other = other;
276 lap->overlap = *overlap;
277 lap->nstats = NULL;
278 lap->ostats = NULL;
279 node->overlaps = g_slist_prepend( node->overlaps, lap );
280 node->st->novl++;
281
282 return( lap );
283 }
284
285 static void
overlap_destroy(OverlapInfo * lap)286 overlap_destroy( OverlapInfo *lap )
287 {
288 JoinNode *node = lap->node;
289
290 node->overlaps = g_slist_remove( node->overlaps, lap );
291 g_assert( node->st->novl > 0 );
292 node->st->novl--;
293 }
294
295 static void
junk_table(VipsImage * image,SymbolTable * st)296 junk_table( VipsImage *image, SymbolTable *st ) {
297 int i;
298
299 for( i = 0; i < st->sz; i++ )
300 VIPS_FREEF( g_slist_free, st->table[i] );
301 }
302
303 /* Build a new symbol table.
304 */
305 SymbolTable *
vips__build_symtab(VipsImage * out,int sz)306 vips__build_symtab( VipsImage *out, int sz )
307 {
308 SymbolTable *st = VIPS_NEW( out, SymbolTable );
309 int i;
310
311 if( !st ||
312 !(st->table = VIPS_ARRAY( out, sz, GSList * )) )
313 return( NULL );
314 st->sz = sz;
315 st->im = out;
316 st->novl = 0;
317 st->nim = 0;
318 st->njoin = 0;
319 st->root = NULL;
320 st->leaf = NULL;
321 st->fac = NULL;
322
323 g_signal_connect( out, "close",
324 G_CALLBACK( junk_table ), st );
325
326 for( i = 0; i < sz; i++ )
327 st->table[i] = NULL;
328
329 return( st );
330 }
331
332 /* Does this node have this file name?
333 */
334 static JoinNode *
test_name(JoinNode * node,char * name,void * b)335 test_name( JoinNode *node, char *name, void *b )
336 {
337 if( strcmp( node->name, name ) == 0 )
338 return( node );
339 else
340 return( NULL );
341 }
342
343 /* Look up a filename in the symbol_table.
344 */
345 static JoinNode *
find_node(SymbolTable * st,char * name)346 find_node( SymbolTable *st, char *name )
347 {
348 return( vips_slist_map2( st->table[hash( name )],
349 (VipsSListMap2Fn) test_name, name, NULL ) );
350 }
351
352 /* Given a name: return either the existing node for that name, or a new node
353 * we have made.
354 */
355 static JoinNode *
add_node(SymbolTable * st,char * name)356 add_node( SymbolTable *st, char *name )
357 {
358 JoinNode *node;
359
360 if( !(node = find_node( st, name )) &&
361 !(node = build_node( st, name )) )
362 return( NULL );
363
364 return( node );
365 }
366
367 /* Map a user function over the whole of the symbol table.
368 */
369 void *
vips__map_table(SymbolTable * st,VipsSListMap2Fn fn,void * a,void * b)370 vips__map_table( SymbolTable *st, VipsSListMap2Fn fn, void *a, void *b )
371 {
372 int i;
373 void *r;
374
375 for( i = 0; i < st->sz; i++ )
376 if( (r = vips_slist_map2( st->table[i], fn, a, b )) )
377 return( r );
378
379 return( NULL );
380 }
381
382 /* Set the dirty field on a join.
383 */
384 static void *
set_dirty(JoinNode * node,int state,void * b)385 set_dirty( JoinNode *node, int state, void *b )
386 {
387 node->dirty = state;
388
389 return( NULL );
390 }
391
392 /* Clean the whole table.
393 */
394 static void
clean_table(SymbolTable * st)395 clean_table( SymbolTable *st )
396 {
397 (void) vips__map_table( st,
398 (VipsSListMap2Fn) set_dirty, (void *) 0, NULL );
399 }
400
401 /* Do geometry calculations on a node, assuming geo is up to date for any
402 * children.
403 */
404 static void
calc_geometry(JoinNode * node)405 calc_geometry( JoinNode *node )
406 {
407 VipsRect um;
408
409 switch( node->type ) {
410 case JOIN_LR:
411 case JOIN_TB:
412 case JOIN_LRROTSCALE:
413 case JOIN_TBROTSCALE:
414 /* Join two areas.
415 */
416 vips_rect_unionrect( &node->arg1->cumtrn.oarea,
417 &node->arg2->cumtrn.oarea, &um );
418 node->cumtrn.iarea.left = 0;
419 node->cumtrn.iarea.top = 0;
420 node->cumtrn.iarea.width = um.width;
421 node->cumtrn.iarea.height = um.height;
422 vips__transform_set_area( &node->cumtrn );
423 break;
424
425 case JOIN_CP:
426 /* Copy from child.
427 */
428 node->cumtrn = node->arg1->cumtrn;
429 break;
430
431 case JOIN_LEAF:
432 /* Just use leaf dimensions, if there are any.
433 */
434 if( node->im ) {
435 node->cumtrn.iarea.left = 0;
436 node->cumtrn.iarea.top = 0;
437 node->cumtrn.iarea.width = node->im->Xsize;
438 node->cumtrn.iarea.height = node->im->Ysize;
439 vips__transform_set_area( &node->cumtrn );
440 }
441 break;
442
443 default:
444 vips_error_exit( "internal error #98356" );
445 /*NOTREACHED*/
446 }
447 }
448
449 /* Propagate a transform down a tree. If dirty is set, we've been here before,
450 * so there is a doubling up of this node. If this is a leaf, then we have the
451 * same leaf twice (which, in fact, we can cope with); if this is a node, we
452 * have circularity.
453 */
454 static int
propagate_transform(JoinNode * node,VipsTransformation * trn)455 propagate_transform( JoinNode *node, VipsTransformation *trn )
456 {
457 if( !node )
458 return( 0 );
459
460 if( node->dirty && node->arg1 && node->arg2 ) {
461 vips_error( "vips_global_balance",
462 "%s", _( "circularity detected" ) );
463 return( -1 );
464 }
465 node->dirty = 1;
466
467 /* Transform our children.
468 */
469 if( propagate_transform( node->arg1, trn ) ||
470 propagate_transform( node->arg2, trn ) )
471 return( -1 );
472
473 /* Transform us, and recalculate our position and size.
474 */
475 vips__transform_add( &node->cumtrn, trn, &node->cumtrn );
476 calc_geometry( node );
477
478 return( 0 );
479 }
480
481 /* Ah ha! A leaf is actually made up of two smaller files with an lr or a tb
482 * merge. Turn a leaf node into a join node. Propagate the transform down
483 * arg2's side of the tree.
484 */
485 static int
make_join(SymbolTable * st,JoinType type,JoinNode * arg1,JoinNode * arg2,JoinNode * out,double a,double b,double dx,double dy,int mwidth)486 make_join( SymbolTable *st, JoinType type,
487 JoinNode *arg1, JoinNode *arg2, JoinNode *out,
488 double a, double b, double dx, double dy, int mwidth )
489 {
490 VipsTransformation trn;
491
492 /* Check output is ok.
493 */
494 if( out->type != JOIN_LEAF ) {
495 vips_error( "vips_global_balance",
496 _( "image \"%s\" used twice as output" ), out->name );
497 return( -1 );
498 }
499
500 /* Fill fields.
501 */
502 out->type = type;
503 out->mwidth = mwidth;
504 out->a = a;
505 out->b = b;
506 out->dx = dx;
507 out->dy = dy;
508 out->arg1 = arg1;
509 out->arg2 = arg2;
510 out->thistrn.a = a;
511 out->thistrn.b = -b;
512 out->thistrn.c = b;
513 out->thistrn.d = a;
514 out->thistrn.idx = 0;
515 out->thistrn.idy = 0;
516 out->thistrn.odx = dx;
517 out->thistrn.ody = dy;
518
519 /* Clean the table and propagate the transform down the RHS of the
520 * graph.
521 */
522 clean_table( st );
523 if( propagate_transform( arg2, &out->thistrn ) )
524 return( -1 );
525
526 /* Find the position and size of our output.
527 */
528 calc_geometry( out );
529
530 /* Now normalise the result, so that out is at (0,0) again.
531 */
532 trn.a = 1.0;
533 trn.b = 0.0;
534 trn.c = 0.0;
535 trn.d = 1.0;
536 trn.idx = 0;
537 trn.idy = 0;
538 trn.odx = -out->cumtrn.oarea.left;
539 trn.ody = -out->cumtrn.oarea.top;
540 clean_table( st );
541 if( propagate_transform( out, &trn ) )
542 return( -1 );
543
544 return( 0 );
545 }
546
547 /* Make a copy node.
548 */
549 static int
make_copy(SymbolTable * st,JoinNode * before,JoinNode * after)550 make_copy( SymbolTable *st, JoinNode *before, JoinNode *after )
551 {
552 /* Check output is ok.
553 */
554 if( after->type != JOIN_LEAF ) {
555 vips_error( "vips_global_balance",
556 _( "image \"%s\" used twice as output" ), after->name );
557 return( -1 );
558 }
559
560 /* Fill fields.
561 */
562 after->type = JOIN_CP;
563 after->arg1 = before;
564 after->arg2 = NULL;
565
566 /* Copy over the position and size from the before to the after.
567 */
568 calc_geometry( after );
569
570 return( 0 );
571 }
572
573 /* Process a single .desc line.
574 */
575 static int
process_line(SymbolTable * st,const char * text)576 process_line( SymbolTable *st, const char *text )
577 {
578 char line[1024];
579
580 #ifdef DEBUG
581 printf( "read: %s\n", text );
582 #endif /*DEBUG*/
583
584 /* We destroy line during the parse.
585 */
586 vips_strncpy( line, text, 1024 );
587
588 if( vips_isprefix( "#LRJOIN ", line ) ||
589 vips_isprefix( "#TBJOIN ", line ) ) {
590 /* Yes: magic join command. Break into tokens. Format is eg.
591
592 #LRJOIN <left> <right> <out> <x> <y> [<mwidth>]
593
594 */
595 char *item[MAX_ITEMS];
596 int nitems;
597 JoinType type;
598 JoinNode *arg1, *arg2, *join;
599 int dx, dy, mwidth;
600
601 if( (nitems = break_items( line, item )) < 0 )
602 return( -1 );
603 if( nitems != 5 && nitems != 6 ) {
604 vips_error( "global_balance",
605 "%s", _( "bad number of args in join line" ) );
606 return( -1 );
607 }
608
609 if( !(arg1 = add_node( st, item[0] )) ||
610 !(arg2 = add_node( st, item[1] )) ||
611 !(join = add_node( st, item[2] )) )
612 return( -1 );
613 dx = atoi( item[3] );
614 dy = atoi( item[4] );
615 if( nitems == 6 )
616 mwidth = atoi( item[5] );
617 else
618 mwidth = -1;
619 if( vips_isprefix( "#LRJOIN ", line ) )
620 type = JOIN_LR;
621 else
622 type = JOIN_TB;
623
624 if( make_join( st, type, arg1, arg2,
625 join, 1.0, 0.0, dx, dy, mwidth ) )
626 return( -1 );
627 }
628 else if( vips_isprefix( "#LRROTSCALE ", line ) ||
629 vips_isprefix( "#TBROTSCALE ", line ) ) {
630 /* Rot + scale. Format is eg.
631
632 #LRROTSCALE <left> <right> <out> \
633 <a> <b> <x> <y> [<mwidth>]
634
635 */
636 char *item[MAX_ITEMS];
637 int nitems;
638 JoinType type;
639 JoinNode *arg1, *arg2, *join;
640 double a, b, dx, dy;
641 int mwidth;
642
643 if( (nitems = break_items( line, item )) < 0 )
644 return( -1 );
645 if( nitems != 7 && nitems != 8 ) {
646 vips_error( "global_balance",
647 "%s", _( "bad number of args in join1 line" ) );
648 return( -1 );
649 }
650
651 if( !(arg1 = add_node( st, item[0] )) ||
652 !(arg2 = add_node( st, item[1] )) ||
653 !(join = add_node( st, item[2] )) )
654 return( -1 );
655 a = g_ascii_strtod( item[3], NULL );
656 b = g_ascii_strtod( item[4], NULL );
657 dx = g_ascii_strtod( item[5], NULL );
658 dy = g_ascii_strtod( item[6], NULL );
659 if( nitems == 8 )
660 mwidth = atoi( item[7] );
661 else
662 mwidth = -1;
663 if( vips_isprefix( "#LRROTSCALE ", line ) )
664 type = JOIN_LRROTSCALE;
665 else
666 type = JOIN_TBROTSCALE;
667
668 if( make_join( st, type, arg1, arg2,
669 join, a, b, dx, dy, mwidth ) )
670 return( -1 );
671 }
672 else if( vips_isprefix( "copy ", line ) ) {
673 /* vips_copy() call ... make a JOIN_CP node.
674 */
675 char *item[MAX_ITEMS];
676 int nitems;
677 JoinNode *before, *after;
678
679 if( (nitems = break_items( line, item )) < 0 )
680 return( -1 );
681 if( nitems != 2 ) {
682 vips_error( "global_balance",
683 "%s", _( "bad number of args in copy line" ) );
684 return( -1 );
685 }
686
687 if( !(before = add_node( st, item[0] )) ||
688 !(after = add_node( st, item[1] )) ||
689 make_copy( st, before, after ) )
690 return( -1 );
691 }
692
693 return( 0 );
694 }
695
696 /* Set the dirty flag on any nodes we reference.
697 */
698 static void *
set_referenced(JoinNode * node,void * a,void * b)699 set_referenced( JoinNode *node, void *a, void *b )
700 {
701 if( node->arg1 )
702 node->arg1->dirty = 1;
703 if( node->arg2 )
704 node->arg2->dirty = 1;
705
706 return( NULL );
707 }
708
709 /* Is this a root node? Should be clean.
710 */
711 static void *
is_root(JoinNode * node,void * a,void * b)712 is_root( JoinNode *node, void *a, void *b )
713 {
714 if( !node->dirty )
715 return( (void *) node );
716 else
717 return( NULL );
718 }
719
720 /* Scan the symbol table, looking for a node which no node references.
721 */
722 static JoinNode *
find_root(SymbolTable * st)723 find_root( SymbolTable *st )
724 {
725 JoinNode *root;
726
727 /* Clean the table, then scan it, setting all pointed-to nodes dirty.
728 */
729 clean_table( st );
730 vips__map_table( st, (VipsSListMap2Fn) set_referenced, NULL, NULL );
731
732 /* Look for the first clean symbol.
733 */
734 root = (JoinNode *) vips__map_table( st,
735 (VipsSListMap2Fn) is_root, NULL, NULL );
736
737 /* No root? Hot dang!
738 */
739 if( !root ) {
740 vips_error( "vips_global_balance",
741 "%s", _( "mosaic root not found in desc file\n"
742 "is this really a mosaiced image?" ) );
743 return( NULL );
744 }
745
746 /* Now dirty that - then if there are any more clean symbols, we have
747 * more than one root.
748 */
749 root->dirty = 1;
750 if( vips__map_table( st, (VipsSListMap2Fn) is_root, NULL, NULL ) ) {
751 vips_error( "vips_global_balance",
752 "%s", _( "more than one root" ) );
753 return( NULL );
754 }
755
756 return( root );
757 }
758
759 /* Walk history_list and parse each line.
760 */
761 int
vips__parse_desc(SymbolTable * st,VipsImage * in)762 vips__parse_desc( SymbolTable *st, VipsImage *in )
763 {
764 GSList *p;
765
766 for( p = in->history_list; p; p = p->next ) {
767 GValue *value = (GValue *) p->data;
768
769 g_assert( G_VALUE_TYPE( value ) == VIPS_TYPE_REF_STRING );
770
771 if( process_line( st, vips_value_get_ref_string( value, NULL ) ) )
772 return( -1 );
773 }
774
775 /* Find root.
776 */
777 if( !(st->root = find_root( st )) )
778 return( -1 );
779
780 return( 0 );
781 }
782
783 /* Count and index all leaf images.
784 */
785 static void *
count_leaves(JoinNode * node,void * a,void * b)786 count_leaves( JoinNode *node, void *a, void *b )
787 {
788 if( node->type == JOIN_LEAF ) {
789 node->index = node->st->nim;
790 node->st->nim++;
791 }
792
793 return( NULL );
794 }
795
796 #ifdef DEBUG
797 /* Print a JoinNode.
798 */
799 static void
print_node(JoinNode * node)800 print_node( JoinNode *node )
801 {
802 char *basename = g_path_get_basename( node->name );
803 printf( "%s, position %dx%d, size %dx%d, index %d\n",
804 basename,
805 node->cumtrn.oarea.left, node->cumtrn.oarea.top,
806 node->cumtrn.oarea.width, node->cumtrn.oarea.height,
807 node->index );
808 g_free( basename );
809 }
810 #endif /*DEBUG*/
811
812 #ifdef DEBUG
813 /* Print a leaf.
814 */
815 static void *
print_leaf(JoinNode * node,void * a,void * b)816 print_leaf( JoinNode *node, void *a, void *b )
817 {
818 if( node->type == JOIN_LEAF )
819 print_node( node );
820
821 return( NULL );
822 }
823 #endif /*DEBUG*/
824
825 /* Count all join nodes.
826 */
827 static void *
count_joins(JoinNode * node,void * a,void * b)828 count_joins( JoinNode *node, void *a, void *b )
829 {
830 if( node->type == JOIN_TB ||
831 node->type == JOIN_LR ||
832 node->type == JOIN_LRROTSCALE ||
833 node->type == JOIN_TBROTSCALE )
834 node->st->njoin++;
835
836 return( NULL );
837 }
838
839 #ifdef DEBUG
840 /* Print a few spaces.
841 */
842 static void
spc(int n)843 spc( int n )
844 {
845 int i;
846
847 for( i = 0; i < n; i++ )
848 printf( " " );
849 }
850 #endif /*DEBUG*/
851
852 #ifdef DEBUG
853 static char *
JoinType2char(JoinType type)854 JoinType2char( JoinType type )
855 {
856 switch( type ) {
857 case JOIN_LR: return( "JOIN_LR" );
858 case JOIN_TB: return( "JOIN_TB" );
859 case JOIN_LRROTSCALE: return( "JOIN_LRROTSCALE" );
860 case JOIN_TBROTSCALE: return( "JOIN_TBROTSCALE" );
861 case JOIN_CP: return( "JOIN_CP" );
862 case JOIN_LEAF: return( "JOIN_LEAF" );
863
864 default:
865 vips_error_exit( "internal error #9275" );
866 /*NOTEACHED*/
867
868 return( NULL );
869 }
870 }
871 #endif /*DEBUG*/
872
873 #ifdef DEBUG
874 /* Print a join node.
875 */
876 static void *
print_joins(JoinNode * node,int indent)877 print_joins( JoinNode *node, int indent )
878 {
879 char *basename = g_path_get_basename( node->name );
880
881 switch( node->type ) {
882 case JOIN_TB:
883 case JOIN_LR:
884 case JOIN_TBROTSCALE:
885 case JOIN_LRROTSCALE:
886 spc( indent );
887 printf( "%s to make %s, size %dx%d, pos. %dx%d, of:\n",
888 JoinType2char( node->type ),
889 basename,
890 node->cumtrn.oarea.width, node->cumtrn.oarea.height,
891 node->cumtrn.oarea.left, node->cumtrn.oarea.top );
892 spc( indent );
893 printf( "reference:\n" );
894 print_joins( node->arg1, indent + 2 );
895 spc( indent );
896 printf( "secondary:\n" );
897 print_joins( node->arg2, indent + 2 );
898 break;
899
900 case JOIN_CP:
901 spc( indent );
902 printf( "copy to make %s of:\n", basename );
903 print_joins( node->arg1, indent + 2 );
904 break;
905
906 case JOIN_LEAF:
907 spc( indent );
908 printf( "input image %s\n", basename );
909 break;
910 }
911
912 g_free( basename );
913
914 return( NULL );
915 }
916 #endif /*DEBUG*/
917
918 #ifdef DEBUG
919 /* Print an overlap.
920 */
921 static void *
print_overlap(OverlapInfo * lap,void * a,void * b)922 print_overlap( OverlapInfo *lap, void *a, void *b )
923 {
924 char *basename_node = g_path_get_basename( lap->node->name );
925 char *basename_other = g_path_get_basename( lap->other->name );
926
927 printf( "-> %s overlaps with %s; (this, other) = (%.4G, %.4G)\n",
928 basename_node,
929 basename_other,
930 *VIPS_MATRIX( lap->nstats, 4, 0 ),
931 *VIPS_MATRIX( lap->ostats, 4, 0 ) );
932
933 g_free( basename_node );
934 g_free( basename_other );
935
936 return( NULL );
937 }
938 #endif /*DEBUG*/
939
940 #ifdef DEBUG
941 /* Print the overlaps on a leaf.
942 */
943 static void *
print_overlaps(JoinNode * node,void * a,void * b)944 print_overlaps( JoinNode *node, void *a, void *b )
945 {
946 char *basename;
947
948 if( node->type == JOIN_LEAF && g_slist_length( node->overlaps ) > 0 ) {
949 basename = g_path_get_basename( node->name );
950 printf( "overlap of %s with:\n", basename );
951 g_free( basename );
952 vips_slist_map2( node->overlaps,
953 (VipsSListMap2Fn) print_overlap, NULL, NULL );
954 }
955
956 return( NULL );
957 }
958 #endif /*DEBUG*/
959
960 #ifdef DEBUG
961 /* Print and accumulate the error on an overlap.
962 */
963 static void *
print_overlap_error(OverlapInfo * lap,double * fac,double * total)964 print_overlap_error( OverlapInfo *lap, double *fac, double *total )
965 {
966 char *basename_other = g_path_get_basename( lap->other->name );
967 double na = *VIPS_MATRIX( lap->nstats, 4, 0 );
968 double oa = *VIPS_MATRIX( lap->ostats, 4, 0 );
969 double err;
970
971 if( fac ) {
972 na *= fac[lap->node->index];
973 oa *= fac[lap->other->index];
974 }
975
976 err = na - oa;
977
978 printf( "-> file %s, error = %g\n",
979 basename_other, err );
980 *total += err * err;
981
982 g_free( basename_other );
983
984 return( NULL );
985 }
986 #endif /*DEBUG*/
987
988 #ifdef DEBUG
989 /* Print and accumulate the overlap errors on a leaf.
990 */
991 static void *
print_overlap_errors(JoinNode * node,double * fac,double * total)992 print_overlap_errors( JoinNode *node, double *fac, double *total )
993 {
994 char *basename;
995
996 if( node->type == JOIN_LEAF && g_slist_length( node->overlaps ) > 0 ) {
997 basename = g_path_get_basename( node->name );
998 printf( "overlap of %s (index %d) with:\n", basename,
999 node->index );
1000 g_free( basename );
1001 vips_slist_map2( node->overlaps,
1002 (VipsSListMap2Fn) print_overlap_error, fac, total );
1003 }
1004
1005 return( NULL );
1006 }
1007 #endif /*DEBUG*/
1008
1009 /* Extract a rect.
1010 */
1011 static int
extract_rect(VipsImage * in,VipsImage ** out,VipsRect * r)1012 extract_rect( VipsImage *in, VipsImage **out, VipsRect *r )
1013 {
1014 return( vips_extract_area( in, out,
1015 r->left, r->top, r->width, r->height, NULL ) );
1016 }
1017
1018 /* Two images overlap in an area ... make a mask the size of the area, which
1019 * has 255 for every pixel where both images are non-zero.
1020 */
1021 static int
make_overlap_mask(VipsImage * mem,VipsImage * ref,VipsImage * sec,VipsImage ** mask,VipsRect * rarea,VipsRect * sarea)1022 make_overlap_mask( VipsImage *mem,
1023 VipsImage *ref, VipsImage *sec, VipsImage **mask,
1024 VipsRect *rarea, VipsRect *sarea )
1025 {
1026 VipsImage **t = (VipsImage **)
1027 vips_object_local_array( VIPS_OBJECT( mem ), 6 );
1028
1029 if( extract_rect( ref, &t[0], rarea ) ||
1030 extract_rect( sec, &t[1], sarea ) ||
1031 vips_extract_band( t[0], &t[2], 0, NULL ) ||
1032 vips_extract_band( t[1], &t[3], 0, NULL ) ||
1033 vips_notequal_const1( t[2], &t[4], 0.0, NULL ) ||
1034 vips_notequal_const1( t[3], &t[5], 0.0, NULL ) ||
1035 vips_andimage( t[4], t[5], mask, NULL ) )
1036 return( -1 );
1037
1038 return( 0 );
1039 }
1040
1041 /* Find the number of non-zero pixels in a mask image.
1042 */
1043 static int
count_nonzero(VipsImage * in,gint64 * count)1044 count_nonzero( VipsImage *in, gint64 *count )
1045 {
1046 double avg;
1047
1048 if( vips_avg( in, &avg, NULL ) )
1049 return( -1 );
1050 *count = (avg * VIPS_IMAGE_N_PELS( in )) / 255.0;
1051
1052 return( 0 );
1053 }
1054
1055 /* Find stats on an area of an IMAGE ... consider only pixels for which the
1056 * mask is true.
1057 */
1058 static VipsImage *
find_image_stats(VipsImage * mem,VipsImage * in,VipsImage * mask,VipsRect * area)1059 find_image_stats( VipsImage *mem,
1060 VipsImage *in, VipsImage *mask, VipsRect *area )
1061 {
1062 VipsImage **t = (VipsImage **)
1063 vips_object_local_array( VIPS_OBJECT( mem ), 5 );
1064
1065 gint64 count;
1066
1067 /* Extract area, build black image, mask out pixels we want.
1068 */
1069 if( extract_rect( in, &t[0], area ) ||
1070 vips_black( &t[1], t[0]->Xsize, t[0]->Ysize,
1071 "bands", t[0]->Bands,
1072 NULL ) ||
1073 vips_cast( t[1], &t[2], t[0]->BandFmt, NULL ) ||
1074 vips_ifthenelse( mask, t[0], t[2], &t[3], NULL ) )
1075 return( NULL );
1076
1077 /* Get stats from masked image.
1078 */
1079 if( vips_stats( t[3], &t[4], NULL ) )
1080 return( NULL );
1081
1082 /* Number of non-zero pixels in mask.
1083 */
1084 if( count_nonzero( mask, &count ) )
1085 return( NULL );
1086
1087 /* And scale masked average to match.
1088 */
1089 *VIPS_MATRIX( t[4], 4, 0 ) *=
1090 (double) count / VIPS_IMAGE_N_PELS( mask );
1091
1092 /* Yuk! Zap the deviation column with the pixel count. Used later to
1093 * determine if this is likely to be a significant overlap.
1094 */
1095 *VIPS_MATRIX( t[4], 5, 0 ) = count;
1096
1097 #ifdef DEBUG
1098 if( count == 0 )
1099 g_warning( "global_balance %s", _( "empty overlap!" ) );
1100 #endif /*DEBUG*/
1101
1102 return( t[4] );
1103 }
1104
1105 /* Find the stats for an overlap struct.
1106 */
1107 static int
find_overlap_stats(OverlapInfo * lap)1108 find_overlap_stats( OverlapInfo *lap )
1109 {
1110 VipsImage *mem = lap->node->st->im;
1111 VipsImage **t = (VipsImage **)
1112 vips_object_local_array( VIPS_OBJECT( mem ), 1 );
1113
1114 VipsRect rarea, sarea;
1115
1116 /* Translate the overlap area into the coordinate scheme for the main
1117 * node.
1118 */
1119 rarea = lap->overlap;
1120 rarea.left -= lap->node->cumtrn.oarea.left;
1121 rarea.top -= lap->node->cumtrn.oarea.top;
1122
1123 /* Translate the overlap area into the coordinate scheme for the other
1124 * node.
1125 */
1126 sarea = lap->overlap;
1127 sarea.left -= lap->other->cumtrn.oarea.left;
1128 sarea.top -= lap->other->cumtrn.oarea.top;
1129
1130 /* Make a mask for the overlap.
1131 */
1132 if( make_overlap_mask( mem,
1133 lap->node->trnim, lap->other->trnim, &t[0], &rarea, &sarea ) )
1134 return( -1 );
1135
1136 /* Find stats for that area.
1137 */
1138 if( !(lap->nstats = find_image_stats( mem,
1139 lap->node->trnim, t[0], &rarea )) )
1140 return( -1 );
1141 if( !(lap->ostats = find_image_stats( mem,
1142 lap->other->trnim, t[0], &sarea )) )
1143 return( -1 );
1144
1145 return( 0 );
1146 }
1147
1148 /* Sub-fn. of below.
1149 */
1150 static void *
overlap_eq(OverlapInfo * this,JoinNode * node,void * b)1151 overlap_eq( OverlapInfo *this, JoinNode *node, void *b )
1152 {
1153 if( this->other == node )
1154 return( this );
1155 else
1156 return( NULL );
1157 }
1158
1159 /* Is this an overlapping leaf? If yes, add to overlap list.
1160 */
1161 static void *
test_overlap(JoinNode * other,JoinNode * node,void * b)1162 test_overlap( JoinNode *other, JoinNode *node, void *b )
1163 {
1164 VipsRect overlap;
1165 OverlapInfo *lap;
1166
1167 /* Is other a suitable leaf to overlap with node?
1168 */
1169 if( other->type != JOIN_LEAF || node == other )
1170 return( NULL );
1171
1172 /* Is there an overlap?
1173 */
1174 vips_rect_intersectrect( &node->cumtrn.oarea, &other->cumtrn.oarea,
1175 &overlap );
1176 if( vips_rect_isempty( &overlap ) )
1177 return( NULL );
1178
1179 /* Is this a trivial overlap? Ignore it if it is.
1180 */
1181 if( overlap.width * overlap.height < TRIVIAL )
1182 /* Too few pixels.
1183 */
1184 return( NULL );
1185
1186 /* Have we already added this overlap the other way around? ie. is
1187 * node on other's overlap list?
1188 */
1189 if( vips_slist_map2( other->overlaps,
1190 (VipsSListMap2Fn) overlap_eq, node, NULL ) )
1191 return( NULL );
1192
1193 /* A new overlap - add to overlap list.
1194 */
1195 if( !(lap = build_overlap( node, other, &overlap )) )
1196 return( node );
1197
1198 /* Calculate overlap statistics. Open stuff relative to this, and
1199 * free quickly.
1200 */
1201 if( find_overlap_stats( lap ) )
1202 return( node );
1203
1204 /* If the pixel count either masked overlap is trivial, ignore this
1205 * overlap.
1206 */
1207 if( *VIPS_MATRIX( lap->nstats, 5, 0 ) < TRIVIAL ||
1208 *VIPS_MATRIX( lap->ostats, 5, 0 ) < TRIVIAL ) {
1209 #ifdef DEBUG
1210 printf( "trivial overlap ... junking\n" );
1211 printf( "nstats count = %g, ostats count = %g\n",
1212 *VIPS_MATRIX( lap->nstats, 5, 0 ), *VIPS_MATRIX( lap->ostats, 5, 0 ) );
1213 print_overlap( lap, NULL, NULL );
1214 #endif /*DEBUG*/
1215 overlap_destroy( lap );
1216 }
1217
1218 return( NULL );
1219 }
1220
1221 /* If this is a leaf, look at all other joins for a leaf that overlaps. Aside:
1222 * If this is a leaf, there should be an IMAGE. Flag an error if there is
1223 * not.
1224 */
1225 static void *
find_overlaps(JoinNode * node,SymbolTable * st,void * b)1226 find_overlaps( JoinNode *node, SymbolTable *st, void *b )
1227 {
1228 if( node->type == JOIN_LEAF ) {
1229 /* Check for image.
1230 */
1231 if( !node->im ) {
1232 vips_error( "vips_global_balance",
1233 _( "unable to open \"%s\"" ), node->name );
1234 return( node );
1235 }
1236 if( !node->trnim )
1237 vips_error_exit( "global_balance: sanity failure #9834" );
1238
1239 return( vips__map_table( st,
1240 (VipsSListMap2Fn) test_overlap, node, NULL ) );
1241 }
1242
1243 return( NULL );
1244 }
1245
1246 /* Bundle of variables for matrix creation.
1247 */
1248 typedef struct {
1249 SymbolTable *st; /* Main table */
1250 JoinNode *leaf; /* Leaf to be 1.000 */
1251 VipsImage *K; /* LHS */
1252 VipsImage *M; /* RHS */
1253 int row; /* Current row */
1254 } MatrixBundle;
1255
1256 /* Add a new row for the nominated overlap to the matrices.
1257 */
1258 static void *
add_nominated(OverlapInfo * ovl,MatrixBundle * bun,double * gamma)1259 add_nominated( OverlapInfo *ovl, MatrixBundle *bun, double *gamma )
1260 {
1261 double ns = pow( *VIPS_MATRIX( ovl->nstats, 4, 0 ), 1.0 / (*gamma) );
1262 double os = pow( *VIPS_MATRIX( ovl->ostats, 4, 0 ), 1.0 / (*gamma) );
1263
1264 *VIPS_MATRIX( bun->K, 0, bun->row ) = ns;
1265 *VIPS_MATRIX( bun->M, ovl->other->index - 1, bun->row ) = os;
1266
1267 bun->row++;
1268
1269 return( NULL );
1270 }
1271
1272 /* Add a new row for an ordinary overlap to the matrices.
1273 */
1274 static void *
add_other(OverlapInfo * ovl,MatrixBundle * bun,double * gamma)1275 add_other( OverlapInfo *ovl, MatrixBundle *bun, double *gamma )
1276 {
1277 double ns = -pow( *VIPS_MATRIX( ovl->nstats, 4, 0 ), 1.0 / (*gamma) );
1278 double os = pow( *VIPS_MATRIX( ovl->ostats, 4, 0 ), 1.0 / (*gamma) );
1279
1280 *VIPS_MATRIX( bun->M, ovl->node->index - 1, bun->row ) = ns;
1281 *VIPS_MATRIX( bun->M, ovl->other->index - 1, bun->row ) = os;
1282
1283 bun->row++;
1284
1285 return( NULL );
1286 }
1287
1288 /* Add stuff for node to matrix.
1289 */
1290 static void *
add_row(JoinNode * node,MatrixBundle * bun,double * gamma)1291 add_row( JoinNode *node, MatrixBundle *bun, double *gamma )
1292 {
1293 if( node == bun->leaf )
1294 vips_slist_map2( node->overlaps,
1295 (VipsSListMap2Fn) add_nominated, bun, gamma );
1296 else
1297 vips_slist_map2( node->overlaps,
1298 (VipsSListMap2Fn) add_other, bun, gamma );
1299
1300 return( NULL );
1301 }
1302
1303 /* Fill K and M. leaf is image selected to have factor of 1.000.
1304 */
1305 static void
fill_matrices(SymbolTable * st,double gamma,VipsImage * K,VipsImage * M)1306 fill_matrices( SymbolTable *st, double gamma, VipsImage *K, VipsImage *M )
1307 {
1308 MatrixBundle bun;
1309
1310 bun.st = st;
1311 bun.leaf = st->leaf;
1312 bun.K = K;
1313 bun.M = M;
1314 bun.row = 0;
1315
1316 /* Build matrices.
1317 */
1318 vips__map_table( st, (VipsSListMap2Fn) add_row, &bun, &gamma );
1319 }
1320
1321 /* Used to select the leaf whose coefficient we set to 1.
1322 */
1323 static void *
choose_leaf(JoinNode * node,void * a,void * b)1324 choose_leaf( JoinNode *node, void *a, void *b )
1325 {
1326 if( node->type == JOIN_LEAF )
1327 return( node );
1328
1329 return( NULL );
1330 }
1331
1332 /* Make an image from a node.
1333 */
1334 static VipsImage *
make_mos_image(SymbolTable * st,JoinNode * node,transform_fn tfn,void * a)1335 make_mos_image( SymbolTable *st, JoinNode *node, transform_fn tfn, void *a )
1336 {
1337 VipsImage *im1, *im2, *out;
1338
1339 switch( node->type ) {
1340 case JOIN_LR:
1341 case JOIN_TB:
1342 if( !(im1 = make_mos_image( st, node->arg1, tfn, a )) ||
1343 !(im2 = make_mos_image( st, node->arg2, tfn, a )) )
1344 return( NULL );
1345
1346 if( vips_merge( im1, im2, &out,
1347 node->type == JOIN_LR ?
1348 VIPS_DIRECTION_HORIZONTAL :
1349 VIPS_DIRECTION_VERTICAL,
1350 -node->dx, -node->dy,
1351 "mblend", node->mwidth,
1352 NULL ) )
1353 return( NULL );
1354 vips_object_local( st->im, out );
1355 vips_image_set_string( out, "mosaic-name", node->name );
1356
1357 break;
1358
1359 case JOIN_LRROTSCALE:
1360 case JOIN_TBROTSCALE:
1361 if( !(im1 = make_mos_image( st, node->arg1, tfn, a )) ||
1362 !(im2 = make_mos_image( st, node->arg2, tfn, a )) )
1363 return( NULL );
1364
1365 out = vips_image_new();
1366 vips_object_local( st->im, out );
1367
1368 vips_image_set_string( out, "mosaic-name", node->name );
1369
1370 if( node->type == JOIN_LRROTSCALE ) {
1371 if( vips__lrmerge1( im1, im2, out,
1372 node->a, node->b, node->dx, node->dy,
1373 node->mwidth ) )
1374 return( NULL );
1375 }
1376 else {
1377 if( vips__tbmerge1( im1, im2, out,
1378 node->a, node->b, node->dx, node->dy,
1379 node->mwidth ) )
1380 return( NULL );
1381 }
1382
1383 break;
1384
1385 case JOIN_LEAF:
1386 /* Trivial case!
1387 */
1388 if( !(out = tfn( node, a )) )
1389 return( NULL );
1390
1391 break;
1392
1393 case JOIN_CP:
1394 /* Very trivial case.
1395 */
1396 out = make_mos_image( st, node->arg1, tfn, a );
1397
1398 break;
1399
1400 default:
1401 vips_error_exit( "internal error #982369824375987" );
1402 /*NOTEACHED*/
1403 return( NULL );
1404 }
1405
1406 return( out );
1407 }
1408
1409 /* Re-build mosaic.
1410 */
1411 int
vips__build_mosaic(SymbolTable * st,VipsImage * out,transform_fn tfn,void * a)1412 vips__build_mosaic( SymbolTable *st, VipsImage *out, transform_fn tfn, void *a )
1413 {
1414 JoinNode *root = st->root;
1415 VipsImage *im1, *im2;
1416 VipsImage *x;
1417
1418 switch( root->type ) {
1419 case JOIN_LR:
1420 case JOIN_TB:
1421 if( !(im1 = make_mos_image( st, root->arg1, tfn, a )) ||
1422 !(im2 = make_mos_image( st, root->arg2, tfn, a )) )
1423 return( -1 );
1424
1425 if( vips_merge( im1, im2, &x,
1426 root->type == JOIN_LR ?
1427 VIPS_DIRECTION_HORIZONTAL :
1428 VIPS_DIRECTION_VERTICAL,
1429 -root->dx, -root->dy,
1430 "mblend", root->mwidth,
1431 NULL ) )
1432 return( -1 );
1433 if( vips_image_write( x, out ) ) {
1434 g_object_unref( x );
1435 return( -1 );
1436 }
1437 g_object_unref( x );
1438
1439 break;
1440
1441 case JOIN_LRROTSCALE:
1442 case JOIN_TBROTSCALE:
1443 if( !(im1 = make_mos_image( st, root->arg1, tfn, a )) ||
1444 !(im2 = make_mos_image( st, root->arg2, tfn, a )) )
1445 return( -1 );
1446
1447 if( root->type == JOIN_LRROTSCALE ) {
1448 if( vips__lrmerge1( im1, im2, out,
1449 root->a, root->b, root->dx, root->dy,
1450 root->mwidth ) )
1451 return( -1 );
1452 }
1453 else {
1454 if( vips__tbmerge1( im1, im2, out,
1455 root->a, root->b, root->dx, root->dy,
1456 root->mwidth ) )
1457 return( -1 );
1458 }
1459
1460 break;
1461
1462 case JOIN_LEAF:
1463 /* Trivial case! Just one file in our mosaic.
1464 */
1465 if( !(im1 = tfn( root, a )) ||
1466 vips_image_write( im1, out ) )
1467 return( -1 );
1468
1469 break;
1470
1471 case JOIN_CP:
1472 /* Very trivial case.
1473 */
1474 if( !(im1 = make_mos_image( st, root->arg1, tfn, a )) ||
1475 vips_image_write( im1, out ) )
1476 return( -1 );
1477
1478 break;
1479
1480 default:
1481 vips_error_exit( "internal error #982369824375987" );
1482 /*NOTEACHED*/
1483 }
1484
1485 return( 0 );
1486 }
1487
1488 static int
vips__matrixtranspose(VipsImage * in,VipsImage ** out)1489 vips__matrixtranspose( VipsImage *in, VipsImage **out )
1490 {
1491 int yc, xc;
1492
1493 /* Allocate output matrix.
1494 */
1495 if( !(*out = vips_image_new_matrix( in->Ysize, in->Xsize )) )
1496 return( -1 );
1497
1498 /* Transpose.
1499 */
1500 for( yc = 0; yc < (*out)->Ysize; ++yc )
1501 for( xc = 0; xc < (*out)->Xsize; ++xc )
1502 *VIPS_MATRIX( *out, xc, yc ) = *VIPS_MATRIX( in, yc, xc );
1503
1504 return( 0 );
1505 }
1506
1507 static int
vips__matrixmultiply(VipsImage * in1,VipsImage * in2,VipsImage ** out)1508 vips__matrixmultiply( VipsImage *in1, VipsImage *in2, VipsImage **out )
1509 {
1510 int xc, yc, col;
1511 double sum;
1512 double *mat, *a, *b;
1513 double *s1, *s2;
1514
1515 /* Check matrix sizes.
1516 */
1517 if( in1->Xsize != in2->Ysize ) {
1518 vips_error( "vips__matrixmultiply", "%s", _( "bad sizes" ) );
1519 return( -1 );
1520 }
1521
1522 /* Allocate output matrix.
1523 */
1524 if( !(*out = vips_image_new_matrix( in2->Xsize, in1->Ysize )) )
1525 return( -1 );
1526
1527 /* Multiply.
1528 */
1529 mat = VIPS_MATRIX( *out, 0, 0 );
1530 s1 = VIPS_MATRIX( in1, 0, 0 );
1531
1532 for( yc = 0; yc < in1->Ysize; yc++ ) {
1533 s2 = VIPS_MATRIX( in2, 0, 0 );
1534
1535 for( col = 0; col < in2->Xsize; col++ ) {
1536 /* Get ready to sweep a row.
1537 */
1538 a = s1;
1539 b = s2;
1540
1541 for( sum = 0.0, xc = 0; xc < in1->Xsize; xc++ ) {
1542 sum += *a++ * *b;
1543 b += in2->Xsize;
1544 }
1545
1546 *mat++ = sum;
1547 s2++;
1548 }
1549
1550 s1 += in1->Xsize;
1551 }
1552
1553 return( 0 );
1554 }
1555
1556 /* Find correction factors.
1557 */
1558 static int
find_factors(SymbolTable * st,double gamma)1559 find_factors( SymbolTable *st, double gamma )
1560 {
1561 VipsImage **t = (VipsImage **)
1562 vips_object_local_array( VIPS_OBJECT( st->im ), 7 );
1563
1564 double total;
1565 double avg;
1566 int i;
1567
1568 /* Make output matrices.
1569 */
1570 if( !(t[0] = vips_image_new_matrix( 1, st->novl )) ||
1571 !(t[1] = vips_image_new_matrix( st->nim - 1, st->novl )) )
1572 return( -1 );
1573
1574 fill_matrices( st, gamma, t[0], t[1] );
1575
1576 #ifdef DEBUG
1577 vips_image_write_to_file( t[0], "K.mat", NULL );
1578 vips_image_write_to_file( t[1], "M.mat", NULL );
1579 #endif /*DEBUG*/
1580
1581 /* Calculate LMS.
1582 */
1583 if( vips__matrixtranspose( t[1], &t[2] ) ||
1584 vips__matrixmultiply( t[2], t[1], &t[3] ) ||
1585 vips_matrixinvert( t[3], &t[4], NULL ) ||
1586 vips__matrixmultiply( t[4], t[2], &t[5] ) ||
1587 vips__matrixmultiply( t[5], t[0], &t[6] ) )
1588 return( -1 );
1589
1590 /* Make array of correction factors.
1591 */
1592 if( !(st->fac = VIPS_ARRAY( st->im, st->nim, double )) )
1593 return( -1 );
1594 for( i = 0; i < t[6]->Ysize; i++ )
1595 st->fac[i + 1] = *VIPS_MATRIX( t[6], 0, i );
1596 st->fac[0] = 1.0;
1597
1598 /* Find average balance factor, normalise to that average.
1599 */
1600 total = 0.0;
1601 for( i = 0; i < st->nim; i++ )
1602 total += st->fac[i];
1603 avg = total / st->nim;
1604 for( i = 0; i < st->nim; i++ )
1605 st->fac[i] /= avg;
1606
1607 #ifdef DEBUG
1608 /* Diagnostics!
1609 */
1610 printf( "debugging output for vips_global_balance():\n" );
1611 for( i = 0; i < st->nim; i++ )
1612 printf( "balance factor %d = %g\n", i, st->fac[i] );
1613 total = 0.0;
1614 printf( "Overlap errors:\n" );
1615 vips__map_table( st,
1616 (VipsSListMap2Fn) print_overlap_errors, NULL, &total );
1617 printf( "RMS error = %g\n", sqrt( total / st->novl ) );
1618
1619 total = 0.0;
1620 printf( "Overlap errors after adjustment:\n" );
1621 vips__map_table( st,
1622 (VipsSListMap2Fn) print_overlap_errors, st->fac, &total );
1623 printf( "RMS error = %g\n", sqrt( total / st->novl ) );
1624 #endif /*DEBUG*/
1625
1626 return( 0 );
1627 }
1628
1629 /* TODO(kleisauke): Copied from im__affinei */
1630 /* Shared with vips_mosaic1(), so not static. */
1631 int
vips__affinei(VipsImage * in,VipsImage * out,VipsTransformation * trn)1632 vips__affinei( VipsImage *in, VipsImage *out, VipsTransformation *trn )
1633 {
1634 VipsImage **t = (VipsImage **)
1635 vips_object_local_array( VIPS_OBJECT( out ), 2 );
1636 VipsArea *oarea;
1637 gboolean repack;
1638
1639 oarea = VIPS_AREA( vips_array_int_newv( 4,
1640 trn->oarea.left, trn->oarea.top,
1641 trn->oarea.width, trn->oarea.height ) );
1642
1643 /* vips7 affine would repack labq and im_benchmark() depends upon
1644 * this.
1645 */
1646 repack = in->Coding == VIPS_CODING_LABQ;
1647
1648 if( vips_affine( in, &t[0],
1649 trn->a, trn->b, trn->c, trn->d,
1650 "oarea", oarea,
1651 "odx", trn->odx,
1652 "ody", trn->ody,
1653 NULL ) ) {
1654 vips_area_unref( oarea );
1655 return( -1 );
1656 }
1657 vips_area_unref( oarea );
1658 in = t[0];
1659
1660 if( repack ) {
1661 if (vips_colourspace( in, &t[1],
1662 VIPS_INTERPRETATION_LABQ, NULL ) )
1663 return ( -1 );
1664 in = t[1];
1665 }
1666
1667 if( vips_image_write( in, out ) )
1668 return( -1 );
1669
1670 return( 0 );
1671 }
1672
1673 /* Look for all leaves, make sure we have a transformed version of each.
1674 */
1675 static void *
generate_trn_leaves(JoinNode * node,SymbolTable * st,void * b)1676 generate_trn_leaves( JoinNode *node, SymbolTable *st, void *b )
1677 {
1678 if( node->type == JOIN_LEAF ) {
1679 /* Check for image.
1680 */
1681 if( !node->im ) {
1682 vips_error( "vips_global_balance",
1683 _( "unable to open \"%s\"" ), node->name );
1684 return( node );
1685 }
1686 if( node->trnim )
1687 vips_error_exit( "global_balance: sanity failure #765" );
1688
1689 /* Special case: if this is an untransformed leaf (there will
1690 * always be at least one), then skip the affine.
1691 */
1692 if( vips__transform_isidentity( &node->cumtrn ) )
1693 node->trnim = node->im;
1694 else {
1695 node->trnim = vips_image_new();
1696 vips_object_local( node->st->im, node->trnim );
1697
1698 if ( vips__affinei( node->im, node->trnim, &node->cumtrn ) )
1699 return( node );
1700 }
1701 }
1702
1703 return( NULL );
1704 }
1705
1706 /* Analyse mosaic.
1707 */
1708 static int
analyse_mosaic(SymbolTable * st,VipsImage * in)1709 analyse_mosaic( SymbolTable *st, VipsImage *in )
1710 {
1711 /* Parse Hist on in.
1712 */
1713 if( vips__parse_desc( st, in ) )
1714 return( -1 );
1715
1716 /* Print parsed data.
1717 */
1718 #ifdef DEBUG
1719 printf( "Input files:\n" );
1720 vips__map_table( st, (VipsSListMap2Fn) print_leaf, NULL, NULL );
1721 printf( "\nOutput file:\n" );
1722 print_node( st->root );
1723 printf( "\nJoin commands:\n" );
1724 print_joins( st->root, 0 );
1725 #endif /*DEBUG*/
1726
1727 /* Generate transformed leaves.
1728 */
1729 if( vips__map_table( st,
1730 (VipsSListMap2Fn) generate_trn_leaves, st, NULL ) )
1731 return( -1 );
1732
1733 /* Find overlaps.
1734 */
1735 if( vips__map_table( st, (VipsSListMap2Fn) find_overlaps, st, NULL ) )
1736 return( -1 );
1737
1738 /* Scan table, counting and indexing input images and joins.
1739 */
1740 vips__map_table( st, (VipsSListMap2Fn) count_leaves, NULL, NULL );
1741 vips__map_table( st, (VipsSListMap2Fn) count_joins, NULL, NULL );
1742
1743 /* Select leaf to be 1.000.
1744 * This must be index == 0, unless you change stuff above!
1745 */
1746 st->leaf = vips__map_table( st,
1747 (VipsSListMap2Fn) choose_leaf, NULL, NULL );
1748
1749 /* And print overlaps.
1750 */
1751 #ifdef DEBUG
1752 printf( "\nLeaf to be 1.000:\n" );
1753 print_node( st->leaf );
1754 printf( "\nOverlaps:\n" );
1755 vips__map_table( st, (VipsSListMap2Fn) print_overlaps, NULL, NULL );
1756 printf( "\n%d input files, %d unique overlaps, %d joins\n",
1757 st->nim, st->novl, st->njoin );
1758 #endif /*DEBUG*/
1759
1760 return( 0 );
1761 }
1762
1763 /* Scale im by fac --- if it's uchar/ushort, use a lut. If we can use a lut,
1764 * transform in linear space. If we can't, don't bother for efficiency.
1765 */
1766 static VipsImage *
transform(JoinNode * node,double * gamma)1767 transform( JoinNode *node, double *gamma )
1768 {
1769 SymbolTable *st = node->st;
1770 VipsImage *in = node->im;
1771 double fac = st->fac[node->index];
1772 VipsImage **t = (VipsImage **)
1773 vips_object_local_array( VIPS_OBJECT( st->im ), 8 );
1774
1775 VipsImage *out;
1776
1777 if( fac == 1.0 ) {
1778 /* Easy!
1779 */
1780 out = in;
1781 }
1782 /* TODO(kleisauke): Could we call vips_gamma instead?
1783 */
1784 else if( in->BandFmt == VIPS_FORMAT_UCHAR ||
1785 in->BandFmt == VIPS_FORMAT_USHORT ) {
1786 if( vips_identity( &t[0],
1787 "bands", 1,
1788 "ushort", in->BandFmt == VIPS_FORMAT_USHORT,
1789 //"size", 65535,
1790 NULL ) ||
1791 vips_pow_const1( t[0], &t[1],
1792 1.0 / (*gamma), NULL ) ||
1793 vips_linear1( t[1], &t[2], fac, 0.0, NULL ) ||
1794 vips_pow_const1( t[2], &t[3], *gamma, NULL ) ||
1795 vips_cast( t[3], &t[4], in->BandFmt, NULL ) ||
1796 vips_maplut( in, &t[5], t[4], NULL ) )
1797 return( NULL );
1798 out = t[5];
1799 }
1800 else {
1801 /* Just vips_linear1 it.
1802 */
1803 if( vips_linear1( in, &t[6], fac, 0.0, NULL ) ||
1804 vips_cast( t[6], &t[7], in->BandFmt, NULL ) )
1805 return( NULL );
1806 out = t[7];
1807 }
1808
1809 vips_image_set_string( out, "mosaic-name", node->name );
1810
1811 return( out );
1812 }
1813
1814 /* As above, but output as float, not matched to input.
1815 */
1816 static VipsImage *
transformf(JoinNode * node,double * gamma)1817 transformf( JoinNode *node, double *gamma )
1818 {
1819 SymbolTable *st = node->st;
1820 VipsImage *in = node->im;
1821 double fac = node->st->fac[node->index];
1822 VipsImage **t = (VipsImage **)
1823 vips_object_local_array( VIPS_OBJECT( st->im ), 6 );
1824
1825 VipsImage *out;
1826
1827 if( fac == 1.0 ) {
1828 /* Easy!
1829 */
1830 out = in;
1831 }
1832 else if( in->BandFmt == VIPS_FORMAT_UCHAR ||
1833 in->BandFmt == VIPS_FORMAT_USHORT ) {
1834 if( vips_identity( &t[0],
1835 "bands", 1,
1836 "ushort", in->BandFmt == VIPS_FORMAT_USHORT,
1837 //"size", 65535,
1838 NULL ) ||
1839 vips_pow_const1( t[0], &t[1],
1840 1.0 / (*gamma), NULL ) ||
1841 vips_linear1( t[1], &t[2], fac, 0.0, NULL ) ||
1842 vips_pow_const1( t[2], &t[3], *gamma, NULL ) ||
1843 vips_maplut( in, &t[4], t[3], NULL ) )
1844 return( NULL );
1845 out = t[4];
1846 }
1847 else {
1848 /* Just vips_linear1 it.
1849 */
1850 if( vips_linear1( in, &t[5], fac, 0.0, NULL ) )
1851 return( NULL );
1852 out = t[5];
1853 }
1854
1855 vips_image_set_string( out, "mosaic-name", node->name );
1856
1857 return( out );
1858 }
1859
1860 typedef struct {
1861 VipsOperation parent_instance;
1862
1863 VipsImage *in;
1864 VipsImage *out;
1865
1866 gboolean int_output;
1867 double gamma;
1868
1869 } VipsGlobalbalance;
1870
1871 typedef VipsOperationClass VipsGlobalbalanceClass;
1872
1873 G_DEFINE_TYPE( VipsGlobalbalance, vips_globalbalance, VIPS_TYPE_OPERATION );
1874
1875 static int
vips_globalbalance_build(VipsObject * object)1876 vips_globalbalance_build( VipsObject *object )
1877 {
1878 VipsGlobalbalance *globalbalance = (VipsGlobalbalance *) object;
1879
1880 SymbolTable *st;
1881 transform_fn trn;
1882
1883 g_object_set( globalbalance, "out", vips_image_new(), NULL );
1884
1885 if( VIPS_OBJECT_CLASS( vips_globalbalance_parent_class )->
1886 build( object ) )
1887 return( -1 );
1888
1889 if( !(st = vips__build_symtab( globalbalance->out, SYM_TAB_SIZE )) ||
1890 analyse_mosaic( st, globalbalance->in ) ||
1891 find_factors( st, globalbalance->gamma ) )
1892 return( -1 );
1893
1894 trn = globalbalance->int_output ?
1895 (transform_fn) transform : (transform_fn) transformf;
1896 if( vips__build_mosaic( st, globalbalance->out,
1897 trn, &globalbalance->gamma ) )
1898 return( -1 );
1899
1900 return( 0 );
1901 }
1902
1903 static void
vips_globalbalance_class_init(VipsGlobalbalanceClass * class)1904 vips_globalbalance_class_init( VipsGlobalbalanceClass *class )
1905 {
1906 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
1907 VipsObjectClass *object_class = (VipsObjectClass *) class;
1908
1909 gobject_class->set_property = vips_object_set_property;
1910 gobject_class->get_property = vips_object_get_property;
1911
1912 object_class->nickname = "globalbalance";
1913 object_class->description = _( "global balance an image mosaic" );
1914 object_class->build = vips_globalbalance_build;
1915
1916 VIPS_ARG_IMAGE( class, "in", 1,
1917 _( "Input" ),
1918 _( "Input image" ),
1919 VIPS_ARGUMENT_REQUIRED_INPUT,
1920 G_STRUCT_OFFSET( VipsGlobalbalance, in ) );
1921
1922 VIPS_ARG_IMAGE( class, "out", 2,
1923 _( "Output" ),
1924 _( "Output image" ),
1925 VIPS_ARGUMENT_REQUIRED_OUTPUT,
1926 G_STRUCT_OFFSET( VipsGlobalbalance, out ) );
1927
1928 VIPS_ARG_DOUBLE( class, "gamma", 5,
1929 _( "gamma" ),
1930 _( "Image gamma" ),
1931 VIPS_ARGUMENT_OPTIONAL_INPUT,
1932 G_STRUCT_OFFSET( VipsGlobalbalance, gamma ),
1933 0.00001, 10, 1.6 );
1934
1935 VIPS_ARG_BOOL( class, "int_output", 7,
1936 _( "Int output" ),
1937 _( "Integer output" ),
1938 VIPS_ARGUMENT_OPTIONAL_INPUT,
1939 G_STRUCT_OFFSET( VipsGlobalbalance, int_output ),
1940 FALSE );
1941
1942 }
1943
1944 static void
vips_globalbalance_init(VipsGlobalbalance * globalbalance)1945 vips_globalbalance_init( VipsGlobalbalance *globalbalance )
1946 {
1947 globalbalance->gamma = 1.6;
1948 }
1949
1950 /**
1951 * vips_globalbalance: (method)
1952 * @in: mosaic to rebuild
1953 * @out: (out): output image
1954 * @...: %NULL-terminated list of optional named arguments
1955 *
1956 * Optional arguments:
1957 *
1958 * * @gamma: gamma of source images
1959 * * @int_output: %TRUE for integer image output
1960 *
1961 * vips_globalbalance() can be used to remove contrast differences in
1962 * an assembled mosaic.
1963 *
1964 * It reads the History field attached to @in and builds a list of the source
1965 * images that were used to make the mosaic and the position that each ended
1966 * up at in the final image.
1967 *
1968 * It opens each of the source images in turn and extracts all parts which
1969 * overlap with any of the other images. It finds the average values in the
1970 * overlap areas and uses least-mean-square to find a set of correction
1971 * factors which will minimise overlap differences. It uses @gamma to
1972 * gamma-correct the source images before calculating the factors. A value of
1973 * 1.0 will stop this.
1974 *
1975 * Each of the source images is transformed with the appropriate correction
1976 * factor, then the mosaic is reassembled. @out is #VIPS_FORMAT_FLOAT, but
1977 * if @int_output is set, the output image is the same format as the input
1978 * images.
1979 *
1980 * There are some conditions that must be met before this operation can work:
1981 * the source images must all be present under the filenames recorded in the
1982 * history on @in, and the mosaic must have been built using only operations in
1983 * this package.
1984 *
1985 * See also: vips_remosaic().
1986 *
1987 * Returns: 0 on success, -1 on error
1988 */
1989 int
vips_globalbalance(VipsImage * in,VipsImage ** out,...)1990 vips_globalbalance( VipsImage *in, VipsImage **out, ... )
1991 {
1992 va_list ap;
1993 int result;
1994
1995 va_start( ap, out );
1996 result = vips_call_split( "globalbalance", ap, in, out );
1997 va_end( ap );
1998
1999 return( result );
2000 }
2001