1 : /******************************************************************************
2 : * $Id: nearblack.cpp 18712 2010-02-03 22:02:41Z rouault $
3 : *
4 : * Project: GDAL Utilities
5 : * Purpose: Convert nearly black or nearly white border to exact black/white.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : * ****************************************************************************
9 : * Copyright (c) 2006, MapShots Inc (www.mapshots.com)
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "gdal.h"
31 : #include "cpl_conv.h"
32 : #include "cpl_string.h"
33 :
34 : CPL_CVSID("$Id: nearblack.cpp 18712 2010-02-03 22:02:41Z rouault $");
35 :
36 : static void ProcessLine( GByte *pabyLine, int iStart, int iEnd, int nSrcBands, int nDstBands,
37 : int nNearDist, int nMaxNonBlack, int bNearWhite,
38 : int *panLastLineCounts,
39 : int bDoHorizontalCheck,
40 : int bDoVerticalCheck );
41 :
42 : /************************************************************************/
43 : /* Usage() */
44 : /************************************************************************/
45 :
46 0 : void Usage()
47 : {
48 : printf( "nearblack [-of format] [-white] [-near dist] [-nb non_black_pixels]\n"
49 0 : " [-setalpha] [-o outfile] [-q] [-co \"NAME=VALUE\"]* infile\n" );
50 0 : exit( 1 );
51 : }
52 :
53 : /************************************************************************/
54 : /* main() */
55 : /************************************************************************/
56 :
57 5 : int main( int argc, char ** argv )
58 :
59 : {
60 : /* Check that we are running against at least GDAL 1.4 (probably older in fact !) */
61 : /* Note to developers : if we use newer API, please change the requirement */
62 5 : if (atoi(GDALVersionInfo("VERSION_NUM")) < 1400)
63 : {
64 : fprintf(stderr, "At least, GDAL >= 1.4.0 is required for this version of %s, "
65 0 : "which was compiled against GDAL %s\n", argv[0], GDAL_RELEASE_NAME);
66 0 : exit(1);
67 : }
68 :
69 : /* -------------------------------------------------------------------- */
70 : /* Generic arg processing. */
71 : /* -------------------------------------------------------------------- */
72 5 : GDALAllRegister();
73 5 : GDALSetCacheMax( 100000000 );
74 5 : argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 );
75 5 : if( argc < 1 )
76 0 : exit( -argc );
77 :
78 : /* -------------------------------------------------------------------- */
79 : /* Parse arguments. */
80 : /* -------------------------------------------------------------------- */
81 : int i;
82 5 : const char *pszOutFile = NULL;
83 5 : const char *pszInFile = NULL;
84 5 : int nMaxNonBlack = 2;
85 5 : int nNearDist = 15;
86 5 : int bNearWhite = FALSE;
87 5 : int bSetAlpha = FALSE;
88 5 : const char* pszDriverName = "HFA";
89 5 : char** papszCreationOptions = NULL;
90 5 : int bQuiet = FALSE;
91 :
92 26 : for( i = 1; i < argc; i++ )
93 : {
94 22 : if( EQUAL(argv[i], "--utility_version") )
95 : {
96 : printf("%s was compiled against GDAL %s and is running against GDAL %s\n",
97 1 : argv[0], GDAL_RELEASE_NAME, GDALVersionInfo("RELEASE_NAME"));
98 1 : return 0;
99 : }
100 24 : else if( EQUAL(argv[i], "-o") && i < argc-1 )
101 3 : pszOutFile = argv[++i];
102 22 : else if( EQUAL(argv[i], "-of") && i < argc-1 )
103 4 : pszDriverName = argv[++i];
104 14 : else if( EQUAL(argv[i], "-white") )
105 1 : bNearWhite = TRUE;
106 17 : else if( EQUAL(argv[i], "-nb") && i < argc-1 )
107 4 : nMaxNonBlack = atoi(argv[++i]);
108 9 : else if( EQUAL(argv[i], "-near") && i < argc-1 )
109 0 : nNearDist = atoi(argv[++i]);
110 9 : else if( EQUAL(argv[i], "-setalpha") )
111 3 : bSetAlpha = TRUE;
112 7 : else if( EQUAL(argv[i], "-q") || EQUAL(argv[i], "-quiet") )
113 1 : bQuiet = TRUE;
114 6 : else if( EQUAL(argv[i], "-co") && i < argc-1 )
115 1 : papszCreationOptions = CSLAddString(papszCreationOptions, argv[++i]);
116 4 : else if( argv[i][0] == '-' )
117 0 : Usage();
118 4 : else if( pszInFile == NULL )
119 4 : pszInFile = argv[i];
120 : else
121 0 : Usage();
122 : }
123 :
124 4 : if( pszInFile == NULL )
125 0 : Usage();
126 :
127 4 : if( pszOutFile == NULL )
128 1 : pszOutFile = pszInFile;
129 :
130 : /* -------------------------------------------------------------------- */
131 : /* Open input file. */
132 : /* -------------------------------------------------------------------- */
133 4 : GDALDatasetH hInDS, hOutDS = NULL;
134 : int nXSize, nYSize, nBands;
135 :
136 4 : if( pszOutFile == pszInFile )
137 1 : hInDS = hOutDS = GDALOpen( pszInFile, GA_Update );
138 : else
139 3 : hInDS = GDALOpen( pszInFile, GA_ReadOnly );
140 :
141 4 : if( hInDS == NULL )
142 0 : exit( 1 );
143 :
144 4 : nXSize = GDALGetRasterXSize( hInDS );
145 4 : nYSize = GDALGetRasterYSize( hInDS );
146 4 : nBands = GDALGetRasterCount( hInDS );
147 4 : int nDstBands = nBands;
148 :
149 4 : if( hOutDS != NULL && papszCreationOptions != NULL)
150 : {
151 : CPLError(CE_Warning, CPLE_AppDefined,
152 0 : "Warning: creation options are ignored when writing to an existing file.");
153 : }
154 :
155 : /* -------------------------------------------------------------------- */
156 : /* Do we need to create output file? */
157 : /* -------------------------------------------------------------------- */
158 4 : if( hOutDS == NULL )
159 : {
160 3 : GDALDriverH hDriver = GDALGetDriverByName( pszDriverName );
161 3 : if (hDriver == NULL)
162 0 : exit(1);
163 :
164 3 : if (bSetAlpha)
165 : {
166 2 : if (nBands == 3)
167 2 : nDstBands ++;
168 0 : else if (nBands == 4)
169 0 : nBands --;
170 : else
171 : {
172 : CPLError(CE_Failure, CPLE_AppDefined,
173 : "Number of bands of file = %d. Expected 3 or 4.",
174 0 : nBands);
175 0 : exit(1);
176 : }
177 : }
178 :
179 : hOutDS = GDALCreate( hDriver, pszOutFile,
180 : nXSize, nYSize, nDstBands, GDT_Byte,
181 3 : papszCreationOptions );
182 3 : if( hOutDS == NULL )
183 0 : exit( 1 );
184 :
185 : double adfGeoTransform[6];
186 :
187 3 : if( GDALGetGeoTransform( hInDS, adfGeoTransform ) == CE_None )
188 : {
189 3 : GDALSetGeoTransform( hOutDS, adfGeoTransform );
190 3 : GDALSetProjection( hOutDS, GDALGetProjectionRef( hInDS ) );
191 : }
192 : }
193 : else
194 : {
195 1 : if (bSetAlpha)
196 : {
197 1 : if (nBands != 4)
198 : {
199 : CPLError(CE_Failure, CPLE_AppDefined,
200 : "Number of bands of output file = %d. Expected 4.",
201 0 : nBands);
202 0 : exit(1);
203 : }
204 :
205 1 : nBands --;
206 : }
207 : }
208 :
209 4 : if (GDALGetRasterXSize(hOutDS) != nXSize ||
210 : GDALGetRasterYSize(hOutDS) != nYSize)
211 : {
212 : CPLError(CE_Failure, CPLE_AppDefined,
213 : "The dimensions of the output dataset don't match "
214 0 : "the dimensions of the input dataset.");
215 0 : exit(1);
216 : }
217 :
218 :
219 : int iBand;
220 16 : for( iBand = 0; iBand < nBands; iBand++ )
221 : {
222 12 : GDALRasterBandH hBand = GDALGetRasterBand(hInDS, iBand+1);
223 12 : if (GDALGetRasterDataType(hBand) != GDT_Byte)
224 : {
225 : CPLError(CE_Warning, CPLE_AppDefined,
226 0 : "Band %d is not of type GDT_Byte. It can lead to unexpected results.", iBand+1);
227 : }
228 12 : if (GDALGetRasterColorTable(hBand) != NULL)
229 : {
230 : CPLError(CE_Warning, CPLE_AppDefined,
231 : "Band %d has a color table, which is ignored by nearblack. "
232 0 : "It can lead to unexpected results.", iBand+1);
233 : }
234 : }
235 :
236 : /* -------------------------------------------------------------------- */
237 : /* Allocate a line buffer. */
238 : /* -------------------------------------------------------------------- */
239 : GByte *pabyLine;
240 : int *panLastLineCounts;
241 :
242 4 : pabyLine = (GByte *) CPLMalloc(nXSize * nDstBands);
243 :
244 4 : panLastLineCounts = (int *) CPLCalloc(sizeof(int),nXSize);
245 :
246 : /* -------------------------------------------------------------------- */
247 : /* Processing data one line at a time. */
248 : /* -------------------------------------------------------------------- */
249 : int iLine;
250 :
251 204 : for( iLine = 0; iLine < nYSize; iLine++ )
252 : {
253 : CPLErr eErr;
254 :
255 : eErr = GDALDatasetRasterIO( hInDS, GF_Read, 0, iLine, nXSize, 1,
256 : pabyLine, nXSize, 1, GDT_Byte,
257 200 : nBands, NULL, nDstBands, nXSize * nDstBands, 1 );
258 200 : if( eErr != CE_None )
259 0 : break;
260 :
261 200 : if (bSetAlpha)
262 : {
263 : int iCol;
264 7650 : for(iCol = 0; iCol < nXSize; iCol ++)
265 : {
266 7500 : pabyLine[iCol * nDstBands + nDstBands - 1] = 255;
267 : }
268 : }
269 :
270 : ProcessLine( pabyLine, 0, nXSize-1, nBands, nDstBands, nNearDist, nMaxNonBlack,
271 200 : bNearWhite, panLastLineCounts, TRUE, TRUE );
272 : ProcessLine( pabyLine, nXSize-1, 0, nBands, nDstBands, nNearDist, nMaxNonBlack,
273 200 : bNearWhite, NULL, TRUE, FALSE );
274 :
275 : eErr = GDALDatasetRasterIO( hOutDS, GF_Write, 0, iLine, nXSize, 1,
276 : pabyLine, nXSize, 1, GDT_Byte,
277 200 : nDstBands, NULL, nDstBands, nXSize * nDstBands, 1 );
278 200 : if( eErr != CE_None )
279 0 : break;
280 :
281 200 : if (!bQuiet)
282 150 : GDALTermProgress( 0.5 * ((iLine+1) / (double) nYSize), NULL, NULL );
283 : }
284 :
285 : /* -------------------------------------------------------------------- */
286 : /* Now process from the bottom back up, doing only the vertical pass.*/
287 : /* -------------------------------------------------------------------- */
288 4 : memset( panLastLineCounts, 0, sizeof(int) * nXSize);
289 :
290 204 : for( iLine = nYSize-1; iLine >= 0; iLine-- )
291 : {
292 : CPLErr eErr;
293 :
294 : eErr = GDALDatasetRasterIO( hOutDS, GF_Read, 0, iLine, nXSize, 1,
295 : pabyLine, nXSize, 1, GDT_Byte,
296 200 : nDstBands, NULL, nDstBands, nXSize * nDstBands, 1 );
297 200 : if( eErr != CE_None )
298 0 : break;
299 :
300 : ProcessLine( pabyLine, 0, nXSize-1, nBands, nDstBands, nNearDist, nMaxNonBlack,
301 200 : bNearWhite, panLastLineCounts, FALSE, TRUE );
302 :
303 : eErr = GDALDatasetRasterIO( hOutDS, GF_Write, 0, iLine, nXSize, 1,
304 : pabyLine, nXSize, 1, GDT_Byte,
305 200 : nDstBands, NULL, nDstBands, nXSize * nDstBands, 1 );
306 200 : if( eErr != CE_None )
307 0 : break;
308 :
309 200 : if (!bQuiet)
310 : GDALTermProgress( 0.5 + 0.5 * (nYSize-iLine) / (double) nYSize,
311 150 : NULL, NULL );
312 : }
313 :
314 4 : CPLFree(pabyLine);
315 4 : CPLFree( panLastLineCounts );
316 :
317 4 : GDALClose( hOutDS );
318 4 : if( hInDS != hOutDS )
319 3 : GDALClose( hInDS );
320 4 : GDALDumpOpenDatasets( stderr );
321 4 : CSLDestroy( argv );
322 4 : CSLDestroy( papszCreationOptions );
323 4 : GDALDestroyDriverManager();
324 :
325 4 : return 0;
326 : }
327 :
328 : /************************************************************************/
329 : /* ProcessLine() */
330 : /* */
331 : /* Process a single scanline of image data. */
332 : /************************************************************************/
333 :
334 : static void ProcessLine( GByte *pabyLine, int iStart, int iEnd, int nSrcBands,
335 : int nDstBands,
336 : int nNearDist, int nMaxNonBlack, int bNearWhite,
337 : int *panLastLineCounts,
338 : int bDoHorizontalCheck,
339 600 : int bDoVerticalCheck )
340 :
341 : {
342 : int iDir, i;
343 :
344 : /* -------------------------------------------------------------------- */
345 : /* Vertical checking. */
346 : /* -------------------------------------------------------------------- */
347 600 : if( bDoVerticalCheck )
348 : {
349 400 : int nXSize = MAX(iStart+1,iEnd+1);
350 :
351 20400 : for( i = 0; i < nXSize; i++ )
352 : {
353 : int iBand;
354 20000 : int bIsNonBlack = FALSE;
355 :
356 : // are we already terminated for this column?
357 20000 : if( panLastLineCounts[i] > nMaxNonBlack )
358 16199 : continue;
359 :
360 14145 : for( iBand = 0; iBand < nSrcBands; iBand++ )
361 : {
362 :
363 10704 : if( bNearWhite )
364 : {
365 1563 : if( (255 - pabyLine[i * nDstBands + iBand]) > nNearDist )
366 : {
367 96 : bIsNonBlack = TRUE;
368 96 : break;
369 : }
370 : }
371 : else
372 : {
373 9141 : if( pabyLine[i * nDstBands + iBand] > nNearDist )
374 : {
375 264 : bIsNonBlack = TRUE;
376 264 : break;
377 : }
378 : }
379 : }
380 :
381 3801 : if( bIsNonBlack )
382 : {
383 360 : panLastLineCounts[i]++;
384 :
385 360 : if( panLastLineCounts[i] > nMaxNonBlack )
386 360 : continue;
387 : }
388 : else
389 3441 : panLastLineCounts[i] = 0;
390 :
391 13764 : for( iBand = 0; iBand < nSrcBands; iBand++ )
392 : {
393 10323 : if( bNearWhite )
394 1467 : pabyLine[i * nDstBands + iBand] = 255;
395 : else
396 8856 : pabyLine[i * nDstBands + iBand] = 0;
397 : }
398 3441 : if( nSrcBands != nDstBands )
399 2457 : pabyLine[i * nDstBands + nDstBands - 1] = 0;
400 : }
401 : }
402 :
403 : /* -------------------------------------------------------------------- */
404 : /* Horizontal Checking. */
405 : /* -------------------------------------------------------------------- */
406 600 : if( bDoHorizontalCheck )
407 : {
408 400 : int nNonBlackPixels = 0;
409 :
410 400 : if( iStart < iEnd )
411 200 : iDir = 1;
412 : else
413 200 : iDir = -1;
414 :
415 2891 : for( i = iStart; i != iEnd; i += iDir )
416 : {
417 : int iBand;
418 2891 : int bIsNonBlack = FALSE;
419 :
420 10394 : for( iBand = 0; iBand < nSrcBands; iBand++ )
421 : {
422 7903 : if( bNearWhite )
423 : {
424 1300 : if( (255 - pabyLine[i * nDstBands + iBand]) > nNearDist )
425 : {
426 100 : bIsNonBlack = TRUE;
427 100 : break;
428 : }
429 : }
430 : else
431 : {
432 6603 : if( pabyLine[i * nDstBands + iBand] > nNearDist )
433 : {
434 300 : bIsNonBlack = TRUE;
435 300 : break;
436 : }
437 : }
438 : }
439 :
440 2891 : if( bIsNonBlack )
441 : {
442 400 : nNonBlackPixels++;
443 :
444 400 : if( nNonBlackPixels > nMaxNonBlack )
445 400 : return;
446 : }
447 : else
448 2491 : nNonBlackPixels = 0;
449 :
450 9964 : for( iBand = 0; iBand < nSrcBands; iBand++ )
451 : {
452 7473 : if( bNearWhite )
453 1200 : pabyLine[i * nDstBands + iBand] = 255;
454 : else
455 6273 : pabyLine[i * nDstBands + iBand] = 0;
456 : }
457 2491 : if( nSrcBands != nDstBands )
458 1794 : pabyLine[i * nDstBands + nDstBands - 1] = 0;
459 : }
460 : }
461 :
462 : }
|