2022-10-28 09:31:13 +02:00
|
|
|
#include "common/texture.h"
|
|
|
|
#include <cmath>
|
|
|
|
#include <fstream>
|
|
|
|
#include <iostream>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
Texture::Texture(int width, int height) { this->resize(width, height); }
|
|
|
|
|
|
|
|
Texture::Texture(char const *fileName) { this->load(fileName); }
|
|
|
|
|
|
|
|
bool Texture::load(char const *fileName) {
|
2022-12-13 00:16:19 +01:00
|
|
|
CImg<unsigned char> img_char(fileName);
|
|
|
|
if (img_char.is_empty()) {
|
|
|
|
std::cerr << "(Image): Could not open file " << fileName << std::endl;
|
|
|
|
return false;
|
2022-10-28 09:31:13 +02:00
|
|
|
}
|
|
|
|
|
2022-12-13 00:16:19 +01:00
|
|
|
this->image_ = CImg<float>(img_char.width(), img_char.height(), img_char.depth(), 3, 0);
|
|
|
|
for (int c = 0; c < 3; c++)
|
|
|
|
cimg_forXYZ(img_char, x, y, z) {
|
|
|
|
this->image_(x, y, z, c) = img_char(x, y, z, std::min(c, img_char.spectrum() - 1)) / 255.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2022-10-28 09:31:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Texture::save(char const *fileName) const {
|
2022-12-13 00:16:19 +01:00
|
|
|
CImg<unsigned char> img_char;
|
|
|
|
img_char = 255 * this->image_;
|
2022-10-28 09:31:13 +02:00
|
|
|
|
2022-12-13 00:16:19 +01:00
|
|
|
img_char.save(fileName);
|
|
|
|
return true;
|
2022-10-28 09:31:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Color Texture::getPixelAt(int x, int y) const {
|
2022-12-13 00:16:19 +01:00
|
|
|
if ((x %= this->width()) < 0)
|
|
|
|
x += this->width();
|
|
|
|
if ((y %= this->height()) < 0)
|
|
|
|
y += this->height();
|
|
|
|
return Color(this->image_.atXY(x, y, 0), this->image_.atXY(x, y, 1), this->image_.atXY(x, y, 2));
|
2022-10-28 09:31:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Texture::setPixelAt(int x, int y, Color const &color) {
|
2022-12-13 00:16:19 +01:00
|
|
|
if ((x %= this->width()) < 0)
|
|
|
|
x += this->width();
|
|
|
|
if ((y %= this->height()) < 0)
|
|
|
|
y += this->height();
|
|
|
|
this->image_(x, y, 0) = color.r;
|
|
|
|
this->image_(x, y, 1) = color.g;
|
|
|
|
this->image_(x, y, 2) = color.b;
|
2022-10-28 09:31:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Color Texture::color(float u, float v, bool interpolate) const {
|
2022-12-25 09:26:13 +01:00
|
|
|
Color color;
|
|
|
|
if (!interpolate) {
|
|
|
|
color = this->getPixelAt(int(roundf(u * this->width())), int(roundf(v * this->height())));
|
|
|
|
} else {
|
|
|
|
// bilinear interpolation
|
|
|
|
// adjacent pixel coordinates
|
|
|
|
int left = int(floorf(u * this->width()));
|
|
|
|
int right = int(ceilf(u * this->width()));
|
|
|
|
int top = int(floorf(v * this->height()));
|
|
|
|
int bottom = int(ceilf(v * this->height()));
|
2022-12-14 03:51:11 +01:00
|
|
|
|
2022-12-25 09:26:13 +01:00
|
|
|
// weights
|
|
|
|
float w[4];
|
|
|
|
w[0] = right - u * this->width();
|
|
|
|
w[1] = 1 - w[0];
|
|
|
|
w[2] = bottom - v * this->height();
|
|
|
|
w[3] = 1 - w[2];
|
2022-12-14 03:51:11 +01:00
|
|
|
|
2022-12-25 09:26:13 +01:00
|
|
|
// get color values and interpolate
|
|
|
|
Color val[4];
|
|
|
|
val[0] = this->getPixelAt(left, top);
|
|
|
|
val[1] = this->getPixelAt(right, top);
|
|
|
|
val[2] = this->getPixelAt(left, bottom);
|
|
|
|
val[3] = this->getPixelAt(right, bottom);
|
|
|
|
color = w[2] * w[0] * val[0] + w[2] * w[1] * val[1] + w[3] * w[0] * val[2] + w[3] * w[1] * val[3];
|
|
|
|
}
|
|
|
|
return color;
|
2022-10-28 09:31:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Color Texture::color(Vector2d const &surfacePosition, bool interpolate) const {
|
2022-12-13 00:16:19 +01:00
|
|
|
return color(surfacePosition.u, surfacePosition.v, interpolate);
|
2022-10-28 09:31:13 +02:00
|
|
|
}
|
2023-01-24 13:32:19 +01:00
|
|
|
|
|
|
|
void Texture::gaussianBlur(int kernelSize, float sigma) {
|
|
|
|
CImg<float> kernel = computeGaussianKernel(kernelSize, sigma);
|
|
|
|
image_ = convolution(image_, kernel);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function to compute Gaussian kernel
|
|
|
|
CImg<float> Texture::computeGaussianKernel(int kernelSize, float sigma) {
|
|
|
|
// Create kernel
|
|
|
|
CImg<float> kernel(kernelSize, kernelSize, 1, 1);
|
|
|
|
|
|
|
|
// Compute Gaussian kernel
|
|
|
|
float sum = 0.0f;
|
|
|
|
int i, j;
|
|
|
|
for (i = 0; i < kernelSize; i++) {
|
|
|
|
for (j = 0; j < kernelSize; j++) {
|
|
|
|
kernel(i, j) = exp(-0.5f * (pow((i - kernelSize / 2.f) / sigma, 2.f) +
|
|
|
|
pow((j - kernelSize / 2.f) / sigma, 2.f))) / (2 * M_PI * sigma * sigma);
|
|
|
|
sum += kernel(i, j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Normalize kernel
|
|
|
|
kernel /= sum;
|
|
|
|
|
|
|
|
return kernel;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function to perform convolution
|
|
|
|
CImg<float> Texture::convolution(CImg<float> &img, CImg<float> &kernel) {
|
|
|
|
int kernelSize = kernel.width();
|
|
|
|
int imgRows = img.height();
|
|
|
|
int imgCols = img.width();
|
|
|
|
CImg<float> result(imgCols, imgRows, 1, 3);
|
|
|
|
float sum;
|
|
|
|
int i, j, m, n;
|
|
|
|
int kernelRadius = kernelSize / 2;
|
|
|
|
|
|
|
|
// Perform convolution
|
|
|
|
cimg_forXYC(img, i, j, c) {
|
|
|
|
sum = 0;
|
|
|
|
cimg_forY(kernel, m) {
|
|
|
|
cimg_forX(kernel, n) {
|
|
|
|
int x = i + n - kernelRadius;
|
|
|
|
int y = j + m - kernelRadius;
|
|
|
|
if(x >= 0 && x < imgCols && y >= 0 && y < imgRows){
|
|
|
|
sum += img(x, y, 0, c) * kernel(n, m);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result(i, j, 0, c) = sum;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Texture::scaleBrightness(float scale) {
|
|
|
|
image_ *= scale;
|
|
|
|
}
|