#ifndef LAZY_IMAGE_C #define LAZY_IMAGE_C #include #include #include #include #include struct yuv { // values in [0,1] double y; // brightness double u; // red/green part double v; // green/blue part }; class LazyImage { private: QImage* img; //Not managed by us, just used. QImage* histogramm_normal_image; QImage* histogramm_cumulative_image; double histogramm_relative_intensity[256]; double histogramm_relative_cumulative_intensity[256]; int histogramm_absolute_intensity[256]; int histogramm_absolute_cumulative_intensity[256]; int intensity_average; int intensity_variance; void gatherHistogrammData(void) { // Zero existing histogramm data first for(int i=0; i<256; i++) { histogramm_relative_intensity[i] = 0; histogramm_absolute_intensity[i] = 0; histogramm_absolute_cumulative_intensity[i] = 0; } // Count all the brightness values for(int x=0; ximg->width(); x++) { for(int y=0; yimg->height(); y++) { int r,g,b; QColor color = QColor::fromRgb(this->img->pixel(x, y)); color.getRgb(&r,&g,&b); int intensity = this->calcIntensity(r, g, b); histogramm_absolute_intensity[intensity] += 1; } } // Calculate relative histogramm and absolute cumulative histogramm int pixels = this->img->width()*this->img->height(); int sum = 0; double relative_sum = 0.0; for(int i=0; i<256; i++) { histogramm_relative_intensity[i] = (((double) histogramm_absolute_intensity[i])/((double) pixels)); sum += histogramm_absolute_intensity[i]; relative_sum += histogramm_relative_intensity[i]; histogramm_absolute_cumulative_intensity[i] = sum; histogramm_relative_cumulative_intensity[i] = relative_sum; } }; void calcIntensityAverage(void) { double sum = 0; for(int i=0; i<256;i++) { sum += (i*histogramm_relative_intensity[i]); } this->intensity_average = qRound(sum); }; void calcIntensityVariance(void) { int intensity_average = this->intensity_average; double sum = 0; for(int i=0; i<256; i++) { sum += histogramm_relative_intensity[i] * pow((i - intensity_average),2); } this->intensity_variance = qRound(sum); }; void generateNormalHistogrammImage(void) { if(this->histogramm_normal_image != NULL) { delete this->histogramm_normal_image; this->histogramm_normal_image = NULL; } //Find biggest value in histogramm data double max = 0; for(int i=0; i<256; i++) { if(histogramm_relative_intensity[i] > max) max = histogramm_relative_intensity[i]; } this->histogramm_normal_image = new QImage(256, 100, QImage::Format_RGB32); this->histogramm_normal_image->fill(QColor::fromRgb(200,200,200)); int black = QColor::fromRgb(0,0,0).rgb(); for(int x=0; x<256; x++) { int k_max = (int) qRound((100*histogramm_relative_intensity[x])/max); for(int y=0; yhistogramm_normal_image->setPixel(x, (100-y)-1, black); } } }; void generateCumulativeHistogrammImage(void) { if(this->histogramm_cumulative_image != NULL) { delete this->histogramm_cumulative_image; this->histogramm_cumulative_image = NULL; } this->histogramm_cumulative_image = new QImage(256, 100, QImage::Format_RGB32); this->histogramm_cumulative_image->fill(QColor::fromRgb(200,200,200)); int black = QColor::fromRgb(0,0,0).rgb(); double total = 0.0; for(int x=0; x<256; x++) { total += histogramm_relative_intensity[x]; int k_max = (int) qRound(100*total); for(int y=0; yhistogramm_cumulative_image->setPixel(x, (100-y)-1, black); } } }; public: enum overflowMode {DEFAULT, ZERO_PADDING, CONSTANT, MIRRORED, CONTINUOUS}; LazyImage(QImage* img) { this->histogramm_normal_image = NULL; this->histogramm_cumulative_image = NULL; this->setImage(img); }; ~LazyImage() { if(img != NULL) { img = NULL; } }; void setImage(QImage* img) { if(img != NULL) { this->img = img; this->updateStatistics(); } }; QImage* getImage(void) { return this->img; }; void updateStatistics(void) { this->gatherHistogrammData(); this->calcIntensityAverage(); this->calcIntensityVariance(); this->generateNormalHistogrammImage(); this->generateCumulativeHistogrammImage(); }; QImage* getHistogrammNormalImage(void) { return this->histogramm_normal_image; }; QImage* getHistogrammCumulativeImage(void) { return this->histogramm_cumulative_image; }; /** * Uses simple weights to calculate intensity of a pixel. * Using qRound() to reduce the error of the int cast. * * @brief LazyImage::calcIntensity */ int calcIntensity(int r, int g, int b) { return (int) qRound(0.299*r + 0.587*g + 0.114*b); }; int getIntensityAverage(void) { return this->intensity_average; } int getIntensityVariance(void) { return this->intensity_variance; }; int* getAbsoluteIntensityHistogramm(void) { return this->histogramm_absolute_intensity; }; int* getAbsoluteCumulativeIntensityHistogramm(void) { return this->histogramm_absolute_cumulative_intensity; }; double* getRelativeIntensityHistogramm(void) { return this->histogramm_relative_intensity; }; double* getRelativeCumulativeIntensityHistogramm(void) { return this->histogramm_relative_cumulative_intensity; }; void convertToMonochrome(void) { for(int x=0; ximg->width(); x++) { for(int y=0; yimg->height(); y++) { int r,g,b; QColor color = QColor::fromRgb(this->img->pixel(x, y)); color.getRgb(&r,&g,&b); int intensity = this->calcIntensity(r, g, b); color = QColor::fromRgb(intensity, intensity, intensity); this->img->setPixel(x, y, color.rgb()); } } }; /** * Takes coordinates and a mode about how to handle overflows. * Not all modes are 100% bullet proof - it is still possible to * get unexpected results out of this for very big overflows. * * @brief LazyImage::getPixel */ QRgb getPixel(int x, int y, overflowMode mode) { int width = this->img->width(); int height = this->img->height(); bool return_result = false; QRgb result; switch(mode) { case DEFAULT: break; case ZERO_PADDING: // Return black for all out of bound requests if(x < 0 || x >= width || y < 0 || y >= height) { result = qRgb(0, 0, 0); return_result = true; } break; case CONSTANT: // Simply clamp to the border it is stuck on if(x < 0) x = 0; else if(x >= width) x = width - 1; if(y < 0) y = 0; else if(y >= height) y = height - 1; break; case MIRRORED: // Mirror on overflowed axis if(x < 0) x *= -1; else if(x >= width) { int delta = x - (width-1); x = (width-1) - delta; } if(y < 0) y *= -1; else if(y > (height-1)) { int delta = y - (height-1); y = (height-1) - delta; } break; case CONTINUOUS: // simply start over at the other side again x = std::abs(x % width); y = std::abs(y % height); break; default: std::cout << "HELP, SOMETHING WENT WRONG! I DON'T KNOW THIS MODE!" << std::endl; break; // BOOM! } if(return_result) { return result; } else { return this->img->pixel(x, y); } }; static struct yuv RGBToYUV(QColor input) { struct yuv result; int input_r, input_g, input_b; input.getRgb(&input_r, &input_g, &input_b); //std::cout << input_r << "," << input_g << "," << input_b << " (rgb) -> (yuv) "; // normalize input rgb to values in [0,1] double r, g, b; r = input_r / 255.0; g = input_g / 255.0; b = input_b / 255.0; // calculate yuv result.y = 0.299 * r + 0.587 * g + 0.114 * b; result.u = (b - result.y) * 0.493; result.v = (r - result.y) * 0.877; //std::cout << result.y << "," << result.u << "," << result.v << std::endl; return result; }; static QColor YUVToRGB(struct yuv input) { double r, g, b; // calculate rgb //std::cout << input.y << "," << input.u << "," << input.v << " (yuv) -> (rgb) "; b = (input.u / 0.493) + input.y; r = (input.v / 0.877) + input.y; // möp g = (input.y - 0.299*r - 0.144*b) / 0.587; g = (1.0/0.587)*input.y - (0.299/0.587)*r - (0.114/0.585)*b; // normalize into [0,255] int output_r, output_g, output_b; output_r = qRound(r * 255.0); output_g = qRound(g * 255.0); output_b = qRound(b * 255.0); if(output_r < 0) output_r = 0; if(output_g < 0) output_g = 0; if(output_b < 0) output_b = 0; if(output_r > 255) output_r = 255; if(output_g > 255) output_g = 255; if(output_b > 255) output_b = 255; //std::cout << output_r << "," << output_g << "," << output_b << std::endl << std::endl; QColor result = QColor(output_r, output_g, output_b); return result; }; int width(void) { return this->img->width(); }; int height(void) { return this->img->height(); }; }; #endif