// Copyright 2002, 2003, 2004 David Hilvert , // /* This file is part of the Anti-Lamenessing Engine. The Anti-Lamenessing Engine is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. The Anti-Lamenessing Engine is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the Anti-Lamenessing Engine; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * image_bayer_ale_real.h: Bayer-patterned image represented by an array of ale_reals */ #ifndef __image_bayer_ale_real_h__ #define __image_bayer_ale_real_h__ #include "exposure/exposure.h" #include "point.h" #include "image.h" #include "image_ale_real.h" template class image_bayer_ale_real : public image { private: /* * Data structures without file support. */ ale_sreal *_p; /* * Data structures for file support. */ FILE *support; mutable ale_sreal *_p_segments[RESIDENT_DIVISIONS]; mutable int dirty_segments[RESIDENT_DIVISIONS]; mutable int resident_list[RESIDENT_DIVISIONS]; mutable int resident_next; int resident_max; int rows_per_segment; mutable thread::rwlock_t rwlock; private: /* * X offset of 'R' element */ unsigned int r_x_offset() const { return bayer & 0x1; } /* * Y offset of 'R' element */ unsigned int r_y_offset() const { return (bayer & 0x2) >> 1; } public: /* * Return the color of a given pixel. */ unsigned int bayer_color(unsigned int i, unsigned int j) const { return (i + r_y_offset()) % 2 + (j + r_x_offset()) % 2; } char get_channels(int i, int j) const { return (1 << bayer_color(i, j)); } public: /* * Wrapper encapsulating details of the separation between the * resident-checking implementation and non-checking. */ static inline image *new_image_bayer_ale_real(unsigned int dimy, unsigned int dimx, unsigned int depth, unsigned int bayer, const char *name = "anonymous", exposure *exp = NULL) { unsigned int resident = image::get_resident(); if (resident == 0 || resident * 1000000 > dimy * dimx) return new image_bayer_ale_real<0>(dimy, dimx, depth, bayer, name, exp); return new image_bayer_ale_real<1>(dimy, dimx, depth, bayer, name, exp); } image_bayer_ale_real (unsigned int dimy, unsigned int dimx, unsigned int depth, unsigned int bayer, const char *name = "anonymous", exposure *exp = NULL) : image(dimy, dimx, depth, name, exp, bayer) { assert (bayer == IMAGE_BAYER_BGRG || bayer == IMAGE_BAYER_GBGR || bayer == IMAGE_BAYER_GRGB || bayer == IMAGE_BAYER_RGBG); if (disk_support == 0) { _p = new ale_sreal[dimx * dimy]; assert (_p); if (!_p) { fprintf(stderr, "Could not allocate memory for image data.\n"); exit(1); } for (unsigned int i = 0; i < dimx * dimy; i++) { _p[i] = 0; } } else { rows_per_segment = (int) ceil((double) dimy / (double) RESIDENT_DIVISIONS); assert (rows_per_segment > 0); for (int i = 0; i < RESIDENT_DIVISIONS; i++) { _p_segments[i] = NULL; dirty_segments[i] = 0; resident_list[i] = -1; } resident_max = (unsigned int) floor((image::get_resident() * 1000000) / (rows_per_segment * dimx)); assert (resident_max <= RESIDENT_DIVISIONS); if (resident_max == 0) { ui::get()->error_hint( "No segments resident in image array.", "Try recompiling with more RESIDENT_DIVISIONS"); } resident_next = 0; support = tmpfile(); if (!support) { ui::get()->error_hint( "Unable to create temporary file to support image array.", "Set --resident 0, or Win32/64 users might run as admin."); } ale_sreal *zero = new ale_sreal[dimx]; assert(zero); for (unsigned int i = 0; i < dimx; i++) zero[i] = 0; for (unsigned int i = 0; i < dimy; i++) { unsigned int c = fwrite(zero, sizeof(ale_sreal), dimx, support); if (c < dimx) ui::get()->error_hint("Image array support file error.", "Submit a bug report."); } delete[] zero; } } virtual ~image_bayer_ale_real() { if (disk_support == 0) { delete[] _p; } else { for (int i = 0; i < RESIDENT_DIVISIONS; i++) { if (_p_segments[i]) delete[] _p_segments[i]; } fclose(support); } } void resident_begin(unsigned int segment) const { rwlock.rdlock(); if (_p_segments[segment]) return; rwlock.unlock(); rwlock.wrlock(); if (_p_segments[segment]) return; if (resident_list[resident_next] >= 0) { /* * Eject a segment */ if (dirty_segments[resident_list[resident_next]]) { fseek(support, rows_per_segment * _dimx * sizeof(ale_sreal) * resident_list[resident_next], SEEK_SET); assert(_p_segments[resident_list[resident_next]]); fwrite(_p_segments[resident_list[resident_next]], sizeof(ale_sreal), rows_per_segment * _dimx, support); dirty_segments[resident_list[resident_next]] = 0; } delete[] _p_segments[resident_list[resident_next]]; _p_segments[resident_list[resident_next]] = NULL; } resident_list[resident_next] = segment; _p_segments[segment] = new ale_sreal[_dimx * rows_per_segment]; assert (_p_segments[segment]); fseek(support, rows_per_segment * _dimx * sizeof(ale_sreal) * segment, SEEK_SET); fread(_p_segments[segment], sizeof(ale_sreal), rows_per_segment * _dimx, support); /* * Update the next ejection candidate. */ resident_next++; if (resident_next >= resident_max) resident_next = 0; } void resident_end(unsigned int segment) const { rwlock.unlock(); } void set_chan(unsigned int y, unsigned int x, unsigned int k, ale_sreal c) { assert (k == bayer_color(y, x)); if (disk_support == 0) { _p[y * _dimx + x] = c; } else { int segment = y / rows_per_segment; assert (segment < RESIDENT_DIVISIONS); resident_begin(segment); _p_segments[segment][(y % rows_per_segment) * _dimx + x] = c; dirty_segments[segment] = 1; resident_end(segment); } } void add_chan(unsigned int y, unsigned int x, unsigned int k, ale_sreal c) { assert (k == bayer_color(y, x)); if (disk_support == 0) { _p[y * _dimx + x] += c; } else { int segment = y / rows_per_segment; assert (segment < RESIDENT_DIVISIONS); resident_begin(segment); _p_segments[segment][(y % rows_per_segment) * _dimx + x] += c; dirty_segments[segment] = 1; resident_end(segment); } } void div_chan(unsigned int y, unsigned int x, unsigned int k, ale_sreal c) { assert (k == bayer_color(y, x)); if (disk_support == 0) { _p[y * _dimx + x] /= c; } else { int segment = y / rows_per_segment; assert (segment < RESIDENT_DIVISIONS); resident_begin(segment); _p_segments[segment][(y % rows_per_segment) * _dimx + x] /= c; dirty_segments[segment] = 1; resident_end(segment); } } ale_sreal get_chan(unsigned int y, unsigned int x, unsigned int k) const { #if 0 /* * This may be expensive. */ assert (k == bayer_color(y, x)); #endif if (disk_support == 0) { return _p[y * _dimx + x]; } else { int segment = y / rows_per_segment; assert (segment < RESIDENT_DIVISIONS); resident_begin(segment); ale_sreal result = _p_segments[segment] [(y % rows_per_segment) * _dimx + x]; resident_end(segment); return result; } } /* * This method throws away data not stored at this pixel * position. */ void set_pixel(unsigned int y, unsigned int x, spixel p) { set_chan(y, x, bayer_color(y, x), p[bayer_color(y, x)]); } /* * This method uses bilinear interpolation. */ spixel get_pixel(unsigned int y, unsigned int x) const { pixel result; unsigned int k = bayer_color(y, x); ale_real sum; unsigned int num; result[k] = get_chan(y, x, k); if (k == 1) { unsigned int k1 = bayer_color(y + 1, x); unsigned int k2 = 2 - k1; sum = 0; num = 0; if (y > 0) { sum += get_chan(y - 1, x, k1); num++; } if (y < _dimy - 1) { sum += get_chan(y + 1, x, k1); num++; } assert (num > 0); result[k1] = sum / num; sum = 0; num = 0; if (x > 0) { sum += get_chan(y, x - 1, k2); num++; } if (x < _dimx - 1) { sum += get_chan(y, x + 1, k2); num++; } assert (num > 0); result[k2] = sum / num; return result; } sum = 0; num = 0; if (y > 0) { sum += get_chan(y - 1, x, 1); num++; } if (x > 0) { sum += get_chan(y, x - 1, 1); num++; } if (y < _dimy - 1) { sum += get_chan(y + 1, x, 1); num++; } if (x < _dimx - 1) { sum += get_chan(y, x + 1, 1); num++; } assert (num > 0); result[1] = sum / num; sum = 0; num = 0; if (y > 0 && x > 0) { sum += get_chan(y - 1, x - 1, 2 - k); num++; } if (y > 0 && x < _dimx - 1) { sum += get_chan(y - 1, x + 1, 2 - k); num++; } if (y < _dimy - 1 && x > 0) { sum += get_chan(y + 1, x - 1, 2 - k); num++; } if (y < _dimy - 1 && x < _dimx - 1) { sum += get_chan(y + 1, x + 1, 2 - k); num++; } result[2 - k] = sum/num; return result; } spixel get_raw_pixel(unsigned int y, unsigned int x) const { pixel result; int k = bayer_color(y, x); result[k] = get_chan(y, x, k); return result; } /* * Make a new image suitable for receiving scaled values. */ virtual image *scale_generator(int height, int width, int depth, const char *name) const { return new_image_ale_real(height, width, depth, name, _exp); } /* * Extend the image area to the top, bottom, left, and right, * initializing the new image areas with black pixels. */ image *_extend(int top, int bottom, int left, int right) { /* * Bayer-patterned images should always represent inputs, * which should not ever be extended. */ assert(0); return NULL; } private: void trigger(pixel multiplier) { for (unsigned int i = 0; i < _dimy; i++) for (unsigned int j = 0; j < _dimx; j++) { unsigned int k = bayer_color(i, j); set_chan(i, j, k, get_chan(i, j, k) * multiplier[k]); } } }; /* * Wrapper encapsulating details of the separation between the * resident-checking implementation and non-checking. */ static inline image *new_image_bayer_ale_real(unsigned int dimy, unsigned int dimx, unsigned int depth, unsigned int bayer, const char *name = "anonymous", exposure *exp = NULL) { return image_bayer_ale_real<0>::new_image_bayer_ale_real(dimy, dimx, depth, bayer, name, exp); } #endif