|
degate 0.1.1
|
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::tr1::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::tr1::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::tr1::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::tr1::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::tr1::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::tr1::shared_ptr<ImageTypeDst> dst, 00210 std::tr1::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->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::tr1::shared_ptr<ImageTypeDst> dst, 00229 std::tr1::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->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::tr1::shared_ptr<ImageTypeDst> dst, 00254 std::tr1::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::tr1::shared_ptr<ImageTypeDst> dst, 00279 std::tr1::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->get_pixel_as<gs_byte_pixel_t>(x, y); 00287 dst->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::tr1::shared_ptr<ImageTypeDst>, std::tr1::shared_ptr<ImageTypeSrc>) 00294 */ 00295 00296 template<typename ImageType> 00297 void convert_to_greyscale(std::tr1::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::tr1::shared_ptr<ImageTypeDst> dst, 00308 std::tr1::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->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->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->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->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->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::tr1::shared_ptr<ImageTypeDst> dst, 00378 std::tr1::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::tr1::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::tr1::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::tr1::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::tr1::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::tr1::shared_ptr<ImageTypeDst> dst, 00441 std::tr1::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->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->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::tr1::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::tr1::shared_ptr<ImageTypeDst> dst, 00514 std::tr1::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->get_pixel_as<typename ImageTypeDst::pixel_type>(x, y); 00526 dst->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::tr1::shared_ptr<ImageTypeDst> dst, 00539 std::tr1::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->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::tr1::shared_ptr<ImageTypeDst> dst, 00586 std::tr1::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->set_pixel_as<typename ImageTypeSrc::pixel_type>(x, y, out); 00617 } 00618 } 00619 00620 } 00621 00622 00623 } 00624 00625 #endif
1.7.4