1 /*
2 * ptstitch.c
3 *
4 * Routines related to stitching and creation of alpha channels
5 *
6 * Copyright Helmut Dersch and Daniel M. German
7 *
8 * Aug 2006
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This software is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this software; see the file COPYING. If not, a copy
22 * can be downloaded from http://www.gnu.org/licenses/gpl.html, or
23 * obtained by writing to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 *
26 *
27 * Author: Daniel M German dmgerman at uvic doooot ca
28 *
29 */
30
31 #include <assert.h>
32
33 #include "filter.h"
34 #include "pttiff.h"
35 #include "file.h"
36 #include "ptstitch.h"
37 #include "PTcommon.h"
38 #include "ptfeather.h"
39 #include "metadata.h"
40
41 // Get the value of a channel in the pixel pointed by ptr
panoStitchPixelChannelGet(unsigned char * ptr,int bytesPerChannel,int channel)42 unsigned int panoStitchPixelChannelGet(unsigned char *ptr, int bytesPerChannel, int channel)
43 {
44 uint16_t *pixel16;
45 assert(ptr != NULL);
46
47 assert(channel >= 0 && channel <=3);
48 assert(bytesPerChannel == 1 || bytesPerChannel ==2);
49
50 if (bytesPerChannel == 1) {
51 return *(ptr + channel);
52 }
53 else if (bytesPerChannel == 2) {
54 pixel16 = (uint16_t *) ptr;
55 return *(pixel16+channel);
56 }
57 else {
58 assert(0);
59 return 0;// fix warning.
60 }
61
62 }
63
64 // Get the value of a channel in the pixel pointed by ptr
panoStitchPixelChannelSet(unsigned char * ptr,int bytesPerChannel,int channel,unsigned int value)65 void panoStitchPixelChannelSet(unsigned char *ptr, int bytesPerChannel, int channel, unsigned int value)
66 {
67 uint16_t *pixel16;
68 assert(ptr != NULL);
69
70 assert(channel >= 0 && channel <=3);
71 assert(bytesPerChannel == 4 || bytesPerChannel ==8);
72
73 if (bytesPerChannel == 4) {
74 *(ptr + channel) = value;
75 }
76 else if (bytesPerChannel == 8) {
77 pixel16 = (uint16_t *) ptr;
78 *(pixel16+channel) = value;
79 }
80 else {
81 assert(0);
82 }
83
84 }
85
panoStitchPixelMapGet(unsigned char * ptr,int bytesPerPixel)86 static unsigned int panoStitchPixelMapGet(unsigned char *ptr, int bytesPerPixel)
87 {
88 uint16_t *pixel16;
89 unsigned char *temp;
90 assert(ptr != NULL);
91
92 assert(bytesPerPixel == 4 || bytesPerPixel ==8);
93
94 // We use 2 channel (Green in 16 bit) and (Green and Blue in 8 bit images) to store the map
95
96 // Make sure this happens in char *
97 temp = ptr + bytesPerPixel /2;
98 pixel16 = (uint16_t *) temp;
99
100 return *pixel16;
101 }
102
103 // Set the value of a given channel in the pixel pointed by ptr
panoStitchPixelMapSet(unsigned char * ptr,int bytesPerPixel,unsigned int value)104 static void panoStitchPixelMapSet(unsigned char *ptr, int bytesPerPixel, unsigned int value)
105 {
106 uint16_t *ptr16;
107 unsigned char *temp;
108
109 // We use 2 channel (Green in 16 bit) and (Green and Blue in 8 bit images) to store the map
110
111 assert(bytesPerPixel == 4 || bytesPerPixel ==8);
112 assert(ptr != NULL);
113
114 assert(value >= 0);
115 assert(value <= 0xffff);
116
117 temp = ptr + bytesPerPixel /2;
118 ptr16 = (uint16_t *) temp;
119
120 *ptr16 = value;
121 }
122
123 // Set the map of a pixel only if it is necessary
panoStitchPixelDetermineMap(unsigned char * pixel,int bytesPerPixel,unsigned int * count)124 static void panoStitchPixelDetermineMap(unsigned char *pixel, int bytesPerPixel, unsigned int *count)
125 {
126
127 int alphaChannel;
128 unsigned int value = 0;
129
130 assert(bytesPerPixel == 4 || bytesPerPixel ==8);
131 assert(pixel != NULL);
132
133 alphaChannel = panoStitchPixelChannelGet(pixel, bytesPerPixel/4, 0);
134
135 if (alphaChannel == 0) {
136 *count = 0;
137 }
138 else {
139 (*count)++;
140 }
141 value = panoStitchPixelMapGet(pixel, bytesPerPixel);
142
143 if (value < *count) {
144 *count = value;
145 }
146 else
147 panoStitchPixelMapSet(pixel, bytesPerPixel, *count);
148
149 }
150
151
152
153 /*
154 * This routine creates the stitching mask map
155 *
156 * Stitching maps contain the same alpha channel than the original image, but instead of pixel data they contiain
157 * an "index" of how good that pixel is. Pixels at the center of the image have better indexes than
158 * those at the edge
159 *
160 */
panoStitchComputeMaskMap(Image * image)161 void panoStitchComputeMaskMap(Image * image)
162 {
163
164 int column;
165 int row;
166 unsigned char *ptr;
167 unsigned char *pixel = NULL;
168 unsigned int count;
169 int bytesPerPixel = 0;
170 unsigned int alphaChannel;
171
172
173 // determine the type of image
174 bytesPerPixel = panoImageBytesPerPixel(image);
175
176 // Use the GreenBlue pixel area is used to keep a counter of the
177 // minimum distance (in pixels) away we are from the edges of the
178 // mask (horizontal or vertical)
179
180 // The algorithm is fairly simple:
181
182 // For each column
183 // Process each row from top to down
184 // Set each pixel counter to the number of pixels from edge of the mask (from the left)
185 // Process each row from bottom to top
186 // Set each pixel counter to the minimum between current counter and number of pixels
187 // from edge (from the right)
188
189 // for each row
190 // repeat the same algorithm (done per column)
191
192 for (column = 0; column < panoImageWidth(image); column++) {
193 count = 0;
194 // Point to the given column in row 0
195
196 ptr = panoImageData(image) + column * bytesPerPixel;
197
198 // fprintf(stderr, "St1.1 Column[%d]\n", column);
199
200
201 // From top to bottom
202 for (row = 0; row < panoImageHeight(image); row++) {
203
204 // Get alpha channel for this point
205 pixel = ptr + row * panoImageBytesPerLine(image);
206
207 alphaChannel = panoStitchPixelChannelGet(pixel, bytesPerPixel/4, 0);
208
209 if (alphaChannel == 0) {
210 count = 0;
211 }
212 else {
213 count++;
214 }
215 // In the first pass we ALWAYS set the map because it is uninitialized
216 panoStitchPixelMapSet(pixel, bytesPerPixel, count);
217 }
218
219 count = 0;
220 row = image->height;
221
222 // From bottom to top
223 while (--row >= 0) {
224 pixel = ptr + row * image->bytesPerLine;
225
226 panoStitchPixelDetermineMap(pixel, bytesPerPixel, &count);
227
228 } //while
229
230 // fprintf(stderr, "St1.5 Column[%d]\n", column);
231
232 } //
233
234 ///////////// row by row
235
236 // fprintf(stderr, "St2\n");
237
238 for (row = 0; row < image->height; row++) {
239 count = 0;
240 ptr = row * image->bytesPerLine + *(image->data);
241
242 // process from left to right
243 for (column = 0; column < image->width; column++) {
244 pixel = ptr + panoImageBytesPerPixel(image) * column;
245
246 panoStitchPixelDetermineMap(pixel, bytesPerPixel, &count);
247 } // for column
248
249 //-----------------------------;;
250
251
252 // fprintf(stderr, "St3\n");
253
254 count = 0;
255 column = image->width;
256
257 while (--column >= 0) {
258 pixel = ptr + panoImageBytesPerPixel(image) * column;
259
260 panoStitchPixelDetermineMap(pixel, bytesPerPixel, &count);
261 }
262 } // end of for row
263
264 }
265
266
267 //
268 // Compute the map of the stitching mask and create a file with it.
269 // The stitching mask will be contained in the GB channels (this is,
270 // the 16 bits corresponding to the G and B channel will contain a uint16_t that
271 // contains, for that particular point, the stitching mask.
272 //
panoStitchCreateMaskMapFiles(fullPath * inputFiles,fullPath * maskFiles,int numberImages)273 int panoStitchCreateMaskMapFiles(fullPath * inputFiles, fullPath * maskFiles,
274 int numberImages)
275 {
276 int index;
277 char tempString[512];
278 Image image;
279
280 if (ptQuietFlag == 0)
281 Progress(_initProgress, "Preparing Stitching Masks");
282
283 // for each image, create merging mask and save to temporal file
284 for (index = 0; index < numberImages; index++) {
285
286 sprintf(tempString, "%d", index * 100 / numberImages);
287
288 // Do progress
289 if (ptQuietFlag == 0) {
290 if (Progress(_setProgress, tempString) == 0) {
291 return 0;
292 }
293 }
294
295 if (panoTiffRead(&image, inputFiles[index].name) == 0) {
296 PrintError("Could not read TIFF-file");
297 return 0;
298 }
299
300 // Compute the stitching mask in-situ
301 panoStitchComputeMaskMap(&image);
302
303 strcpy(maskFiles[index].name, inputFiles[0].name);
304
305 if (panoFileMakeTemp(&maskFiles[index]) == 0) {
306 PrintError("Could not make Tempfile");
307 return -1;
308 }
309
310 if (panoTiffWrite(&image, maskFiles[index].name) == 0) {
311 PrintError("Could not write TIFF-file [%s]", maskFiles[index].name);
312 return -1;
313 }
314
315 // fprintf(stderr, "Written to file %s\n", maskFiles[index].name);
316
317 panoImageDispose(&image);
318
319 } // for (index...
320
321 // Do progress
322
323 if (!ptQuietFlag)
324
325 Progress(_setProgress, "100");
326 Progress(_disposeProgress, tempString);
327
328 return 1;
329 }
330
331 /*
332 * This routine takes one given row from numberImages images and tries
333 * to set their alpha channel to show the one with the 'best' pixel
334 *
335 * TODO: unify this and the 8 bits version
336 */
panoStitchSetBestAlphaChannel16bits(unsigned char * imagesBuffer,int numberImages,pano_ImageMetadata * imageParms)337 static void panoStitchSetBestAlphaChannel16bits(unsigned char *imagesBuffer,
338 int numberImages,
339 pano_ImageMetadata * imageParms)
340 {
341 // fprintf(stderr, "SetBestAlphaChannel16bits not supported yet\n");
342 //assert(0); // it should not be here... yet
343
344 unsigned char *pixel;
345 uint16_t *ptrCount;
346 uint16_t best;
347 uint16_t maskValue;
348 int column;
349 int j;
350 int bytesPerLine;
351
352 assert(imageParms->bytesPerPixel == 8);
353
354 bytesPerLine = imageParms->cropInfo.fullWidth * imageParms->bytesPerPixel;
355
356 for (column = 0, pixel = imagesBuffer;
357 column < imageParms->cropInfo.fullWidth; column++, pixel += imageParms->bytesPerPixel) {
358
359 best = 0;
360 ptrCount = (uint16_t *) (pixel + 2);
361 maskValue = *ptrCount;
362
363 // find the image with the highest value
364
365 for (j = 1; j < numberImages; j++) {
366
367 ptrCount = (uint16_t *) (pixel + bytesPerLine * j + 2);
368
369 if (*ptrCount > maskValue) {
370
371 best = j;
372 maskValue = *ptrCount;
373
374 }
375 } // for j
376
377 if (maskValue != 0) {
378
379 // set the mask of the ones above, but not below... interesting...
380
381 for (j = best + 1; j < numberImages; j++) {
382 uint16_t *pixel2;
383
384 pixel2 = (uint16_t *) (pixel + bytesPerLine * j);
385
386 if (0 != *pixel2) {
387 *pixel2 = 1;
388 }
389 }
390 }
391 } // for i
392
393
394 }
395
396 /*
397 * This routine takes one given row from numberImages images and tries
398 * to set their alpha channel to show the one with the 'best' pixel
399 *
400 * TODO: unify this and the 16 bits version
401 */
panoStitchSetBestAlphaChannel8bits(unsigned char * imagesBuffer,int numberImages,pano_ImageMetadata * imageParms)402 static void panoStitchSetBestAlphaChannel8bits(unsigned char *imagesBuffer,
403 int numberImages,
404 pano_ImageMetadata * imageParms)
405 {
406 unsigned char *pixel;
407 uint16_t *ptrCount;
408 uint16_t best;
409 uint16_t maskValue;
410 int column;
411 int j;
412
413 int bytesPerLine;
414
415 assert(imageParms->bytesPerPixel == 4);
416
417 bytesPerLine = imageParms->cropInfo.fullWidth * imageParms->bytesPerPixel;
418
419 for (column = 0, pixel = imagesBuffer;
420 column < imageParms->cropInfo.fullWidth;
421 column++, pixel += 4) {
422
423 best = 0;
424 ptrCount = (uint16_t *) (pixel + 2);
425 maskValue = *ptrCount;
426
427 // find the image with the highest value
428
429 for (j = 1; j < numberImages; j++) {
430 unsigned char *temp;
431
432 temp = (pixel + bytesPerLine * j + 2);
433
434 ptrCount = (uint16_t *) temp;
435
436 if (*ptrCount > maskValue) {
437
438 best = j;
439 maskValue = *ptrCount;
440
441 }
442 } // for j
443
444 if (maskValue != 0) {
445
446 // set the mask of the ones above, but not below... interesting...
447
448 for (j = best + 1; j < numberImages; j++) {
449 unsigned char *pixel2;
450
451 pixel2 = pixel + bytesPerLine * j;
452
453 if (0 != *pixel2) {
454 *pixel2 = 1;
455 }
456 }
457 }
458 } // for i
459
460 }
461
462
463
464
465 /*
466 * Creates an image with RBG from input but alpha channel from mask
467 *
468 */
panoStitchReplaceAlphaChannel(fullPath * inputImage,fullPath * mask,fullPath * output)469 static int panoStitchReplaceAlphaChannel(fullPath * inputImage, fullPath * mask,
470 fullPath * output)
471 {
472 unsigned char *imageRowBuffer = NULL;
473 unsigned char *maskRowBuffer = NULL;
474 int row;
475 int j;
476
477 int returnValue = 0;
478 int numberBytesToCopy;
479 unsigned char *source;
480 unsigned char *destination;
481
482 pano_Tiff *imageFile = NULL;
483 pano_Tiff *outputFile = NULL;
484 pano_Tiff *maskFile = NULL;
485
486 int jumpBytes;
487 int alphaChannelOffset;
488
489 // For each row
490 // Read row of image
491 // Read row of mask
492 // Replace alpha channel in image with masks alpha channel
493 // Write row
494 //
495 //Note that all three images involved here (input image, mask image and
496 //resulting image) have the same dimensions, and we don't care what the
497 //dimensions are the for the final output image
498
499 // FIRST PREPARE THE FILES
500
501 // Open input image
502 if ((imageFile = panoTiffOpen(inputImage->name)) == NULL) {
503 PrintError("Could not open TIFF-file");
504 returnValue = 0;
505 goto end;
506 }
507
508 //Allocate line buffers for image and mask
509 if ((imageRowBuffer = calloc(panoTiffBytesPerLine(imageFile), 1)) == NULL
510 || (maskRowBuffer =
511 calloc(panoTiffBytesPerLine(imageFile), 1)) == NULL) {
512 PrintError("Not enough memory");
513 returnValue = 0;
514 goto end;
515 }
516
517 // Open mask file
518 if ((maskFile = panoTiffOpen(mask->name)) == NULL) {
519 PrintError("Could not open mask file");
520 returnValue = 0;
521 goto end;
522 }
523
524 // Create output file
525 if ((outputFile =
526 panoTiffCreate(output->name, &maskFile->metadata)) == NULL) {
527 PrintError("Could not create TIFF-file");
528 returnValue = 0;
529 goto end;
530 }
531
532 // Processing one row at a time
533 if (panoTiffBitsPerPixel(imageFile) == 32) {
534 jumpBytes = 4;
535 alphaChannelOffset = 3;
536 numberBytesToCopy = 1;
537 }
538 else {
539 jumpBytes = 8;
540 alphaChannelOffset = 6;
541 numberBytesToCopy = 2;
542 }
543
544 // Process one line at a time.
545
546 for (row = 0; row < panoTiffImageHeight(imageFile); row++) {
547
548 TIFFReadScanline(imageFile->tiff, imageRowBuffer, row, 0);
549 TIFFReadScanline(maskFile->tiff, maskRowBuffer, row, 0);
550
551 destination = imageRowBuffer + alphaChannelOffset;
552 source = maskRowBuffer + alphaChannelOffset;
553
554 // Copy alpha channel...
555 for (j = 0; j < panoTiffImageWidth(imageFile); j++) {
556 int k;
557 // Copy the mask
558 // TODO: use memcpy
559 for (k = 0; k < numberBytesToCopy; k++) {
560 *(destination + k) = *(source + k);
561 }
562
563 destination += jumpBytes;
564 source += jumpBytes;
565 }
566
567 // Write row to output
568 if (TIFFWriteScanline(outputFile->tiff, imageRowBuffer, row, 0) != 1) {
569 PrintError
570 ("Unable to write data to output file (ReplaceAlphaChannel)\n");
571 returnValue = 0;
572 goto end;
573 }
574
575 }
576
577 returnValue = 1;
578 end:
579
580 if(imageFile)
581 panoTiffClose(imageFile);
582
583 if(maskFile)
584 panoTiffClose(maskFile);
585
586 if(outputFile)
587 panoTiffClose(outputFile);
588
589 free(imageRowBuffer);
590 free(maskRowBuffer);
591
592 return returnValue;
593 }
594
595
panoStitchCalculateAlphaChannel(unsigned char * imagesBuffer,int numberImages,pano_ImageMetadata * imageMetadata)596 static void panoStitchCalculateAlphaChannel(unsigned char *imagesBuffer,
597 int numberImages,
598 pano_ImageMetadata * imageMetadata)
599 {
600
601 switch (imageMetadata->bitsPerSample) {
602 case 8:
603 panoStitchSetBestAlphaChannel8bits(imagesBuffer, numberImages, imageMetadata);
604 break;
605 case 16:
606 panoStitchSetBestAlphaChannel16bits(imagesBuffer, numberImages, imageMetadata);
607 break;
608 default:
609 fprintf(stderr,
610 "CalculateAlphaChannel not supported for this image type (%d bitsPerPixel)\n",
611 imageMetadata->bitsPerPixel);
612 exit(1);
613 }
614 }
615
616
617 /*
618 * Create the alpha channels for the output images
619 *
620 */
panoStitchCreateAlphaChannels(fullPath * masksNames,fullPath * alphaChannelNames,int numberImages)621 int panoStitchCreateAlphaChannels(fullPath * masksNames,
622 fullPath * alphaChannelNames, int numberImages)
623 {
624 pano_Tiff **tiffMasks;
625 pano_Tiff **tiffAlphaChannels;
626 unsigned char *imagesBuffer = NULL;
627 unsigned char *ptrBuffer;
628 int index;
629 char tempString[24];
630
631 int returnValue = 0;
632 int fullSizeRowIndex;
633
634 int fullImageWidth;
635 int fullImageHeight;
636 int bytesPerLine;
637 int bitsPerPixel;
638
639 assert(numberImages > 0);
640 assert(masksNames != NULL);
641 assert(alphaChannelNames != NULL);
642
643 //printf("CreateAlpha %d\n", numberImages);
644
645 // Allocate arrays of TIFF* for the input and output
646 // images. process is one row at a time, with all images
647 // processed at the same time
648 tiffMasks = calloc(numberImages, sizeof(pano_Tiff));
649 tiffAlphaChannels = calloc(numberImages, sizeof(pano_Tiff));
650
651 if (tiffAlphaChannels == NULL || tiffMasks == NULL) {
652 PrintError("Not enough memory");
653 return 0;
654 }
655
656 if (ptQuietFlag == 0)
657 Progress(_initProgress, "Calculating Alpha Channel");
658
659 // Alpha Channel calculation
660 // Open for read
661 // mask files
662 // and input files
663 // Open for write alpha channel files
664
665 // Open up an input image, then create a corresponding output image...repeat for all images
666 for (index = 0; index < numberImages; index++) {
667
668 if ((tiffMasks[index] = panoTiffOpen(masksNames[index].name)) == 0) {
669 PrintError("Could not open TIFF-file");
670 return 0;
671 }
672
673 strcpy(alphaChannelNames[index].name, masksNames[0].name);
674
675 if (panoFileMakeTemp(&alphaChannelNames[index]) == 0) {
676 PrintError("Could not make Tempfile");
677 goto end;
678 }
679
680 tiffAlphaChannels[index] =
681 panoTiffCreate(alphaChannelNames[index].name,
682 panoTiffImageMetadata(tiffMasks[index]));
683
684 if (tiffAlphaChannels[index] == NULL) {
685 PrintError("Could not create TIFF-file");
686 goto end;
687 }
688
689 } // finished opening up output files
690
691 // Get sizes of the entire image
692 fullImageWidth = panoTiffFullImageWidth(tiffMasks[0]);
693 fullImageHeight = panoTiffFullImageHeight(tiffMasks[0]);
694 bitsPerPixel = panoTiffBitsPerPixel(tiffMasks[0]);
695 bytesPerLine = fullImageWidth * panoTiffBytesPerPixel(tiffMasks[0]);
696
697 for (index = 0; index < numberImages; index++) {
698 assert(fullImageWidth == panoTiffFullImageWidth(tiffMasks[index]));
699 assert(fullImageHeight == panoTiffFullImageHeight(tiffMasks[index]));
700 assert(bitsPerPixel == panoTiffBitsPerPixel(tiffMasks[index]));
701 assert(bytesPerLine == fullImageWidth * panoTiffBytesPerPixel(tiffMasks[index]));
702 }
703
704 // just for the sake of it
705
706 // The imagesBuffer contains as many rows as we have input images, and
707 // each row is as wide as the final output image
708
709 // printf("Fulls ize %d %d bytesPerLine %d bitsPerPixel %d\n", numberImages,
710 // bytesPerLine,
711 // bytesPerLine, bitsPerPixel);
712
713 imagesBuffer = calloc(numberImages, bytesPerLine);
714 if (imagesBuffer == NULL) {
715 PrintError("Not enough memory");
716 goto end;
717 }
718
719 assert(fullImageWidth > 0 && fullImageHeight > 0 && bytesPerLine > 0
720 && bitsPerPixel > 0);
721 // fprintf(stderr, "Files have been created, process each row\n");
722
723 //iterate one row at a time, and for each row process all images
724
725 for (fullSizeRowIndex = 0; fullSizeRowIndex < fullImageHeight;
726 fullSizeRowIndex++) {
727
728 // Update progress
729 if (ptQuietFlag == 0) {
730 if (fullSizeRowIndex == (fullSizeRowIndex / 20) * 20) {
731 sprintf(tempString, "%lu",
732 (long unsigned) fullSizeRowIndex * 100 /
733 fullImageHeight);
734 if (Progress(_setProgress, tempString) == 0) {
735 // If user aborts, end
736 returnValue = 0;
737 goto end;
738 }
739 }
740 }
741
742
743 // process the current row for all images
744 for (ptrBuffer = imagesBuffer, index = 0; index < numberImages;
745 index++, ptrBuffer += bytesPerLine) {
746
747 if (!panoTiffReadScanLineFullSize
748 (tiffMasks[index], ptrBuffer, fullSizeRowIndex)) {
749 PrintError("Error reading temporary TIFF data");
750 returnValue = 0;
751 goto end;
752 }
753 RGBAtoARGB(ptrBuffer, fullImageWidth, bitsPerPixel);
754
755 }
756
757
758
759 //calculate the alpha channel for this row in all images
760
761 panoStitchCalculateAlphaChannel(imagesBuffer, numberImages,
762 panoTiffImageMetadata(tiffMasks[0]));
763
764
765
766 //write out the alpha channel data for this row to all output images
767 for (index = 0, ptrBuffer = imagesBuffer; index < numberImages;
768 index++, ptrBuffer += bytesPerLine) {
769
770
771 ARGBtoRGBA(ptrBuffer, fullImageWidth, bitsPerPixel);
772 if (!panoTiffWriteScanLineFullSize
773 (tiffAlphaChannels[index], ptrBuffer, fullSizeRowIndex)) {
774 PrintError
775 ("Unable to write data to output file (CreateAlphaChannel)\n");
776 returnValue = 0;
777 goto end;
778 }
779 }
780
781
782 } //for fullSizeRowIndex
783 returnValue = 1;
784
785 end:
786
787 if (!ptQuietFlag) {
788 Progress(_setProgress, "100");
789 Progress(_disposeProgress, "");
790 }
791
792 for (index = 0; index < numberImages; index++) {
793 if (tiffMasks[index] != NULL)
794 panoTiffClose(tiffMasks[index]);
795 if (tiffAlphaChannels[index] != NULL)
796 panoTiffClose(tiffAlphaChannels[index]);
797 } // for index.
798
799 free(imagesBuffer);
800 free(tiffAlphaChannels);
801 free(tiffMasks);
802
803 return returnValue;
804 }
805
806
807
808 /**
809 * Replaces the alpha channel in each image in inputFiles with a generated
810 * mask. The mask is calculated so as to route the seam between overlapping
811 * images through the center of the overlap region...
812 */
panoStitchReplaceMasks(fullPath * inputFiles,fullPath * outputFiles,int numberImages,int featherSize)813 int panoStitchReplaceMasks(fullPath * inputFiles, fullPath * outputFiles,
814 int numberImages, int featherSize)
815 {
816 int returnValue = -1; // default to fail
817 fullPath *alphaChannelFiles = NULL;
818 fullPath *maskFiles = NULL;
819 int i;
820 Image image;
821 char tempString[512];
822
823 if (numberImages == 0) {
824 return 0;
825 }
826
827
828 SetImageDefaults(&image);
829
830 maskFiles = calloc(numberImages, sizeof(fullPath));
831 alphaChannelFiles = calloc(numberImages, sizeof(fullPath));
832
833
834 if (maskFiles == NULL || alphaChannelFiles == NULL) {
835 PrintError("Not enough memory");
836 goto end;
837 }
838
839 // CREATE stitching maps
840 if (!panoStitchCreateMaskMapFiles(inputFiles, maskFiles, numberImages)) {
841 PrintError("Could not create the stitching masks");
842 goto end;
843 }
844
845 if (!panoStitchCreateAlphaChannels(maskFiles, alphaChannelFiles, numberImages)) {
846 PrintError("Could not create alpha channels");
847 goto end;
848 }
849
850 // From this point on we do not need to process all files at once. This will save temporary disk space
851
852 for (i = 0; i < numberImages; i++) {
853 fullPath withAlphaChannel;
854
855 sprintf(tempString, "%d", 100 * i / numberImages);
856
857 if (ptQuietFlag == 0) {
858 if (Progress(_setProgress, tempString) == 0) {
859 // We have to delete any temp file
860 goto end;
861 }
862 }
863
864 // We no longer need the mask files
865 remove(maskFiles[i].name);
866
867 // Reuse the temporary name
868 memcpy(&withAlphaChannel, &maskFiles[i], sizeof(fullPath));
869
870 // Replace the alpha channel of the input image
871 if (!panoStitchReplaceAlphaChannel
872 (&inputFiles[i], &alphaChannelFiles[i], &withAlphaChannel)) {
873 PrintError("Unable to replace alpha channel in image %d", i);
874 goto end;
875 }
876 // we no longer need the alpha channel
877 remove(alphaChannelFiles[i].name);
878
879
880 // Do feathering
881 if (featherSize > 0) {
882
883 fullPath feathered;
884
885 memcpy(&feathered, &maskFiles[i], sizeof(fullPath));
886
887 if (!panoFeatherFile(&withAlphaChannel, &feathered, featherSize)) {
888 PrintError("Unable to apply feather to image %d", i);
889 goto end;
890 }
891
892 if (strcmp(withAlphaChannel.name, feathered.name) != 0) {
893 remove(withAlphaChannel.name);
894 }
895 rename(feathered.name, outputFiles[i].name);
896 }
897 else {
898 rename(withAlphaChannel.name, outputFiles[i].name);
899
900 }
901 }
902 returnValue = 0; //success
903
904 end:
905 free(maskFiles);
906 free(alphaChannelFiles);
907
908 return returnValue;
909
910 }
911
912 /// BLENDING ROUTINES
913
panoStitchBlendLayers8Bit(unsigned char ** imageDataBuffers,int counterImageFiles,unsigned char * resultBuffer,int lines,int imageWidth,int scanLineSize)914 static void panoStitchBlendLayers8Bit(unsigned char **imageDataBuffers, int counterImageFiles,
915 unsigned char *resultBuffer, int lines, int imageWidth,
916 int scanLineSize)
917 {
918
919 // 0x8(%ebp) imageDataBuffers
920 // 0xc(%ebp) counterImageFiles
921 // 0x10(%ebp) resultBuffer
922 // 0x14(%ebp) lines
923 // 0x18(%ebp) imageWidth
924 // 0x1c(%ebp) scanLineSize
925
926 // 0xffffffdc(%ebp) imageIndex
927 // 0xffffffe0(%ebp) alphaChannel
928 // 0xffffffe4(%ebp) blue
929 // 0xffffffe8(%ebp) green
930 // 0xffffffec(%ebp) red
931 // 0xfffffff0(%ebp) rowOffset
932 // 0xfffffff4(%ebp) pixelOffset
933 // 0xfffffff8(%ebp) currentLine
934 // 0xfffffffc(%ebp) currentColumn
935
936 int imageIndex = 0;
937 unsigned int colours[3];
938 unsigned int alphaChannel;
939 unsigned int currentLine;
940 unsigned int currentColumn;
941 unsigned int rowOffset;
942
943 currentLine = 0;
944
945 for (currentLine = 0; (int)currentLine < lines; currentLine++) {
946
947 //printf("Currnet line %d\n", currentLine);
948
949 rowOffset = scanLineSize * currentLine;
950
951 for (currentColumn = 0; (int) currentColumn < imageWidth; currentColumn++) {
952
953 unsigned int pixelOffset;
954 unsigned int i;
955
956 // printf("Currnet column %d\n", currentColumn);
957
958 pixelOffset = rowOffset + currentColumn * 4;
959
960
961 // Initialize colours for this pixel
962 alphaChannel = 0;
963 for (i = 0; i < 3; i++)
964 colours[i] = 0;
965
966
967 // Do alpha blending, from top to bottom. Bail out when alpha channel is equal to maximum
968
969 for (imageIndex = counterImageFiles - 1; imageIndex >= 0;
970 imageIndex--) {
971
972 unsigned int alphaContribution;
973 unsigned char *ptrPixel;
974 unsigned int bottomAlpha;
975 unsigned int index;
976
977
978 // printf("Currnet image %d\n", imageIndex);
979
980
981 // The alpha blending algorithm is (for premultiplied values)
982
983 // C_result = C_above + C_below * (1 - alpha_above)
984 // A_result = Alpha_above + alpha_below * (1 - alpha_above)
985
986 // Find pixel in this layer
987 ptrPixel = imageDataBuffers[imageIndex] + pixelOffset;
988
989
990 // printf("TO read pixel\n");
991
992 bottomAlpha = *(ptrPixel + 3); // this should be the value of the mask for this particular pixel
993
994 // printf("After read pixel\n");
995
996 alphaContribution =
997 ((0xff - alphaChannel) * bottomAlpha) / 0xff;
998
999 // I don't really think this step is necessary, but due to innestability of the calculation
1000 // alphaContribution it might overflow the byte valuex
1001
1002 if (alphaChannel + alphaContribution > 0xff) {
1003 alphaContribution = 0xff - alphaChannel;
1004 }
1005
1006 alphaChannel += alphaContribution;
1007
1008 // Makek sure the alpha channel is within range
1009 assert(alphaChannel >= 0 && alphaChannel <= 0xff);
1010
1011 // now do the colours
1012
1013 // printf("TO set pixel\n");
1014
1015 for (index = 0; index < 3; index++) {
1016 colours[index] += (*(ptrPixel + index) * alphaContribution) / 0xff; //
1017
1018 if (!(colours[index] >= 0 && colours[index] <= 0xff)) {
1019 printf("PPPPPPPPPPPPPPPPPanic %d index [%d]\n",
1020 colours[index], index);
1021 }
1022 assert(colours[index] >= 0 && colours[index] <= 0xff);
1023 }
1024
1025 // We don't need to continue if the alpha channel is at the max
1026 if (alphaChannel >= 0xff)
1027 break;
1028
1029 } // for (imageIndex =counterImageFiles-1; imageIndex >= 0; imageIndex--) {
1030
1031 // Is it really necessary to check the values of the colours and alphachannel to make
1032 // sure they are not overflowing a byte?
1033
1034 // Set the value of the pixel
1035 for (i = 0; i < 3; i++) {
1036 assert(colours[i] <= 0xff && colours[i] >= 0);
1037 *(resultBuffer + pixelOffset + i) = colours[i];
1038 }
1039
1040 *(resultBuffer + pixelOffset + 3) = alphaChannel;
1041
1042
1043 } //(currentColumn < imageWidth)
1044
1045 } //for currentLine < lines
1046
1047 }
1048
panoStitchBlendLayers16Bit(unsigned char ** imageDataBuffers,int counterImageFiles,unsigned char * resultBuffer,int lines,int imageWidth,int scanLineSize)1049 static void panoStitchBlendLayers16Bit(unsigned char **imageDataBuffers, int counterImageFiles,
1050 unsigned char *resultBuffer, int lines, int imageWidth,
1051 int scanLineSize)
1052 {
1053
1054 int imageIndex = 0;
1055 unsigned long long colours[3];
1056 unsigned long long alphaChannel;
1057 unsigned int currentLine;
1058 unsigned int currentColumn;
1059 unsigned int rowOffset;
1060
1061 uint16_t *u16ResultBuffer = (uint16_t *) resultBuffer;
1062 uint16_t **u16ImageDataBuffers = (uint16 **) imageDataBuffers;
1063
1064 currentLine = 0;
1065
1066
1067 for (currentLine = 0; (int) currentLine < lines; currentLine++) {
1068
1069 // printf("Lines %d\n", lines);
1070 // printf("Width %d\n", imageWidth);
1071 // printf("length %d\n", scanLineSize);
1072
1073
1074 //printf("Currnet line %d\n", currentLine);
1075
1076 // scanLineSize is in bytes, but we need the length in 16bit units
1077 rowOffset = (scanLineSize / 2) * currentLine;
1078
1079 for (currentColumn = 0; (int) currentColumn < imageWidth; currentColumn++) {
1080
1081 unsigned int pixelOffset;
1082 unsigned int i;
1083
1084 // printf("Currnet column %d\n", currentColumn);
1085
1086 pixelOffset = rowOffset + currentColumn * 4;
1087
1088 // printf("Currnet offset %d\n", pixelOffset);
1089
1090 // Initialize colours for this pixel
1091 alphaChannel = 0;
1092 for (i = 0; i < 3; i++)
1093 colours[i] = 0;
1094
1095
1096 // Do alpha blending, from top to bottom. Bail out when alpha channel is equal to maximum
1097
1098 for (imageIndex = counterImageFiles - 1; imageIndex >= 0;
1099 imageIndex--) {
1100
1101 unsigned long long alphaContribution;
1102 uint16_t *ptrPixel;
1103 unsigned long long bottomAlpha;
1104 unsigned int index;
1105
1106
1107 // printf("Currnet image %d\n", imageIndex);
1108
1109
1110 // The alpha blending algorithm is (for premultiplied values)
1111
1112 // C_result = C_above + C_below * (1 - alpha_above)
1113 // A_result = Alpha_above + alpha_below * (1 - alpha_above)
1114
1115 // Find pixel in this layer
1116 ptrPixel = u16ImageDataBuffers[imageIndex] + pixelOffset;
1117
1118
1119 // printf("TO read pixel\n");
1120
1121 bottomAlpha = *(ptrPixel + 3); // this should be the value of the mask for this particular pixel
1122
1123 //printf("After read pixel\n");
1124
1125 alphaContribution =
1126 ((0xffff - alphaChannel) * bottomAlpha) / 0xffff;
1127
1128 // I don't really think this step is necessary, but due to innestability of the calculation
1129 // alphaContribution it might overflow the byte valuex
1130
1131 if (alphaChannel + alphaContribution > 0xffff) {
1132 alphaContribution = 0xffff - alphaChannel;
1133 }
1134
1135 alphaChannel += alphaContribution;
1136
1137 // Makek sure the alpha channel is within range
1138 assert(alphaChannel >= 0 && alphaChannel <= 0xffff);
1139
1140 // now do the colours
1141
1142 //printf("TO set pixel\n");
1143
1144 for (index = 0; index < 3; index++) {
1145 colours[index] += (*(ptrPixel + index) * alphaContribution) / 0xffff; //
1146 if (!(colours[index] >= 0 && colours[index] <= 0xffff)) {
1147 printf("PPPPPPPPPPPPPPPPPanic %lld index [%d]\n",
1148 colours[index], index);
1149 }
1150 assert(colours[index] >= 0 && colours[index] <= 0xffff);
1151 }
1152
1153 // We don't need to continue if the alpha channel is at the max
1154 if (alphaChannel >= 0xffff)
1155 break;
1156
1157 } // for (imageIndex =counterImageFiles-1; imageIndex >= 0; imageIndex--) {
1158
1159 // Is it really necessary to check the values of the colours and alphachannel to make
1160 // sure they are not overflowing a byte?
1161 // printf("Done loop\n");
1162 // Set the value of the pixel
1163 for (i = 0; i < 3; i++) {
1164 assert(colours[i] <= 0xffff && colours[i] >= 0);
1165 *(u16ResultBuffer + pixelOffset + i) = (uint16_t)(colours[i]);
1166 }
1167 // printf("Done loop 2\n");
1168 *(u16ResultBuffer + pixelOffset + 3) = (uint16_t)(alphaChannel);
1169
1170
1171 } //(currentColumn < imageWidth)
1172
1173 } //for currentLine < lines
1174
1175 }
1176
1177 /*
1178 * TODO: BLend 8 and 16 versions into one
1179 * Blend all the images into one
1180 */
panoStitchBlendLayers(unsigned char ** imageDataBuffers,unsigned int counterImageFiles,unsigned char * resultBuffer,unsigned int linesToRead,int imageWidth,unsigned int bitsPerPixel,unsigned int scanLineSize)1181 void panoStitchBlendLayers(unsigned char **imageDataBuffers,
1182 unsigned int counterImageFiles,
1183 unsigned char *resultBuffer,
1184 unsigned int linesToRead,
1185 int imageWidth,
1186 unsigned int bitsPerPixel,
1187 unsigned int scanLineSize)
1188 {
1189
1190 if (bitsPerPixel == 32) {
1191 panoStitchBlendLayers8Bit(imageDataBuffers, counterImageFiles, resultBuffer,
1192 linesToRead, imageWidth, scanLineSize);
1193 }
1194 else if (bitsPerPixel == 64) {
1195 panoStitchBlendLayers16Bit(imageDataBuffers, counterImageFiles, resultBuffer,
1196 linesToRead, imageWidth, scanLineSize);
1197 }
1198 }
1199