13e354e869
vips_shutdown() flushes caches, closes plugins and does a leak check
567 lines
12 KiB
C
567 lines
12 KiB
C
/* Join together images
|
|
*
|
|
* mergeup x y file_name output_dir <root>.0x0.v <root>.0x1.v ...
|
|
*
|
|
* Where the image has been take with patches named as
|
|
*
|
|
* . .
|
|
* . .
|
|
* <root>.0x1.v <root>.1x1.v ..
|
|
* <root>.0x0.v <root>.1x0.v ..
|
|
*
|
|
*
|
|
* Tries to generate optimal join sequence. Does not require any intermidiate
|
|
* files for temporary storage.
|
|
* It uses partials on all IO by including tbmerge / lrmerge programs.
|
|
*
|
|
*
|
|
* Copyright (C) Feb./1995, Ahmed. Abbood
|
|
* National Gallery. London
|
|
*
|
|
*/
|
|
|
|
/*
|
|
|
|
This file is part of VIPS.
|
|
|
|
VIPS is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif /*HAVE_CONFIG_H*/
|
|
#include <vips/intl.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <fcntl.h>
|
|
#include <locale.h>
|
|
|
|
#include <vips/vips.h>
|
|
|
|
#define NUM_FILES 1000
|
|
#define MAXPOINTS 60
|
|
|
|
static int xoverlap;
|
|
static int yoverlap;
|
|
|
|
static int file_ptr = 0;
|
|
static IMAGE *in[ NUM_FILES ];
|
|
|
|
/* Strategy: build a tree describing the sequence of joins we want. Walk the
|
|
* tree assigning temporary file names, compile the tree into a linear
|
|
* sequence of join commands.
|
|
*/
|
|
|
|
|
|
|
|
/* Decoded file name info.
|
|
*/
|
|
static char *file_root = NULL;
|
|
static char *output_file = NULL;
|
|
static int width = 0; /* Number of frames across */
|
|
static int height = 0; /* Number of frames down */
|
|
|
|
static int file_list[ NUM_FILES ];
|
|
|
|
|
|
static int
|
|
im_phmerge( Rect *larea, Rect *rarea, Rect *outarea )
|
|
{
|
|
|
|
Rect overlap;
|
|
|
|
|
|
/* Compute overlap.
|
|
*/
|
|
im_rect_intersectrect( larea, rarea, &overlap );
|
|
|
|
outarea->width = rarea->left + rarea->width;
|
|
outarea->height = overlap.height;
|
|
outarea->top = overlap.top;
|
|
outarea->left = larea->left;
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
|
|
static int
|
|
im_pvmerge( Rect *tarea, Rect *barea, Rect *outarea )
|
|
{
|
|
|
|
Rect overlap;
|
|
|
|
|
|
/* Compute overlap.
|
|
*/
|
|
im_rect_intersectrect( tarea, barea, &overlap );
|
|
|
|
outarea->width = overlap.width;
|
|
outarea->height = barea->top + barea->height ;
|
|
outarea->left = overlap.left;
|
|
outarea->top = tarea->top;
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
merge_analysis(int width,int height,IMAGE **in,int xoff,
|
|
int yoff,int *vxdisp,int *vydisp,int *hxdisp,
|
|
int *hydisp,Rect *hrect,Rect *vrect)
|
|
{
|
|
int i,j;
|
|
int curr_im,offset;
|
|
int curr_x, curr_y;
|
|
Rect larea, rarea, barea;
|
|
|
|
|
|
|
|
curr_im = -1;
|
|
curr_x = -1;
|
|
curr_y = -1;
|
|
for(i=0; i<=height; i++){
|
|
for(j=0; j<=width; j++){
|
|
++curr_im;
|
|
if( width == 0 ){
|
|
++curr_x;
|
|
hrect[curr_x].width = in[curr_im]->Xsize;
|
|
hrect[curr_x].height= in[curr_im]->Ysize;
|
|
hrect[curr_x].top = 0;
|
|
hrect[curr_x].left = 0;
|
|
}
|
|
else{
|
|
if( j == 0){
|
|
++curr_x;
|
|
|
|
/* Area occupied by left image.
|
|
*/
|
|
larea.left = 0;
|
|
larea.top = 0;
|
|
larea.height = in[curr_im]->Ysize;
|
|
larea.width = in[curr_im]->Xsize;
|
|
/* Area occupied by right image.
|
|
*/
|
|
if( in[curr_im]->Xsize < xoff )
|
|
offset = 0;
|
|
else
|
|
offset =xoff;
|
|
rarea.left = in[curr_im]->Xsize - (offset + hxdisp[curr_x]) ;
|
|
rarea.top = hydisp[curr_x];
|
|
rarea.width = in[curr_im+1]->Xsize;
|
|
rarea.height = in[curr_im+1]->Ysize;
|
|
im_phmerge( &larea, &rarea, &hrect[curr_x] );
|
|
}
|
|
else if( j < width ){
|
|
++curr_x;
|
|
|
|
/* Area occupied by right image.
|
|
*/
|
|
if( in[curr_im+1]->Xsize < xoff )
|
|
offset = 0;
|
|
else
|
|
offset =xoff;
|
|
|
|
rarea.left = hrect[curr_x -1].width - (offset + hxdisp[curr_x]) ;
|
|
rarea.top = hydisp[curr_x];
|
|
rarea.width = in[curr_im+1]->Xsize;
|
|
rarea.height = in[curr_im+1]->Ysize;
|
|
im_phmerge( &hrect[curr_x -1], &rarea, &hrect[curr_x] );
|
|
}
|
|
}
|
|
}
|
|
if( i > 0 ){
|
|
++curr_y;
|
|
|
|
/* Area occupied by bottom image in output.
|
|
*/
|
|
barea.left = vxdisp[curr_y];
|
|
barea.width = hrect[curr_x].width;
|
|
barea.height = hrect[curr_x].height;
|
|
if( in[curr_x - width]->Ysize < yoff )
|
|
offset = 0;
|
|
else
|
|
offset = yoff;
|
|
if( i == 1){
|
|
barea.top = hrect[curr_x - width].height - offset - vydisp[curr_y] ;
|
|
im_pvmerge( &hrect[curr_x - width], &barea, &vrect[curr_y] );
|
|
}
|
|
else{
|
|
barea.top = vrect[curr_y - 1].height - yoff - vydisp[curr_y] ;
|
|
im_pvmerge( &vrect[curr_y -1], &barea, &vrect[curr_y] );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Find the root name of a file name. Return new, shorter, string.
|
|
*/
|
|
static char *
|
|
find_root( name )
|
|
char *name;
|
|
{ char *out = strdup( name );
|
|
char *p;
|
|
|
|
/* Chop off '.v'.
|
|
*/
|
|
if( !(p = strrchr( out, '.' )) ) {
|
|
im_error( "find_mosaic",
|
|
_( "bad file name format '%s'" ), name );
|
|
free( out );
|
|
return( NULL );
|
|
}
|
|
*p = '\0';
|
|
|
|
/* Chop off nxn.
|
|
*/
|
|
if( !(p = strrchr( out, '.' )) ) {
|
|
im_error( "find_mosaic",
|
|
_( "bad file name format '%s'" ), name );
|
|
free( out );
|
|
return( NULL );
|
|
}
|
|
*p = '\0';
|
|
|
|
return( out );
|
|
}
|
|
|
|
/* Find the x position of a file name (extract n from <root>.nxm.v).
|
|
*/
|
|
static int
|
|
find_x( name )
|
|
char *name;
|
|
{ int n;
|
|
char *p;
|
|
char *out = strdup( name );
|
|
|
|
/* Chop off '.v'.
|
|
*/
|
|
if( !(p = strrchr( out, '.' )) ) {
|
|
im_error( "find_mosaic",
|
|
_( "bad file name format '%s'" ), name );
|
|
free( out );
|
|
return( -1 );
|
|
}
|
|
*p = '\0';
|
|
|
|
/* Find '.nxm'.
|
|
*/
|
|
if( !(p = strrchr( out, '.' )) ) {
|
|
im_error( "find_mosaic",
|
|
_( "bad file name format '%s'" ), name );
|
|
free( out );
|
|
return( -1 );
|
|
}
|
|
|
|
/* Read out x posn.
|
|
*/
|
|
if( sscanf( p, ".%dx%*d", &n ) != 1 ) {
|
|
im_error( "find_mosaic",
|
|
_( "bad file name format '%s'" ), name );
|
|
free( out );
|
|
return( -1 );
|
|
}
|
|
|
|
return( n );
|
|
}
|
|
|
|
/* Find the y position of a file name (extract m from <root>.nxm.v).
|
|
*/
|
|
static int
|
|
find_y( name )
|
|
char *name;
|
|
{ int m;
|
|
char *p;
|
|
char *out = strdup( name );
|
|
|
|
/* Chop off '.v'.
|
|
*/
|
|
if( !(p = strrchr( out, '.' )) ) {
|
|
im_error( "find_mosaic",
|
|
_( "bad file name format '%s'" ), name );
|
|
free( out );
|
|
return( -1 );
|
|
}
|
|
*p = '\0';
|
|
|
|
/* Find '.nxm'.
|
|
*/
|
|
if( !(p = strrchr( out, '.' )) ) {
|
|
im_error( "find_mosaic",
|
|
_( "bad file name format '%s'" ), name );
|
|
free( out );
|
|
return( -1 );
|
|
}
|
|
|
|
/* Read out y posn.
|
|
*/
|
|
if( sscanf( p, ".%*dx%d", &m ) != 1 ) {
|
|
im_error( "find_mosaic",
|
|
_( "bad file name format '%s'" ), name );
|
|
free( out );
|
|
return( -1 );
|
|
}
|
|
|
|
free( out );
|
|
return( m );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Join two frames left-right. Have to open them and find their sizes.
|
|
*/
|
|
static int
|
|
join_leftright(IMAGE *left, IMAGE *right, IMAGE *out, int dx, int dy )
|
|
{
|
|
|
|
if (im_lrmerge(left, right, out, dx, dy, 20) == -1)
|
|
return( -1 );
|
|
return( 0 );
|
|
}
|
|
|
|
|
|
/* Join two frames up-down. Have to open them and find their sizes.
|
|
*/
|
|
static int
|
|
join_updown( IMAGE *top, IMAGE *bottom, IMAGE *out, int dx, int dy )
|
|
{
|
|
if (im_tbmerge(top, bottom, out, dx, dy, 20) == -1)
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
|
|
static int
|
|
merge_up( int width, int height, IMAGE **inp, IMAGE *outp, int xoff, int yoff,
|
|
int *hxdisp, int *hydisp, Rect *vrect )
|
|
{
|
|
int dx,dy,first_row;
|
|
int i, j, partial_no, in_no;
|
|
IMAGE **p_img;
|
|
char name[29];
|
|
int v_no, h_no;
|
|
|
|
|
|
|
|
p_img = (IMAGE **) malloc(1 + 3 * width * height * sizeof(IMAGE *));
|
|
if( p_img == NULL ){
|
|
im_error( "mergeup", "%s", _( "allocation failure in mergeup") );
|
|
return( -1 );
|
|
}
|
|
partial_no = 0;
|
|
v_no = 0;
|
|
h_no = 0;
|
|
in_no = 0;
|
|
first_row = 0;
|
|
|
|
if( (width == 0 ) && (height == 0 ) ){
|
|
im_error( "mergeup", "%s", _( "Need more than one image") );
|
|
return( -1 );
|
|
}
|
|
|
|
|
|
for(i=0; i<=height; i++){
|
|
for(j=0; j<=width; j++){
|
|
p_img[partial_no] = inp[in_no];
|
|
++partial_no;
|
|
if( j != 0 ){
|
|
im_snprintf( name, 29, "partial_img.%d.v",partial_no );
|
|
if( !( p_img[partial_no] = im_open( name, "p" )) ){
|
|
free(p_img);
|
|
return( -1 );
|
|
}
|
|
++partial_no;
|
|
dy = hydisp[h_no ] ;
|
|
dx = -p_img[partial_no-3]->Xsize + hxdisp[h_no] + xoff ;
|
|
|
|
if( (height == 0) && ( j == width) )
|
|
join_leftright( p_img[partial_no-3],
|
|
p_img[partial_no-2],outp,dx,dy );
|
|
else
|
|
join_leftright( p_img[partial_no-3],
|
|
p_img[partial_no-2],p_img[partial_no-1],dx,dy );
|
|
++h_no;
|
|
}
|
|
++in_no;
|
|
}
|
|
|
|
if( first_row == 0)
|
|
first_row = partial_no - 1;
|
|
|
|
if( ( i > 0 ) || ( height == 0) ){
|
|
if( i < height ){
|
|
im_snprintf( name, 29, "partial_img.%d.v", partial_no );
|
|
if( !( p_img[partial_no] = im_open( name, "p" )) ){
|
|
free(p_img);
|
|
return( -1 );
|
|
}
|
|
++partial_no;
|
|
|
|
dy = -( vrect[v_no].height - p_img[partial_no-2]->Ysize );
|
|
dx = vrect[v_no].left ;
|
|
|
|
++v_no;
|
|
join_updown( p_img[first_row],
|
|
p_img[partial_no-2], p_img[partial_no-1],dx,dy );
|
|
first_row = partial_no-1;
|
|
}
|
|
else{
|
|
dy = -( vrect[v_no].height - p_img[partial_no-1]->Ysize );
|
|
dx = vrect[v_no].left ;
|
|
|
|
join_updown( p_img[first_row], p_img[partial_no-1],outp,dx,dy );
|
|
}
|
|
}
|
|
}
|
|
return( 0 );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
main( argc, argv )
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
int i, n, j, k;
|
|
char name[ 1000 ];
|
|
FILE *fp;
|
|
char *r;
|
|
IMAGE *out;
|
|
int vxdisp[NUM_FILES + 1] ;
|
|
int vydisp[NUM_FILES + 1] ;
|
|
int hxdisp[NUM_FILES + 1] ;
|
|
int hydisp[NUM_FILES + 1] ;
|
|
Rect hrect[NUM_FILES];
|
|
Rect vrect[NUM_FILES];
|
|
|
|
if( im_init_world( argv[0] ) )
|
|
error_exit( "unable to start VIPS" );
|
|
textdomain( GETTEXT_PACKAGE );
|
|
setlocale( LC_ALL, "" );
|
|
|
|
/* Too many?
|
|
*/
|
|
if( argc > NUM_FILES + 1 )
|
|
error_exit( "Too many files to merge" );
|
|
for(i=0; i< NUM_FILES; i++)
|
|
file_list[i] = 0;
|
|
/* Too few?
|
|
*/
|
|
if( argc == 1 )
|
|
error_exit( "usage: xoverlap yoverlap file_name output_dir "
|
|
"<root>.0x0.v <root>.0x1.v ..." );
|
|
xoverlap = atoi(argv[1]);
|
|
yoverlap = atoi(argv[2]);
|
|
fp = fopen( argv[3] , "r" );
|
|
|
|
for( i = 5; i < argc; i++ ){
|
|
/* Find/check root.
|
|
*/
|
|
if( !file_root ) {
|
|
file_root = find_root( argv[i] );
|
|
if( !file_root )
|
|
error_exit( "error at file_root" );
|
|
}
|
|
else {
|
|
if( !(r = find_root( argv[i] )) )
|
|
error_exit( "Error in reading parameters" );
|
|
if( strcmp( r, file_root ) != 0 )
|
|
error_exit( "Not all roots identical!" );
|
|
}
|
|
|
|
/* Read out position.
|
|
*/
|
|
if( (n = find_x( argv[i] )) < 0 )
|
|
error_exit( "Error in reading file name" );
|
|
if( n > width - 1 )
|
|
width = n;
|
|
if( (n = find_y( argv[i] )) < 0 )
|
|
error_exit( "Error in reading file name" );
|
|
if( n > height - 1 )
|
|
height = n;
|
|
|
|
file_list[n] +=1;
|
|
}
|
|
|
|
/* Make output name. and store them in an array.
|
|
*/
|
|
im_snprintf( name, 1000, "%s/paint.hr.v", argv[4] );
|
|
if( !(out = im_open( name, "w" )) )
|
|
error_exit("unable to open file for output");
|
|
|
|
file_ptr =0;
|
|
for(i=height; i>=0; i--)
|
|
for(j=0; j<file_list[i]; j++){
|
|
im_snprintf( name, 1000, "%s.%dx%d.v", file_root,j,i );
|
|
output_file = strdup( name );
|
|
if( !(in[file_ptr] = im_open( output_file, "r" )) )
|
|
error_exit("unable to open %s for input",output_file);
|
|
++file_ptr;
|
|
|
|
}
|
|
|
|
k = 0;
|
|
for( i=0; i<height; i++ ){
|
|
for( j=0; j<width; j++ ){
|
|
if(fscanf(fp,"%d %d ", &hxdisp[k] , &hydisp[k])!=2)
|
|
error_exit("argh");
|
|
k++;
|
|
}
|
|
if(fscanf(fp,"\n")!=0)
|
|
error_exit("argh3");
|
|
}
|
|
|
|
for( i=0; i<height; i++ )
|
|
if(fscanf(fp,"%d %d\n", &vxdisp[i] , &vydisp[i])!=2)
|
|
error_exit("argh2");
|
|
|
|
merge_analysis(width,height,in,xoverlap,yoverlap,vxdisp,vydisp,hxdisp,hydisp,hrect,vrect);
|
|
merge_up( width, height, in, out, xoverlap, yoverlap, hxdisp, hydisp, vrect );
|
|
|
|
for(i=0; i<file_ptr; i++)
|
|
if( im_close(in[i]) == -1)
|
|
error_exit("unable to close partial file");
|
|
if( im_close(out) == -1)
|
|
error_exit("unable to close\n ");
|
|
|
|
vips_shutdown();
|
|
|
|
return( 0 );
|
|
}
|