/* * This file is part of RawTherapee. * * Copyright (c) 2004-2010 Gabor Horvath * * RawTherapee 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. * * RawTherapee 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 RawTherapee. If not, see . */ #ifndef _IIMAGE_ #define _IIMAGE_ #include #include #include #include #include "rt_math.h" #include "alignedbuffer.h" #include "imagedimensions.h" #include "LUT.h" #include "coord2d.h" #include "procparams.h" #include "color.h" #include "colortemp.h" #define TR_NONE 0 #define TR_R90 1 #define TR_R180 2 #define TR_R270 3 #define TR_VFLIP 4 #define TR_HFLIP 8 #define TR_ROT 3 namespace rtengine { extern const char sImage8[]; extern const char sImage16[]; extern const char sImagefloat[]; class ProgressListener; class Color; enum TypeInterpolation { TI_Nearest, TI_Bilinear }; // -------------------------------------------------------------------- // Generic classes // -------------------------------------------------------------------- class ImageDatas : virtual public ImageDimensions { public: template void convertTo (S srcValue, D &dstValue) { dstValue = static_cast(srcValue); } // parameters that will never be used, replaced by the subclasses r, g and b parameters! // they are still necessary to implement operator() in this parent class virtual ~ImageDatas() {} virtual void allocate (int W, int H) {} virtual void rotate (int deg) {} // free the memory allocated for the image data without deleting the object. virtual void flushData () { allocate(0,0); } virtual void hflip () {} virtual void vflip () {} // Read the raw dump of the data void readData (FILE *fh) {} // Write a raw dump of the data void writeData (FILE *fh) {} virtual void normalizeInt (int srcMinVal, int srcMaxVal) {}; virtual void normalizeFloat (float srcMinVal, float srcMaxVal) {}; virtual void computeHistogramAutoWB (double &avg_r, double &avg_g, double &avg_b, int &n, LUTu &histogram, int compression) {} virtual void getSpotWBData (double &reds, double &greens, double &blues, int &rn, int &gn, int &bn, std::vector &red, std::vector &green, std::vector &blue, int tran) {} virtual void getAutoWBMultipliers (double &rm, double &gm, double &bm) { rm=1.0; bm=1.0; gm=1.0; } virtual const char* getType () const { return "unknown"; } }; template <> inline void ImageDatas::convertTo (unsigned short srcValue, unsigned char &dstValue) { dstValue = (unsigned char)(srcValue >> 8); } template <> inline void ImageDatas::convertTo (unsigned char srcValue, int &dstValue) { dstValue = (int)(srcValue) << 8; } template <> inline void ImageDatas::convertTo (unsigned char srcValue, unsigned short &dstValue) { dstValue = (unsigned short)(srcValue) << 8; } template <> inline void ImageDatas::convertTo (float srcValue, unsigned char &dstValue) { dstValue = (unsigned char)( (unsigned short)(srcValue) >> 8 ); } template <> inline void ImageDatas::convertTo (unsigned char srcValue, float &dstValue) { dstValue = float( (unsigned short)(srcValue) << 8 ); } // -------------------------------------------------------------------- // Planar order classes // -------------------------------------------------------------------- template class PlanarPtr { protected: AlignedBuffer ab; public: T** ptrs; PlanarPtr() : ptrs (NULL) {} bool resize(int newSize) { if (ab.resize(newSize)) { ptrs=ab.data; return true; } else { ptrs=NULL; return false; } } void swap (PlanarPtr &other) { ab.swap(other.ab); T** tmpsPtrs = other.ptrs; other.ptrs = ptrs; ptrs = tmpsPtrs; } T*& operator() (unsigned row) { return ptrs[row]; } // Will send back the start of a row, starting with a red, green or blue value T* operator() (unsigned row) const { return ptrs[row]; } // Will send back a value at a given row, col position T& operator() (unsigned row, unsigned col) { return ptrs[row][col]; } const T operator() (unsigned row, unsigned col) const { return ptrs[row][col]; } }; template class PlanarImageData : virtual public ImageDatas { private: AlignedBuffer abData; int rowstride; // Plan size, in bytes (all padding bytes included) int planestride; // Row length, in bytes (padding bytes included) public: T* data; PlanarPtr r; PlanarPtr g; PlanarPtr b; PlanarImageData() : rowstride(0), planestride(0), data (NULL) {} PlanarImageData(int w, int h) : rowstride(0), planestride(0), data (NULL) { allocate(w, h); } // Send back the row stride. WARNING: unit = byte, not element! int getRowStride () { return rowstride; } // Send back the plane stride. WARNING: unit = byte, not element! int getPlaneStride () { return planestride; } void swap(PlanarImageData &other) { abData.swap(other.abData); r.swap(other.r); g.swap(other.g); b.swap(other.b); T* tmpData = other.data; other.data = data; data = tmpData; int tmpWidth = other.width; other.width = width; width = tmpWidth; int tmpHeight = other.height; other.height = height; height = tmpHeight; } // use as pointer to data //operator void*() { return data; }; /* If any of the required allocation fails, "width" and "height" are set to -1, and all remaining buffer are freed * Can be safely used to reallocate an existing image */ void allocate (int W, int H) { if (W==width && H==height) return; width=W; height=H; if (sizeof(T) > 1) { // 128 bits memory alignment for >8bits data rowstride = ( width*sizeof(T)+15 )/16*16; planestride = rowstride * height; } else { // No memory alignment for 8bits data rowstride = width*sizeof(T); planestride = rowstride * height; } // find the padding length to ensure a 128 bits alignment for each row size_t size = rowstride * 3*height; if (size && abData.resize(size, 1) && r.resize(height) && g.resize(height) && b.resize(height) ) { data = abData.data; } else { // asking for a new size of 0 is safe and will free memory, if any! abData.resize(0); r.resize(0); g.resize(0); b.resize(0); width = height = -1; return; } char *redstart = (char*)(data); char *greenstart = (char*)(data) + planestride; char *bluestart = (char*)(data) + 2*planestride; for (int i=0; i *dest) { assert (dest!=NULL); // Make sure that the size is the same, reallocate if necessary dest->allocate(width, height); for (int i=0; ir(i), r(i), width*sizeof(T)); memcpy (dest->g(i), g(i), width*sizeof(T)); memcpy (dest->b(i), b(i), width*sizeof(T)); } } void rotate (int deg) { if (deg==90) { PlanarImageData rotatedImg(height, width); // New image, rotated for (int ny=0; ny rotatedImg(height, width); // New image, rotated for (int nx=0; nx32 && height>50; #pragma omp parallel for schedule(static) if(bigImage) #endif for (int i=0; i void resizeImgTo (int nw, int nh, TypeInterpolation interp, IC *imgPtr) { //printf("resizeImgTo: resizing %s image data (%d x %d) to %s (%d x %d)\n", getType(), width, height, imgPtr->getType(), imgPtr->width, imgPtr->height); if (interp == TI_Nearest) { for (int i=0; ir(i,j)); convertTo(g(ri,ci), imgPtr->g(i,j)); convertTo(b(ri,ci), imgPtr->b(i,j)); } } } else if (interp == TI_Bilinear) { for (int i=0; i=height) sy = height-1; float dy = float(i)*float(height)/float(nh) - float(sy); int ny = sy+1; if (ny>=height) ny = sy; for (int j=0; j=width) sx = width; float dx = float(j)*float(width)/float(nw) - float(sx); int nx = sx+1; if (nx>=width) nx = sx; convertTo(r(sy,sx)*(1.f-dx)*(1.f-dy) + r(sy,nx)*dx*(1.f-dy) + r(ny,sx)*(1.f-dx)*dy + r(ny,nx)*dx*dy, imgPtr->r(i,j)); convertTo(g(sy,sx)*(1.f-dx)*(1.f-dy) + g(sy,nx)*dx*(1.f-dy) + g(ny,sx)*(1.f-dx)*dy + g(ny,nx)*dx*dy, imgPtr->g(i,j)); convertTo(b(sy,sx)*(1.f-dx)*(1.f-dy) + b(sy,nx)*dx*(1.f-dy) + b(ny,sx)*(1.f-dx)*dy + b(ny,nx)*dx*dy, imgPtr->b(i,j)); } } } else { // This case should never occur! for (int i=0; i32 && height>50; #pragma omp parallel for schedule(static) if(bigImage) #endif for (int i=0; i32 && height>50; #pragma omp parallel for schedule(static) if(bigImage) #endif for (int i=0; i>histcompr); histogram.clear(); for (int i=0; i(r(i,j), r_); convertTo(g(i,j), g_); convertTo(b(i,j), b_); histogram[(int)Color::igamma_srgb (r_)>>histcompr]++; histogram[(int)Color::igamma_srgb (g_)>>histcompr]++; histogram[(int)Color::igamma_srgb (b_)>>histcompr]++; } } void computeHistogramAutoWB (double &avg_r, double &avg_g, double &avg_b, int &n, LUTu &histogram, const int compression) { histogram.clear(); avg_r = avg_g = avg_b = 0.; n=0; for (unsigned int i=0; i<(unsigned int)(height); i++) for (unsigned int j=0; j<(unsigned int)(width); j++) { float r_, g_, b_; convertTo(r(i,j), r_); convertTo(g(i,j), g_); convertTo(b(i,j), b_); int rtemp = Color::igamma_srgb (r_); int gtemp = Color::igamma_srgb (g_); int btemp = Color::igamma_srgb (b_); histogram[rtemp>>compression]++; histogram[gtemp>>compression]+=2; histogram[btemp>>compression]++; // autowb computation if (r_>64000.f || g_>64000.f || b_>64000.f) continue; avg_r += double(r_); avg_g += double(g_); avg_b += double(b_); n++; } } void getAutoWBMultipliers (double &rm, double &gm, double &bm) { double avg_r = 0.; double avg_g = 0.; double avg_b = 0.; int n = 0; //int p = 6; for (unsigned int i=0; i<(unsigned int)(height); i++) for (unsigned int j=0; j<(unsigned int)(width); j++) { float r_, g_, b_; convertTo(r(i,j), r_); convertTo(g(i,j), g_); convertTo(b(i,j), b_); if (r_>64000.f || g_>64000.f || b_>64000.f) continue; avg_r += double(r_); avg_g += double(g_); avg_b += double(b_); /*avg_r += intpow( (double)r(i, j), p); avg_g += intpow( (double)g(i, j), p); avg_b += intpow( (double)b(i, j), p);*/ n++; } rm = avg_r/double(n); gm = avg_g/double(n); bm = avg_b/double(n); } void transformPixel (int x, int y, int tran, int& tx, int& ty) { int W = width; int H = height; int sw = W, sh = H; if ((tran & TR_ROT) == TR_R90 || (tran & TR_ROT) == TR_R270) { sw = H; sh = W; } int ppx = x, ppy = y; if (tran & TR_HFLIP) ppx = sw - 1 - x; if (tran & TR_VFLIP) ppy = sh - 1 - y; tx = ppx; ty = ppy; if ((tran & TR_ROT) == TR_R180) { tx = W - 1 - ppx; ty = H - 1 - ppy; } else if ((tran & TR_ROT) == TR_R90) { tx = ppy; ty = H - 1 - ppx; } else if ((tran & TR_ROT) == TR_R270) { tx = W - 1 - ppy; ty = ppx; } } virtual void getSpotWBData (double &reds, double &greens, double &blues, int &rn, int &gn, int &bn, std::vector &red, std::vector &green, std::vector &blue, int tran) { int x; int y; reds = 0, greens = 0, blues = 0; rn = 0, gn = 0, bn = 0; for (size_t i=0; i=0 && y>=0 && x(this->r(y, x), v); reds += double(v); rn++; } transformPixel (green[i].x, green[i].y, tran, x, y); if (x>=0 && y>=0 && x(this->g(y, x), v); greens += double(v); gn++; } transformPixel (blue[i].x, blue[i].y, tran, x, y); if (x>=0 && y>=0 && x(this->b(y, x), v); blues += double(v); bn++; } } } void readData (FILE *f) { for (int i=0; i class ChunkyPtr { private: T* ptr; int width; public: ChunkyPtr() : ptr (NULL), width(-1) {} void init(T* base, int w=-1) { ptr = base; width=w; } void swap (ChunkyPtr &other) { T* tmpsPtr = other.ptr; other.ptr = ptr; ptr = tmpsPtr; int tmpWidth = other.width; other.width = width; width = tmpWidth; } // Will send back the start of a row, starting with a red, green or blue value T* operator() (unsigned row) const { return &ptr[3*(row*width)]; } // Will send back a value at a given row, col position T& operator() (unsigned row, unsigned col) { return ptr[3*(row*width+col)]; } const T operator() (unsigned row, unsigned col) const { return ptr[3*(row*width+col)]; } }; template class ChunkyImageData : virtual public ImageDatas { private: AlignedBuffer abData; public: T* data; ChunkyPtr r; ChunkyPtr g; ChunkyPtr b; ChunkyImageData() : data (NULL) {} ChunkyImageData(int w, int h) : data (NULL) { allocate(w, h); } /** Returns the pixel data, in r/g/b order from top left to bottom right continuously. * @return a pointer to the pixel data */ const T* getData () { return data; } void swap(ChunkyImageData &other) { abData.swap(other.abData); r.swap(other.r); g.swap(other.g); b.swap(other.b); T* tmpData = other.data; other.data = data; data = tmpData; int tmpWidth = other.width; other.width = width; width = tmpWidth; int tmpHeight = other.height; other.height = height; height = tmpHeight; } /* * If any of the required allocation fails, "width" and "height" are set to -1, and all remaining buffer are freed * Can be safely used to reallocate an existing image or to free up it's memory with "allocate (0,0);" */ void allocate (int W, int H) { if (W==width && H==height) return; width=W; height=H; if (abData.resize(width*height*3)) { data = abData.data; r.init(data, width); g.init(data+1, width); b.init(data+2, width); } else { data = NULL; r.init(NULL); g.init(NULL); b.init(NULL); width = height = -1; } } /** Copy the data to another ChunkyImageData */ void copyData(ChunkyImageData *dest) { assert (dest!=NULL); // Make sure that the size is the same, reallocate if necessary dest->allocate(width, height); for (int i=0; idata, data, 3*width*height*sizeof(T)); } } void rotate (int deg) { if (deg==90) { ChunkyImageData rotatedImg(height, width); // New image, rotated for (int ny=0; ny rotatedImg(height, width); // New image, rotated for (int nx=0; nx void resizeImgTo (int nw, int nh, TypeInterpolation interp, IC *imgPtr) { //printf("resizeImgTo: resizing %s image data (%d x %d) to %s (%d x %d)\n", getType(), width, height, imgPtr->getType(), imgPtr->width, imgPtr->height); if (interp == TI_Nearest) { for (int i=0; ir(i,j)); convertTo(g(ri,ci), imgPtr->g(i,j)); convertTo(b(ri,ci), imgPtr->b(i,j)); } } } else if (interp == TI_Bilinear) { for (int i=0; i=height) sy = height-1; float dy = float(i)*float(height)/float(nh) - float(sy); int ny = sy+1; if (ny>=height) ny = sy; for (int j=0; j=width) sx = width; float dx = float(j)*float(width)/float(nw) - float(sx); int nx = sx+1; if (nx>=width) nx = sx; T valR = r(sy,sx)*(1.f-dx)*(1.f-dy) + r(sy,nx)*dx*(1.f-dy) + r(ny,sx)*(1.f-dx)*dy + r(ny,nx)*dx*dy; T valG = g(sy,sx)*(1.f-dx)*(1.f-dy) + g(sy,nx)*dx*(1.f-dy) + g(ny,sx)*(1.f-dx)*dy + g(ny,nx)*dx*dy; T valB = b(sy,sx)*(1.f-dx)*(1.f-dy) + b(sy,nx)*dx*(1.f-dy) + b(ny,sx)*(1.f-dx)*dy + b(ny,nx)*dx*dy; convertTo(valR, imgPtr->r(i,j)); convertTo(valG, imgPtr->g(i,j)); convertTo(valB, imgPtr->b(i,j)); } } } else { // This case should never occur! for (int i=0; i lBuffer(3*width); T* lineBuffer = lBuffer.data; size_t size = 3*width*sizeof(T); for (int i=0; i>histcompr); histogram.clear(); for (int i=0; i(r(i,j), r_); convertTo(g(i,j), g_); convertTo(b(i,j), b_); histogram[(int)Color::igamma_srgb (r_)>>histcompr]++; histogram[(int)Color::igamma_srgb (g_)>>histcompr]++; histogram[(int)Color::igamma_srgb (b_)>>histcompr]++; } } void computeHistogramAutoWB (double &avg_r, double &avg_g, double &avg_b, int &n, LUTu &histogram, const int compression) { histogram.clear(); avg_r = avg_g = avg_b = 0.; n=0; for (unsigned int i=0; i<(unsigned int)(height); i++) for (unsigned int j=0; j<(unsigned int)(width); j++) { float r_, g_, b_; convertTo(r(i,j), r_); convertTo(g(i,j), g_); convertTo(b(i,j), b_); int rtemp = Color::igamma_srgb (r_); int gtemp = Color::igamma_srgb (g_); int btemp = Color::igamma_srgb (b_); histogram[rtemp>>compression]++; histogram[gtemp>>compression]+=2; histogram[btemp>>compression]++; // autowb computation if (r_>64000.f || g_>64000.f || b_>64000.f) continue; avg_r += double(r_); avg_g += double(g_); avg_b += double(b_); n++; } } void getAutoWBMultipliers (double &rm, double &gm, double &bm) { double avg_r = 0.; double avg_g = 0.; double avg_b = 0.; int n = 0; //int p = 6; for (unsigned int i=0; i<(unsigned int)(height); i++) for (unsigned int j=0; j<(unsigned int)(width); j++) { float r_, g_, b_; convertTo(r(i,j), r_); convertTo(g(i,j), g_); convertTo(b(i,j), b_); if (r_>64000.f || g_>64000.f || b_>64000.f) continue; avg_r += double(r_); avg_g += double(g_); avg_b += double(b_); /*avg_r += intpow( (double)r(i, j), p); avg_g += intpow( (double)g(i, j), p); avg_b += intpow( (double)b(i, j), p);*/ n++; } rm = avg_r/double(n); gm = avg_g/double(n); bm = avg_b/double(n); } void transformPixel (int x, int y, int tran, int& tx, int& ty) { int W = width; int H = height; int sw = W, sh = H; if ((tran & TR_ROT) == TR_R90 || (tran & TR_ROT) == TR_R270) { sw = H; sh = W; } int ppx = x, ppy = y; if (tran & TR_HFLIP) ppx = sw - 1 - x; if (tran & TR_VFLIP) ppy = sh - 1 - y; tx = ppx; ty = ppy; if ((tran & TR_ROT) == TR_R180) { tx = W - 1 - ppx; ty = H - 1 - ppy; } else if ((tran & TR_ROT) == TR_R90) { tx = ppy; ty = H - 1 - ppx; } else if ((tran & TR_ROT) == TR_R270) { tx = W - 1 - ppy; ty = ppx; } } virtual void getSpotWBData (double &reds, double &greens, double &blues, int &rn, int &gn, int &bn, std::vector &red, std::vector &green, std::vector &blue, int tran) { int x; int y; reds = 0, greens = 0, blues = 0; rn = 0, gn = 0, bn = 0; for (size_t i=0; i=0 && y>=0 && x(this->r(y, x), v); reds += double(v); rn++; } transformPixel (green[i].x, green[i].y, tran, x, y); if (x>=0 && y>=0 && x(this->g(y, x), v); greens += double(v); gn++; } transformPixel (blue[i].x, blue[i].y, tran, x, y); if (x>=0 && y>=0 && x(this->b(y, x), v); blues += double(v); bn++; } } } void readData (FILE *f) { for (int i=0; i { public: virtual ~IImagefloat() {} }; /** This class represents an image having a classical 8 bits/pixel representation */ class IImage8 : public IImage, public ChunkyImageData { public: virtual ~IImage8() {} }; /** This class represents an image having a 16 bits/pixel planar representation. The planes are stored as two dimensional arrays. All the rows have a 128 bits alignment. */ class IImage16 : public IImage, public PlanarImageData { public: virtual ~IImage16() {} }; } #endif