1 /* Copyright 2016 Google Inc. All Rights Reserved. 2 Author: zip753 (at) gmail.com (Ivan Nikulin) 3 4 Distributed under MIT license. 5 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 6 */ 7 8 /* Tool for drawing diff PPM images between two input PGM images. Normally used 9 with backward reference histogram drawing tool. */ 10 11 #include <algorithm> 12 #include <cassert> 13 #include <cmath> 14 #include <cstdint> 15 #include <cstdio> 16 #include <cstdlib> /* exit, EXIT_FAILURE */ 17 #include <vector> 18 19 #if !defined(CHECK) 20 #define CHECK(X) if (!(X)) exit(EXIT_FAILURE); 21 #endif 22 23 typedef uint8_t* ScanLine; 24 typedef ScanLine* Image; 25 26 void ReadPGM(FILE* f, Image* image, size_t* height, size_t* width) { 27 int colors; 28 CHECK(fscanf(f, "P5\n%lu %lu\n%d\n", width, height, &colors) == 3); 29 assert(colors == 255); 30 ScanLine* lines = new ScanLine[*height]; 31 *image = lines; 32 for (int i = *height - 1; i >= 0; --i) { 33 ScanLine line = new uint8_t[*width]; 34 lines[i] = line; 35 CHECK(fread(line, 1, *width, f) == *width); 36 } 37 } 38 39 void CalculateDiff(int** diff, Image image1, Image image2, 40 size_t height, size_t width) { 41 for (size_t i = 0; i < height; ++i) { 42 for (size_t j = 0; j < width; ++j) { 43 diff[i][j] = static_cast<int>(image1[i][j]) - image2[i][j]; 44 } 45 } 46 } 47 48 void DrawDiff(int** diff, Image image1, Image image2, 49 size_t height, size_t width, FILE* f) { 50 int max = -1234; 51 int min = +1234; 52 for (size_t i = 0; i < height; ++i) { 53 for (size_t j = 0; j < width; ++j) { 54 if (max < diff[i][j]) max = diff[i][j]; 55 if (min > diff[i][j]) min = diff[i][j]; 56 int img_min = std::min(255 - image1[i][j], 255 - image2[i][j]); 57 if (max < img_min) max = img_min; 58 } 59 } 60 61 int abs_max = -min; 62 if (abs_max < max) abs_max = max; 63 64 fprintf(f, "P6\n%lu %lu\n%d\n", width, height, abs_max); 65 66 uint8_t* row = new uint8_t[3 * width]; 67 for (int i = height - 1; i >= 0; --i) { 68 for (int j = 0; j < width; ++j) { 69 int min_val = std::min(255 - image1[i][j], 255 - image2[i][j]); 70 int max_val = std::max(min_val, abs(diff[i][j])); 71 if (diff[i][j] > 0) { /* red */ 72 row[3 * j + 0] = abs_max - max_val + diff[i][j]; 73 row[3 * j + 1] = abs_max - max_val; 74 row[3 * j + 2] = abs_max - max_val + min_val; 75 } else { /* green */ 76 row[3 * j + 0] = abs_max - max_val; 77 row[3 * j + 1] = abs_max - max_val - diff[i][j]; 78 row[3 * j + 2] = abs_max - max_val + min_val; 79 } 80 } 81 fwrite(row, 1, 3 * width, f); 82 } 83 delete[] row; 84 } 85 86 int main(int argc, char** argv) { 87 if (argc != 4) { 88 printf("usage: %s pgm1 pgm2 diff_ppm_path\n", argv[0]); 89 return 1; 90 } 91 92 Image image1, image2; 93 size_t h1, w1, h2, w2; 94 95 FILE* fimage1 = fopen(argv[1], "rb"); 96 ReadPGM(fimage1, &image1, &h1, &w1); 97 fclose(fimage1); 98 99 FILE* fimage2 = fopen(argv[2], "rb"); 100 ReadPGM(fimage2, &image2, &h2, &w2); 101 fclose(fimage2); 102 103 if (!(h1 == h2 && w1 == w2)) { 104 printf("Images must have the same size.\n"); 105 return 1; 106 } 107 108 int** diff = new int*[h1]; 109 for (size_t i = 0; i < h1; ++i) diff[i] = new int[w1]; 110 CalculateDiff(diff, image1, image2, h1, w1); 111 112 FILE* fdiff = fopen(argv[3], "wb"); 113 DrawDiff(diff, image1, image2, h1, w1, fdiff); 114 fclose(fdiff); 115 116 return 0; 117 } 118