Home | History | Annotate | Download | only in image_processing
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 
      9 #if defined(WIN32)
     10 #include <fcntl.h>
     11 #include <io.h>
     12 #endif
     13 
     14 enum Commands {
     15   CROP_PIXELS = 0,
     16   HISTOGRAM = 1,
     17   BOUNDING_BOX = 2
     18 };
     19 
     20 bool ReadInt(int* out) {
     21   return fread(out, sizeof(*out), 1, stdin) == 1;
     22 }
     23 
     24 void WriteResponse(void* data, int size) {
     25   fwrite(&size, sizeof(size), 1, stdout);
     26   fwrite(data, size, 1, stdout);
     27   fflush(stdout);
     28 }
     29 
     30 struct Box {
     31   Box() : left(), top(), right(), bottom() {}
     32 
     33   // Expected input is:
     34   // left, top, width, height
     35   bool Read() {
     36     int width;
     37     int height;
     38     if (!(ReadInt(&left) && ReadInt(&top) &&
     39           ReadInt(&width) && ReadInt(&height))) {
     40       fprintf(stderr, "Could not parse Box.\n");
     41       return false;
     42     }
     43     if (left < 0 || top < 0 || width < 0 || height < 0) {
     44       fprintf(stderr, "Box dimensions must be non-negative.\n");
     45       return false;
     46     }
     47     right = left + width;
     48     bottom = top + height;
     49     return true;
     50   }
     51 
     52   void Union(int x, int y) {
     53     if (left > x) left = x;
     54     if (right <= x) right = x + 1;
     55     if (top > y) top = y;
     56     if (bottom <= y) bottom = y + 1;
     57   }
     58 
     59   int width() const { return right - left; }
     60   int height() const { return bottom - top; }
     61 
     62   int left;
     63   int top;
     64   int right;
     65   int bottom;
     66 };
     67 
     68 
     69 // Represents a bitmap buffer with a crop box.
     70 struct Bitmap {
     71   Bitmap() : pixels(NULL) {}
     72 
     73   ~Bitmap() {
     74     if (pixels)
     75       delete[] pixels;
     76   }
     77 
     78   // Expected input is:
     79   // bpp, width, height, box, pixels
     80   bool Read() {
     81     int bpp;
     82     int width;
     83     int height;
     84     if (!(ReadInt(&bpp) && ReadInt(&width) && ReadInt(&height))) {
     85       fprintf(stderr, "Could not parse Bitmap initializer.\n");
     86       return false;
     87     }
     88     if (bpp <= 0 || width <= 0 || height <= 0) {
     89       fprintf(stderr, "Dimensions must be positive.\n");
     90       return false;
     91     }
     92 
     93     int size = width * height * bpp;
     94 
     95     row_stride = width * bpp;
     96     pixel_stride = bpp;
     97     total_size = size;
     98     row_size = row_stride;
     99 
    100     if (!box.Read()) {
    101       fprintf(stderr, "Expected crop box argument not found.\n");
    102       return false;
    103     }
    104 
    105     if (box.bottom * row_stride > total_size ||
    106         box.right * pixel_stride > row_size) {
    107       fprintf(stderr, "Crop box overflows the bitmap.\n");
    108       return false;
    109     }
    110 
    111     pixels = new unsigned char[size];
    112     if (fread(pixels, sizeof(pixels[0]), size, stdin) <
    113         static_cast<size_t>(size)) {
    114       fprintf(stderr, "Not enough pixels found,\n");
    115       return false;
    116     }
    117 
    118     total_size = (box.bottom - box.top) * row_stride;
    119     row_size = (box.right - box.left) * pixel_stride;
    120     data = pixels + box.top * row_stride + box.left * pixel_stride;
    121     return true;
    122   }
    123 
    124   void WriteCroppedPixels() const {
    125     int out_size = row_size * box.height();
    126     unsigned char* out = new unsigned char[out_size];
    127     unsigned char* dst = out;
    128     for (const unsigned char* row = data;
    129         row < data + total_size;
    130         row += row_stride, dst += row_size) {
    131       // No change in pixel_stride, so we can copy whole rows.
    132       memcpy(dst, row, row_size);
    133     }
    134 
    135     WriteResponse(out, out_size);
    136     delete[] out;
    137   }
    138 
    139   unsigned char* pixels;
    140   Box box;
    141   // Points at the top-left pixel in |pixels|.
    142   const unsigned char* data;
    143   // These counts are in bytes.
    144   int row_stride;
    145   int pixel_stride;
    146   int total_size;
    147   int row_size;
    148 };
    149 
    150 
    151 static inline
    152 bool PixelsEqual(const unsigned char* pixel1, const unsigned char* pixel2,
    153                  int tolerance) {
    154   // Note: this works for both RGB and RGBA. Alpha channel is ignored.
    155   return (abs(pixel1[0] - pixel2[0]) <= tolerance) &&
    156          (abs(pixel1[1] - pixel2[1]) <= tolerance) &&
    157          (abs(pixel1[2] - pixel2[2]) <= tolerance);
    158 }
    159 
    160 
    161 static inline
    162 bool PixelsEqual(const unsigned char* pixel, int color, int tolerance) {
    163   unsigned char pixel2[3] = { color >> 16, color >> 8, color };
    164   return PixelsEqual(pixel, pixel2, tolerance);
    165 }
    166 
    167 
    168 static
    169 bool Histogram(const Bitmap& bmp) {
    170   int ignore_color;
    171   int tolerance;
    172   if (!(ReadInt(&ignore_color) && ReadInt(&tolerance))) {
    173     fprintf(stderr, "Could not parse HISTOGRAM command.\n");
    174     return false;
    175   }
    176 
    177   const int kLength = 3 * 256;
    178   int counts[kLength] = {};
    179 
    180   for (const unsigned char* row = bmp.data; row < bmp.data + bmp.total_size;
    181        row += bmp.row_stride) {
    182     for (const unsigned char* pixel = row; pixel < row + bmp.row_size;
    183        pixel += bmp.pixel_stride) {
    184       if (ignore_color >= 0 && PixelsEqual(pixel, ignore_color, tolerance))
    185         continue;
    186       ++(counts[256 * 0 + pixel[0]]);
    187       ++(counts[256 * 1 + pixel[1]]);
    188       ++(counts[256 * 2 + pixel[2]]);
    189     }
    190   }
    191 
    192   WriteResponse(counts, sizeof(counts));
    193   return true;
    194 }
    195 
    196 
    197 static
    198 bool BoundingBox(const Bitmap& bmp) {
    199   int color;
    200   int tolerance;
    201   if (!(ReadInt(&color) && ReadInt(&tolerance))) {
    202     fprintf(stderr, "Could not parse BOUNDING_BOX command.\n");
    203     return false;
    204   }
    205 
    206   Box box;
    207   box.left = bmp.total_size;
    208   box.top = bmp.total_size;
    209   box.right = 0;
    210   box.bottom = 0;
    211 
    212   int count = 0;
    213   int y = 0;
    214   for (const unsigned char* row = bmp.data; row < bmp.data + bmp.total_size;
    215        row += bmp.row_stride, ++y) {
    216     int x = 0;
    217     for (const unsigned char* pixel = row; pixel < row + bmp.row_size;
    218          pixel += bmp.pixel_stride, ++x) {
    219       if (!PixelsEqual(pixel, color, tolerance))
    220         continue;
    221       box.Union(x, y);
    222       ++count;
    223     }
    224   }
    225 
    226   int response[] = { box.left, box.top, box.width(), box.height(), count };
    227   WriteResponse(response, sizeof(response));
    228   return true;
    229 }
    230 
    231 
    232 int main() {
    233   Bitmap bmp;
    234   int command;
    235 
    236 #if defined(WIN32)
    237   _setmode(_fileno(stdin), _O_BINARY);
    238   _setmode(_fileno(stdout), _O_BINARY);
    239 #else
    240   stdin = freopen(NULL, "rb", stdin);
    241   stdout = freopen(NULL, "wb", stdout);
    242 #endif
    243 
    244   if (!bmp.Read()) return -1;
    245   if (!ReadInt(&command)) {
    246     fprintf(stderr, "Expected command.\n");
    247     return -1;
    248   }
    249   switch (command) {
    250     case CROP_PIXELS:
    251       bmp.WriteCroppedPixels();
    252       break;
    253     case BOUNDING_BOX:
    254       if (!BoundingBox(bmp)) return -1;
    255       break;
    256     case HISTOGRAM:
    257       if (!Histogram(bmp)) return -1;
    258       break;
    259     default:
    260       fprintf(stderr, "Unrecognized command\n");
    261       return -1;
    262   }
    263   return 0;
    264 }
    265