CG2_Tasks/lazy_image.cpp

316 lines
9.3 KiB
C++

#ifndef LAZY_IMAGE_C
#define LAZY_IMAGE_C
#include <QtGlobal>
#include <QMainWindow>
#include <QColor>
#include <iostream>
#include <math.h>
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; x<this->img->width(); x++) {
for(int y=0; y<this->img->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; y<k_max; y++) {
this->histogramm_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; y<k_max; y++) {
this->histogramm_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; x<this->img->width(); x++) {
for(int y=0; y<this->img->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