degate  0.1.2
ImageManipulation.h
Go to the documentation of this file.
00001 /* -*-c++-*-
00002 
00003  This file is part of the IC reverse engineering tool degate.
00004 
00005  Copyright 2008, 2009, 2010 by Martin Schobert
00006 
00007  Degate is free software: you can redistribute it and/or modify
00008  it under the terms of the GNU General Public License as published by
00009  the Free Software Foundation, either version 3 of the License, or
00010  any later version.
00011 
00012  Degate is distributed in the hope that it will be useful,
00013  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  GNU General Public License for more details.
00016 
00017  You should have received a copy of the GNU General Public License
00018  along with degate. If not, see <http://www.gnu.org/licenses/>.
00019 
00020 */
00021 
00022 #ifndef __IMAGEMANIPULATION_H__
00023 #define __IMAGEMANIPULATION_H__
00024 
00025 #include <BoundingBox.h>
00026 #include <Image.h>
00027 #include <FilterKernel.h>
00028 #include <Statistics.h>
00029 #include <ImageStatistics.h>
00030 
00031 #include <boost/format.hpp>
00032 
00033 namespace degate {
00034 
00035   /**
00036    * Flip image in place from left to right.
00037    */
00038   template<typename ImageType>
00039   void flip_left_right(std::shared_ptr<ImageType> img) {
00040     if(img->get_width() == 1) return;
00041 
00042     for(unsigned int y = 0; y < img->get_height(); y++)
00043       for(unsigned int x = 0; x < (img->get_width() >> 1); x++) {
00044         unsigned int other_x = img->get_width() - 1 - x;
00045         typename ImageType::pixel_type p1 = img->get_pixel(x, y);
00046         typename ImageType::pixel_type p2 = img->get_pixel(other_x, y);
00047         img->set_pixel(x, y, p2);
00048         img->set_pixel(other_x, y, p1);
00049       }
00050   }
00051 
00052   /**
00053    * Flip image in place from top to down.
00054    */
00055   template<typename ImageType>
00056   void flip_up_down(std::shared_ptr<ImageType> img) {
00057     if(img->get_height() == 1) return;
00058 
00059     for(unsigned int y = 0; y < (img->get_height() >> 1); y++)
00060       for(unsigned int x = 0; x < img->get_width(); x++) {
00061         unsigned int other_y = img->get_height() - 1 - y;
00062         typename ImageType::pixel_type p1 = img->get_pixel(x, y);
00063         typename ImageType::pixel_type p2 = img->get_pixel(x, other_y);
00064         img->set_pixel(x, y, p2);
00065         img->set_pixel(x, other_y, p1);
00066       }
00067   }
00068 
00069   /**
00070    * Flip image in place from top to down and from left to right.
00071    */
00072   template<typename ImageType>
00073   void flip_both(std::shared_ptr<ImageType> img) {
00074     flip_up_down<ImageType>(img);
00075     flip_left_right<ImageType>(img);
00076   }
00077 
00078   /**
00079    * Convert an RGBA pixel to a hue value.
00080    */
00081    inline double rgba_to_hue(rgba_pixel_t p) {
00082      int red = MASK_R(p);
00083      int green = MASK_G(p);
00084      int blue = MASK_B(p);
00085 
00086      int max = std::max(red, std::max(green, blue));
00087      int min = std::min(red, std::min(green, blue));
00088      double delta = max - min;
00089      double h = 0;
00090 
00091      if(max == min) h = 0;
00092      else if(max == red) h = 60.0 *  (double)(green-blue)/delta;
00093      else if(max == green) h = 60.0 * (2.0 +(double)(blue-red)/delta);
00094      else if(max == blue) h = 60.0 * (4.0 + (double)(red-green)/delta);
00095      if(h < 0) h+=360;
00096 
00097      //h *= 255.0/360.0;
00098      return h;
00099 
00100    }
00101 
00102   /**
00103    * Convert an RGBA pixel to a saturation value.
00104    */
00105 
00106    inline double rgba_to_saturation(rgba_pixel_t p) {
00107      int red = MASK_R(p);
00108      int green = MASK_G(p);
00109      int blue = MASK_B(p);
00110 
00111      double max = std::max(red, std::max(green, blue));
00112      double min = std::min(red, std::min(green, blue));
00113 
00114      if(max == 0) return 0;
00115      else return (max - min) / max;
00116    }
00117 
00118   /**
00119    * Convert an RGBA pixel to a lightness value.
00120    */
00121 
00122    inline double rgba_to_lightness(rgba_pixel_t p) {
00123      int red = MASK_R(p);
00124      int green = MASK_G(p);
00125      int blue = MASK_B(p);
00126 
00127      return std::max(red, std::max(green, blue));
00128    }
00129 
00130 
00131   /**
00132    * Convert a pixel from a source type to a destination type. The
00133    * default implementation will just make a copy of the pixel.
00134    */
00135   template<typename PixelTypeDst, typename PixelTypeSrc>
00136   inline PixelTypeDst convert_pixel(PixelTypeSrc p) {
00137     return p;
00138   }
00139 
00140 
00141   /**
00142    * Convert pixel value from from rgba -> byte.
00143    */
00144   template<>
00145   inline gs_byte_pixel_t convert_pixel<gs_byte_pixel_t, rgba_pixel_t>(rgba_pixel_t p) {
00146     return RGBA_TO_GS_BY_VAL(p);
00147   }
00148 
00149   /**
00150    * Convert pixel value from from rgba -> double.
00151    */
00152   template<>
00153   inline gs_double_pixel_t convert_pixel<gs_double_pixel_t, rgba_pixel_t>(rgba_pixel_t p) {
00154     return RGBA_TO_GS_BY_VAL(p);
00155   }
00156 
00157 
00158 
00159   /**
00160    * Convert pixel value from from byte -> rgba.
00161    */
00162   template<>
00163   inline rgba_pixel_t convert_pixel<rgba_pixel_t, gs_byte_pixel_t>(gs_byte_pixel_t p) {
00164     return MERGE_CHANNELS(p, p, p, 255);
00165   }
00166 
00167   /**
00168    * Convert pixel value from from double -> rgba.
00169    */
00170   template<>
00171   inline rgba_pixel_t convert_pixel<rgba_pixel_t, gs_double_pixel_t>(gs_double_pixel_t p) {
00172     gs_byte_pixel_t b = p;
00173     return MERGE_CHANNELS(b, b, b, 255);
00174   }
00175 
00176 
00177 
00178 
00179 
00180   /*
00181    * Get pixel value as ...
00182    */
00183   template<typename PixelTypeDst, typename ImageTypeSrc>
00184   inline PixelTypeDst get_pixel_as(typename std::shared_ptr<ImageTypeSrc> img,
00185                                    unsigned int x, unsigned int y) {
00186     return convert_pixel<PixelTypeDst, typename ImageTypeSrc::pixel_type>(img->get_pixel(x, y));
00187   }
00188 
00189   /*
00190    * Set a pixel value as ...
00191    */
00192   template<typename PixelTypeSrc, typename ImageTypeDst>
00193   inline void set_pixel_as(typename std::shared_ptr<ImageTypeDst> img,
00194                     unsigned int x, unsigned int y, PixelTypeSrc p) {
00195 
00196     img->get_pixel(x, y, convert_pixel<typename ImageTypeDst::pixel_type, PixelTypeSrc>(p));
00197   }
00198 
00199 
00200   /**
00201    * Copy an image.
00202    * Copy the source image into the destination image. If the images differ in
00203    * pixel types, the pixel values will be converted. The images can also differ
00204    * in size. Only the region is copied, which is present in both images. It is
00205    * possible that the image \p dst is not completely overwritten, if the image
00206    * \p src is not large enough.
00207    */
00208   template<typename ImageTypeDst, typename ImageTypeSrc>
00209   void copy_image(std::shared_ptr<ImageTypeDst> dst,
00210                   std::shared_ptr<ImageTypeSrc> src) {
00211 
00212 
00213     unsigned int h = std::min(src->get_height(), dst->get_height());
00214     unsigned int w = std::min(src->get_width(), dst->get_width());
00215 
00216     for(unsigned int y = 0; y < h; y++)
00217       for(unsigned int x = 0; x < w; x++)
00218         dst->set_pixel(x, y, src->template get_pixel_as<typename ImageTypeDst::pixel_type>(x, y));
00219   }
00220 
00221 
00222   /**
00223    * Extract a partial image from \p src with the region defined by the parameters
00224    * into a destination image \p dst. Clipping occurs, if the destination image is
00225    * smaller than the region or the image \p src.
00226    */
00227   template<typename ImageTypeDst, typename ImageTypeSrc>
00228   void extract_partial_image(std::shared_ptr<ImageTypeDst> dst,
00229                              std::shared_ptr<ImageTypeSrc> src,
00230                              unsigned int min_x, unsigned int max_x,
00231                              unsigned int min_y, unsigned int max_y) {
00232 
00233     assert(min_x < max_x);
00234     assert(min_y < max_y);
00235 
00236     unsigned int h = std::min(std::min(std::min(src->get_height(), max_y), dst->get_height()), max_y - min_y);
00237     unsigned int w = std::min(std::min(std::min(src->get_width(), max_x), dst->get_width()), max_x - min_x);
00238 
00239     unsigned int dst_x = 0, dst_y = 0, x, y;
00240 
00241     for(y = min_y; y < min_y + h; y++, dst_y++) {
00242       for(x = min_x, dst_x = 0; x < min_x + w; x++, dst_x++)
00243         dst->set_pixel(dst_x, dst_y,
00244                        src->template get_pixel_as<typename ImageTypeDst::pixel_type>(x, y));
00245     }
00246   }
00247 
00248   /**
00249    * Extract a partial image.
00250    * @see extract_partial_image()
00251    */
00252   template<typename ImageTypeDst, typename ImageTypeSrc>
00253   void extract_partial_image(std::shared_ptr<ImageTypeDst> dst,
00254                              std::shared_ptr<ImageTypeSrc> src,
00255                              BoundingBox const& bounding_box) {
00256 
00257     extract_partial_image<ImageTypeDst, ImageTypeSrc>(dst, src,
00258                                                       bounding_box.get_min_x(),
00259                                                       bounding_box.get_max_x(),
00260                                                       bounding_box.get_min_y(),
00261                                                       bounding_box.get_max_y());
00262   }
00263 
00264 
00265   /**
00266    * Convert an image to greyscale.
00267    * You can get the same effect if you copy_image() an RGBA into a greyscale
00268    * image and implicitly use the auto conversion. This function is useful, if
00269    * you don't have two different image types. Then there would be no auto
00270    * conversion.
00271    * You can use that function with \p dst = \p src . Then the image is converted
00272    * in place.
00273    * Like copy_image() this function works only on the region, in which both images
00274    * intersect.
00275    * @see copy_image()
00276    */
00277   template<typename ImageTypeDst, typename ImageTypeSrc>
00278   void convert_to_greyscale(std::shared_ptr<ImageTypeDst> dst,
00279                             std::shared_ptr<ImageTypeSrc> src) {
00280 
00281     unsigned int h = std::min(src->get_height(), dst->get_height());
00282     unsigned int w = std::min(src->get_width(), dst->get_width());
00283 
00284     for(unsigned int y = 0; y < h; y++)
00285       for(unsigned int x = 0; x < w; x++) {
00286         gs_byte_pixel_t p = src->template get_pixel_as<gs_byte_pixel_t>(x, y);
00287         dst->template set_pixel_as<gs_byte_pixel_t>(x, y, p);
00288       }
00289   }
00290 
00291   /**
00292    * In place conversion to a greyscale image.
00293    * @see convert_to_greyscale(std::shared_ptr<ImageTypeDst>, std::shared_ptr<ImageTypeSrc>)
00294    */
00295 
00296   template<typename ImageType>
00297   void convert_to_greyscale(std::shared_ptr<ImageType> img) {
00298     convert_to_greyscale<ImageType, ImageType>(img, img);
00299   }
00300 
00301 
00302   /**
00303    * Scale a source image down by factor 2.
00304    * You can scale images in place.
00305    */
00306   template<typename ImageTypeDst, typename ImageTypeSrc>
00307   void scale_down_by_2(std::shared_ptr<ImageTypeDst> dst,
00308                        std::shared_ptr<ImageTypeSrc> src) {
00309 
00310 
00311     unsigned int dst_x, dst_y, src_x, src_y;
00312 
00313     for(dst_y = 0; dst_y < dst->get_height(); dst_y++) {
00314 
00315       src_y = dst_y * 2;
00316 
00317       for(dst_x = 0; dst_x < dst->get_width(); dst_x++) {
00318 
00319         src_x = dst_x * 2;
00320 
00321         // 1 2
00322         // 3 4
00323 
00324         int i = 1;
00325         unsigned int r = 0, g = 0, b = 0, a = 0;
00326 
00327         rgba_pixel_t pix = src->template get_pixel_as<rgba_pixel_t>(src_x, src_y);
00328         r += MASK_R(pix);
00329         g += MASK_G(pix);
00330         b += MASK_B(pix);
00331         a += MASK_A(pix);
00332 
00333         if(src_x + 1 < src->get_width()) {
00334           pix = src->template get_pixel_as<rgba_pixel_t>(src_x + 1, src_y);
00335           i++;
00336           r += MASK_R(pix);
00337           g += MASK_G(pix);
00338           b += MASK_B(pix);
00339           a += MASK_A(pix);
00340         }
00341 
00342         if(src_y + 1 < src->get_height()) {
00343           pix = src->template get_pixel_as<rgba_pixel_t>(src_x, src_y + 1);
00344           i++;
00345           r += MASK_R(pix);
00346           g += MASK_G(pix);
00347           b += MASK_B(pix);
00348           a += MASK_A(pix);
00349         }
00350 
00351         if(src_x + 1 < src->get_width() && src_y + 1 < src->get_height()) {
00352           pix = src->template get_pixel_as<rgba_pixel_t>(src_x + 1, src_y + 1);
00353           i++;
00354           r += MASK_R(pix);
00355           g += MASK_G(pix);
00356           b += MASK_B(pix);
00357           a += MASK_A(pix);
00358         }
00359 
00360         r /= i;
00361         g /= i;
00362         b /= i;
00363         a /= i;
00364 
00365         dst->template set_pixel_as<rgba_pixel_t>(dst_x, dst_y, MERGE_CHANNELS(r, g, b, a));
00366       }
00367     }
00368   }
00369 
00370 
00371   /**
00372    * Scale a source image down by factor 2.
00373    * @exception DegateRuntimeException This excpetion is thrown if the
00374    *  destination image has no dimension definition.
00375    */
00376   template<typename ImageTypeDst, typename ImageTypeSrc>
00377   void scale_down_by_power_of_2(std::shared_ptr<ImageTypeDst> dst,
00378                                 std::shared_ptr<ImageTypeSrc> src) {
00379 
00380     if(dst->get_width() == 0) throw DegateRuntimeException("Invalid image dimension for destination image.");
00381 
00382     unsigned int scaling = lrint((double)src->get_width() / (double)dst->get_width());
00383 
00384     if(scaling == 1)
00385       copy_image<ImageTypeDst, ImageTypeSrc>(dst, src);
00386     else if(scaling == 2)
00387       scale_down_by_2<ImageTypeDst, ImageTypeSrc>(dst, src);
00388     else {
00389       std::shared_ptr<ImageTypeDst> tmp(new ImageTypeDst(src->get_width(), src->get_height()));
00390       copy_image<ImageTypeDst, ImageTypeSrc>(tmp, src);
00391 
00392       scaling >>= 1;
00393       for(unsigned int i = 0; i < scaling - 1; i*=2) {
00394         scale_down_by_2<ImageTypeDst, ImageTypeDst>(tmp, tmp);
00395       }
00396       scale_down_by_2<ImageTypeDst, ImageTypeDst>(dst, tmp);
00397     }
00398 
00399   }
00400 
00401   /**
00402    * Clear an image.
00403    */
00404   template<typename ImageType>
00405   void clear_image(std::shared_ptr<ImageType> img) {
00406 
00407     for(unsigned int y = 0; y < img->get_height(); y++)
00408       for(unsigned int x = 0; x < img->get_width(); x++)
00409         img->set_pixel(x, y, 0);
00410   }
00411 
00412 
00413 
00414   /**
00415    * Helper function to load existing images in a degate image format.
00416    * We assume that the file or directory, where the image is stored,
00417    * exists.
00418    * @exception InvalidPathException This exception is thrown, if
00419    *   the \p path doen't exists.
00420    */
00421 
00422   template<typename ImageType>
00423   std::shared_ptr<ImageType> load_degate_image(unsigned int width, unsigned int height,
00424                                                     std::string const& path) {
00425     if(!file_exists(path)) {
00426       boost::format fmter("Error in load_degate_image(): The image file or directory %1% does not exist.");
00427       fmter % path;
00428       throw InvalidPathException(fmter.str());
00429     }
00430     return std::shared_ptr<ImageType>(new ImageType(width, height, path));
00431   }
00432 
00433 
00434 
00435   /**
00436    * Normalize a single channel image.
00437    * Source and destination image can be the same image.
00438    */
00439   template<typename ImageTypeDst, typename ImageTypeSrc>
00440   void normalize(std::shared_ptr<ImageTypeDst> dst,
00441                  std::shared_ptr<ImageTypeSrc> src,
00442                  double lower_bound = 0, double upper_bound = 1) {
00443 
00444     assert_is_single_channel_image<ImageTypeSrc>();
00445 
00446     typename ImageTypeSrc::pixel_type src_min = get_minimum<ImageTypeSrc>(src);
00447     typename ImageTypeSrc::pixel_type src_max = get_maximum<ImageTypeSrc>(src);
00448 
00449     if(src_max - src_min == 0) return;
00450 
00451     double shift = -src_min;
00452     double factor = (double)(upper_bound - lower_bound) / (double)(src_max - src_min);
00453 
00454     /*
00455     std::cout
00456       << "lower bound: " << lower_bound << std::endl
00457       << "upper bound: " << upper_bound << std::endl
00458       << std::endl
00459       << "min val    : " << src_min << std::endl
00460       << "max val    : " << src_max << std::endl
00461       << std::endl
00462       << "factor     : " << factor << std::endl
00463       << std::endl
00464       ;
00465     */
00466 
00467     unsigned int h = std::min(src->get_height(), dst->get_height());
00468     unsigned int w = std::min(src->get_width(), dst->get_width());
00469 
00470     for(unsigned int y = 0; y < h; y++) {
00471       for(unsigned int x = 0; x < w; x++) {
00472         typename ImageTypeDst::pixel_type p =
00473           src->template get_pixel_as<typename ImageTypeDst::pixel_type>(x, y);
00474 
00475         double d = ((double)p + shift) * factor + lower_bound;
00476         if(d < lower_bound) {
00477           if(abs(lower_bound - d) < 0.001)
00478             d = lower_bound;
00479           std::cout << "transformed value "<< p << " beyond lower bound: " << d << std::endl;
00480           //d = lower_bound;
00481         }
00482         else if(d > upper_bound) {
00483           if(abs(d - upper_bound) < 0.001)
00484             d = upper_bound;
00485           std::cout << "transformed value "<< p << " beyond upper bound: " << d << std::endl;
00486 
00487         }
00488         assert(d >= lower_bound);
00489         assert(d <= upper_bound);
00490         dst->template set_pixel_as<double>(x, y, d);
00491       }
00492     }
00493 
00494   }
00495 
00496 
00497   /**
00498    * Normalize a single channel image in place.
00499    */
00500   template<typename ImageType>
00501   void normalize(std::shared_ptr<ImageType> img,
00502                  double lower_bound = 0, double upper_bound = 1) {
00503     normalize<ImageType, ImageType>(img, img, lower_bound, upper_bound);
00504   }
00505 
00506   /**
00507    * Thresholding a single channel image.
00508    * Source and destination image can be the same image.
00509    * The thresholding sets a pixel value to 0 if it is below the threshold or
00510    * to a non-0 value if it is greater or equal than the trheshold.
00511    */
00512   template<typename ImageTypeDst, typename ImageTypeSrc>
00513   void thresholding_image(std::shared_ptr<ImageTypeDst> dst,
00514                           std::shared_ptr<ImageTypeSrc> src,
00515                           double threshold) {
00516 
00517     assert_is_single_channel_image<ImageTypeSrc>();
00518 
00519     unsigned int h = std::min(src->get_height(), dst->get_height());
00520     unsigned int w = std::min(src->get_width(), dst->get_width());
00521 
00522     for(unsigned int y = 0; y < h; y++) {
00523       for(unsigned int x = 0; x < w; x++) {
00524         typename ImageTypeDst::pixel_type p =
00525           src->template get_pixel_as<typename ImageTypeDst::pixel_type>(x, y);
00526         dst->template set_pixel_as<double>(x, y, p >= threshold ? 1 : 0);
00527       }
00528     }
00529   }
00530 
00531   /**
00532    * Convolve a single channel source image with a filter kernel
00533    * and write it into a destination image.
00534    * Depending on the filter kernel size there is a region next to the
00535    * image boundary that you cannot use for further processing.
00536    */
00537   template<typename ImageTypeDst, typename ImageTypeSrc>
00538   void convolve(std::shared_ptr<ImageTypeDst> dst,
00539                 std::shared_ptr<ImageTypeSrc> src,
00540                 FilterKernel_shptr kernel) {
00541 
00542     assert_is_single_channel_image<ImageTypeSrc>();
00543 
00544     clear_image<ImageTypeDst>(dst);
00545 
00546     unsigned int h = std::min(src->get_height(), dst->get_height());
00547     unsigned int w = std::min(src->get_width(), dst->get_width());
00548 
00549     unsigned int x, y, i, j;
00550 
00551     for(y = kernel->get_center_row(); y < h - kernel->get_center_row(); y++) {
00552       for(x = kernel->get_center_column(); x < w - kernel->get_center_column(); x++) {
00553 
00554         double accu = 0;
00555 
00556         for(i = 0; i < kernel->get_columns(); i++ ) {
00557           for(j = 0; j < kernel->get_rows(); j++ ) {
00558 
00559             typename ImageTypeSrc::pixel_type p =
00560               src->get_pixel(x - kernel->get_center_column() + i,
00561                              y - kernel->get_center_row() + j);
00562 
00563             double k = kernel->get(kernel->get_columns() - 1 - i,
00564                                    kernel->get_rows() - 1 - j);
00565             accu += k * p;
00566           }
00567         }
00568         dst->template set_pixel_as<double>(x, y, accu);
00569       }
00570     }
00571   }
00572 
00573 
00574   /**
00575    * Filter an (RBGA) image.
00576    *
00577    * @param threshold The threshold parameter is directly passed to the calculate()
00578    *   method of the calculation policy class.
00579    * @exception DegateRuntimeException This exception is thrown if
00580    *   your images are to small for the kernel or if the width of the kernel is
00581    *   to small.
00582    */
00583 
00584   template<typename ImageTypeDst, typename ImageTypeSrc, typename FunctionPolicy>
00585   void filter_image(std::shared_ptr<ImageTypeDst> dst,
00586                     std::shared_ptr<ImageTypeSrc> src,
00587                     unsigned int kernel_width = 3,
00588                     unsigned int threshold = 3) {
00589 
00590     if(kernel_width <= 1)
00591       throw DegateRuntimeException("Error in filter_image(). Kernel width is to small.");
00592 
00593     unsigned int width = std::min(src->get_width(), dst->get_width());
00594     unsigned int height = std::min(src->get_height(), dst->get_height());
00595 
00596     if(width < kernel_width || height < kernel_width)
00597       throw DegateRuntimeException("Error in filter_image(). One of the images is to small.");
00598 
00599     unsigned int kernel_center = kernel_width / 2;
00600 
00601     width -= (kernel_width - kernel_center);
00602     height -= (kernel_width - kernel_center);
00603 
00604     for(unsigned int y = kernel_center; y < height; y++) {
00605       for(unsigned x = kernel_center; x < width; x++) {
00606 
00607         typename ImageTypeSrc::pixel_type out =
00608           FunctionPolicy::calculate(src,
00609                                     x, y,
00610                                     x - kernel_center,
00611                                     x - kernel_center + kernel_width,
00612                                     y - kernel_center,
00613                                     y - kernel_center + kernel_width,
00614                                     threshold);
00615 
00616         dst->template set_pixel_as<typename ImageTypeSrc::pixel_type>(x, y, out);
00617       }
00618     }
00619 
00620   }
00621 
00622 
00623 }
00624 
00625 #endif