1 /* @(#) Function which convolves and subsamples VASARI format picture
2 * @(#) with a mask stored in a file argument.
3 * @(#)
4 * @(#) int im_convsub( in, out, mask, xskip, yskip )
5 * @(#) IMAGE *in, *out;
6 * @(#) INTMASK *mask; details in vips.h
7 * @(#) int xskip, yskip; is the subsamping factor along both directions
8 * @(#)
9 * @(#) Returns either 0 (sucess) or -1 (fail)
10 * @(#)
11 * @(#) Picture can have any number of channels (max 64).
12 * @(#) It is assummed that the output picture is subsampled on
13 * @(#) both directions by a factor of xskip horizontally and yskip vertically.
14 *
15 * Copyright: 1990, N. Dessipris.
16 *
17 * Author: Nicos Dessipris
18 * Written on: 29/04/1991
19 * Modified on:
20 */
21
22 /*
23
24 This file is part of VIPS.
25
26 VIPS is free software; you can redistribute it and/or modify
27 it under the terms of the GNU Lesser General Public License as published by
28 the Free Software Foundation; either version 2 of the License, or
29 (at your option) any later version.
30
31 This program is distributed in the hope that it will be useful,
32 but WITHOUT ANY WARRANTY; without even the implied warranty of
33 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 GNU Lesser General Public License for more details.
35
36 You should have received a copy of the GNU Lesser General Public License
37 along with this program; if not, write to the Free Software
38 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
39 02110-1301 USA
40
41 */
42
43 /*
44
45 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
46
47 */
48
49 #ifdef HAVE_CONFIG_H
50 #include <config.h>
51 #endif /*HAVE_CONFIG_H*/
52 #include <vips/intl.h>
53
54 #include <stdio.h>
55 #include <stdlib.h>
56
57 #include <vips/vips.h>
58 #include <vips/vips7compat.h>
59 #include <vips/internal.h>
60
61 /* Create multiplication luts for all non zero elements of the original mask;
62 * which is kept in buffer of length buffersize
63 * cnt is needed for freeing luts
64 */
65 static int
im__create_int_luts(int * buffer,int buffersize,int ** orig_luts,int ** luts,int * cnt)66 im__create_int_luts( int *buffer, int buffersize,
67 int **orig_luts, int **luts, int *cnt )
68 {
69 int *pbuffer;
70 int *buf1, *buf2, *pbuf1, *pbuf2;
71 int i, j;
72 int min, max;
73 int mark; /* used to mark the buffer mark = max+1 */
74 int counter; /* counts the no of unique elms in mask; returned in cnt*/
75
76 buf1 = (int*)calloc( (unsigned)buffersize, sizeof(int) );
77 buf2 = (int*)calloc( (unsigned)buffersize, sizeof(int) );
78 if ( ( buf1 == NULL ) || ( buf2 == NULL ) )
79 {
80 im_error( "im_create_int_luts", "%s", _( "calloc failed (1)") );
81 return( -1 );
82 }
83
84 pbuffer = buffer;
85 pbuf1 = buf1;
86 /* find max and copy mask to buf1 */
87 max = *pbuffer;
88 for ( i=0; i < buffersize; i++ )
89 {
90 if ( *pbuffer > max )
91 max = *pbuffer;
92 *pbuf1++ = *pbuffer++;
93 }
94 mark = max + 1;
95 pbuf1 = buf1;
96 pbuf2 = buf2;
97 counter = 0;
98 /* find a min at a time; put it into buf2 and mark all values of
99 * buf1 equal to found min, to INT_MAX
100 */
101 for ( i=0; i < buffersize; i++ )
102 {
103 min = mark + 1; /* force min to be greater than mark */
104 pbuf1 = buf1;
105 /* find a min */
106 for ( j=0; j < buffersize; j++ )
107 {
108 if ( *pbuf1 < min )
109 min = *pbuf1;
110 pbuf1++;
111 }
112 if ( min == mark ) /* all min are found */
113 break;
114 *pbuf2++ = min;
115 counter++;
116 pbuf1 = buf1;
117 for ( j=0; j < buffersize; j++ ) /* mark values equal to min */
118 {
119 if ( *pbuf1 == min )
120 *pbuf1 = mark;
121 pbuf1++;
122 }
123 }
124 /* buf2 should keep now counter unique values of the mask, descending order
125 * Malloc counter luts and initialise them
126 */
127 pbuf2 = buf2;
128 for ( i=0; i<counter; i++)
129 {
130 orig_luts[i] = (int*)calloc((unsigned)256, sizeof(int));
131 if (orig_luts[i] == NULL)
132 {
133 im_error( "im_create_int_luts", "%s", _( "calloc failed (2)") );
134 return( -1 );
135 }
136 for ( j=0; j<256; j++ )
137 *(orig_luts[i] + j) = j * (*pbuf2);
138 pbuf2++;
139 }
140
141 pbuffer = buffer;
142 for ( i=0; i<buffersize; i++ )
143 {
144 j = 0;
145 while ( 1 )
146 {
147 if ( *(buf2 + j) == *pbuffer )
148 {
149 luts[i] = orig_luts[j];
150 break;
151 }
152 j++;
153 }
154 pbuffer++;
155 }
156 /* free buf1, buf2 */
157 free((char*)buf1); free( (char*)buf2);
158 *cnt = counter;
159 return(0);
160 }
161
162
im_convsub(in,out,m,xskip,yskip)163 int im_convsub( in, out, m, xskip, yskip )
164 IMAGE *in, *out;
165 INTMASK *m;
166 int xskip, yskip;
167 {
168
169
170 int x; /* horizontal direction */
171 int y; /* vertical direction */
172 int n_clipped = 0;
173 int p_clipped = 0;
174 int i, b;
175 PEL **pnts, **cpnt1s, **cpnt2s; /* to keep pointers to data */
176 PEL **pnt, **cpnt1, **cpnt2; /* to keep pointers to data */
177 PEL *input, *line, *cpline;
178 int *pm; /* pointer to mask coefficients */
179 int count; /* no of non zero elms of the original mask */
180 int *newm, *pnewm; /* pointer to non zero mask coefficients */
181 int os; /* size of an input line of data */
182 int ms; /* is m->xsize * m->ysize */
183 int **lut_orig, **lut;
184 int lutcnt = 0;
185 int rounding, sum;
186 int tempsize;
187
188 /* Check input, output and vars */
189 if ((xskip < 1)||(yskip < 1))
190 {
191 im_error( "im_convsub", "%s", _( "xskip and yskip must be >= 1") );
192 return(-1);
193 }
194 if (im_iocheck(in, out) == -1)
195 return( -1 );
196
197 if ( (in->Coding != IM_CODING_NONE)||
198 (in->BandFmt != IM_BANDFMT_UCHAR) )
199 {
200 im_error( "im_convsub", "%s", _( "nput should be unsigned char uncoded") );
201 return(-1);
202 }
203
204 /* Prepare output */
205 if (im_cp_desc(out, in) == -1)
206 return( -1 );
207 tempsize = in->Xsize/xskip;
208 while ( 1 )
209 {
210 if ( tempsize * xskip + m->xsize < in->Xsize )
211 break;
212 else
213 tempsize--;
214 if ( tempsize < 0 )
215 break;
216 }
217 out->Xsize = tempsize;
218 tempsize = in->Ysize/yskip;
219 while ( 1 )
220 {
221 if ( tempsize * yskip + m->ysize < in->Ysize )
222 break;
223 else
224 tempsize--;
225 if ( tempsize < 0 )
226 break;
227 }
228 out->Ysize = tempsize;
229 if ( ( out->Xsize < 2 )||( out->Ysize < 2 ) ) {
230 im_error( "im_convsub", "%s", _( "too small output sizes") );
231 return(-1);
232 }
233
234 if( im_setupout(out) == -1)
235 return(-1);
236
237 /* Malloc one line of output data */
238 os = out->Xsize * out->Bands;
239 if ( (line=(PEL*)calloc( (unsigned)os, sizeof(char))) == NULL) {
240 im_error( "im_convsub", "%s", _( "unable to calloc(1)") );
241 return(-1);
242 }
243
244 /* Malloc pointers and put them at correct location */
245 ms = m->xsize * m->ysize;
246 count = 0; /* exclude the non-zero elms */
247 pm = m->coeff;
248 for ( i=0; i<ms; i++)
249 {
250 if ( *pm++ != 0 )
251 count++;
252 }
253
254 if (((newm = (int*)calloc((unsigned)count, sizeof(int))) == NULL )||
255 ((pnts = (PEL**)calloc((unsigned)count, sizeof(char *))) == NULL)||
256 ((cpnt1s=(PEL**)calloc((unsigned)count, sizeof(char *))) == NULL)||
257 ((cpnt2s=(PEL**)calloc((unsigned)count, sizeof(char *))) ==NULL ) )
258 {
259 im_error( "im_convsub", "%s", _( "unable to calloc(2)") );
260 return(-1);
261 }
262
263 pnt = pnts;
264 cpnt1 = cpnt1s;
265 cpnt2 = cpnt2s;
266
267 /* copy the non-zero elms of the original mask and set pointers */
268 i=0;
269 input = (PEL*)in->data;
270 pm = m->coeff;
271 pnewm = newm;
272 for (y=0; y<m->ysize; y++)
273 {
274 for (x=0; x<m->xsize; x++)
275 {
276 if ( *pm != 0 )
277 {
278 *pnewm++ = *pm;
279 pnt[i] = (input +(x + y*in->Xsize) * in->Bands);
280 i++;
281 }
282 pm++;
283 }
284 }
285
286 if ( i != count ) {
287 im_error( "im_convsub", "%s", _( "impossible state") );
288 return(-1); }
289
290 /* Malloc pointers; not all lut_orig are used necessarily */
291 lut_orig = (int**)calloc((unsigned)count, sizeof(int**) );
292 lut = (int**)calloc((unsigned)count, sizeof(int**) );
293 if ( (lut == NULL) || (lut_orig == NULL) ) {
294 im_error( "im_conv", "%s", _( "unable to calloc(1)") );
295 return(-1); }
296
297 /* Create luts; count is needed for freeing pointers. Not all lut_orig are used
298 * if zero elms are detected.
299 */
300 if ( im__create_int_luts(newm, count, lut_orig, lut, &lutcnt ) == -1 )
301 {
302 im_error( "im_convsub", "%s", _( "im_create_int_luts failed") );
303 return(-1);
304 }
305
306 rounding = m->scale/2;
307
308 /* Output out->Ysize processed lines */
309 for(y=0; y < out->Ysize; y++)
310 {
311 cpline = line;
312 for (i=0; i<count; i++)
313 {
314 cpnt1[i] = pnt[i];
315 /* skip yskip input lines */
316 pnt[i] += ( in->Xsize * in->Bands * yskip );
317 }
318
319 /* process out->Xsize points */
320 for( x = 0; x < out->Xsize; x++ )
321 {
322 for (i=0; i<count; i++)
323 { /* skip xskip elms */
324 cpnt2[i] = cpnt1[i];
325 cpnt1[i] += xskip * in->Bands;
326 }
327 for ( b=0; b<out->Bands; b++ )
328 {
329 sum = 0;
330 for (i=0; i<count; i++)
331 { /* core of convolution */
332 sum += *( lut[i] + (*cpnt2[i]) );
333 cpnt2[i]++;
334 }
335 sum = ( (sum+rounding)/m->scale ) + m->offset;
336
337 if ( sum < (int)0 )
338 { n_clipped++; sum = (int)0; }
339 else if ( sum > (int)255)
340 { p_clipped++; sum = (int)255; }
341 *cpline++ = (unsigned char)sum;
342 }
343 }
344
345 /* Output the calculated line */
346 if ( im_writeline(y, out, (PEL*)line) == -1 )
347 {
348 free((char*)line); free((char*)newm);
349 free((char*)pnts);
350 free((char*)cpnt1s); free((char*)cpnt2s);
351 for ( i=0; i<lutcnt; i++)
352 free( (char*)lut_orig[i] );
353 free( (char*)lut_orig ); free( (char*)lut );
354 return(-1);
355 }
356 } /* end of the for (..y..) loop */
357
358 if (n_clipped || p_clipped)
359 fprintf(stderr,
360 "im_convsub: %d pels over 255 and %d under 0 clipped\n",
361 p_clipped, n_clipped);
362
363 free((char*)line); free((char*)newm);
364 free((char*)pnts);
365 free((char*)cpnt1s); free((char*)cpnt2s);
366 for ( i=0; i<lutcnt; i++)
367 free( (char*)lut_orig[i] );
368 free( (char*)lut_orig ); free( (char*)lut );
369
370 return(0);
371 }
372