316 lines
9.3 KiB
C++
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
|