implement image gaussian blur with arbitrary number of passes

This commit is contained in:
sshumakov3
2021-01-14 13:04:19 +03:00
parent 5cabf8fa1e
commit 7a42854a14
6 changed files with 329 additions and 1 deletions

View File

@@ -74,6 +74,7 @@
#include <noggit/Red/NodeEditor/Nodes/Data/Image/ImageInvertNode.hpp>
#include <noggit/Red/NodeEditor/Nodes/Data/Image/ImageMirrorNode.hpp>
#include <noggit/Red/NodeEditor/Nodes/Data/Image/ImageResizeNode.hpp>
#include <noggit/Red/NodeEditor/Nodes/Data/Image/ImageGaussianBlurNode.hpp>
#include <noggit/Red/NodeEditor/Nodes/Data/Noise/NoisePerlinNode.hpp>
#include <noggit/Red/NodeEditor/Nodes/Data/Noise/NoiseToImageNode.hpp>
@@ -181,6 +182,7 @@ namespace noggit
ret->registerModel<ImageSetPixelNode>("Data//Image");
ret->registerModel<ImageToGrayscaleNode>("Data//Image");
ret->registerModel<ImageMirrorNode>("Data//Image");
ret->registerModel<ImageGaussianBlurNode>("Data//Image");
// Noise
ret->registerModel<NoisePerlinNode>("Data//Noise//Generators");

View File

@@ -37,7 +37,6 @@ void ImageFillNode::compute()
NodeValidationState ImageFillNode::validate()
{
if (!static_cast<ImageData*>(_in_ports[1].in_value.lock().get()))
{
setValidationState(NodeValidationState::Error);

View File

@@ -0,0 +1,80 @@
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#include "ImageGaussianBlurNode.hpp"
#include "gaussianblur.h"
#include <noggit/Red/NodeEditor/Nodes/BaseNode.inl>
#include <noggit/Red/NodeEditor/Nodes/DataTypes/GenericData.hpp>
using namespace noggit::Red::NodeEditor::Nodes;
ImageGaussianBlurNode::ImageGaussianBlurNode()
: LogicNodeBase()
{
setName("ImageGaussianBlurNode");
setCaption("Image Gaussian Blur");
_validation_state = NodeValidationState::Valid;
addPortDefault<LogicData>(PortType::In, "Logic", true);
addPortDefault<ImageData>(PortType::In, "Image", true);
addPortDefault<DecimalData>(PortType::In, "Sigma<Decimal>", true);
addPortDefault<UnsignedIntegerData>(PortType::In, "Passes<UInteger>", true);
auto passes = static_cast<QSpinBox*>(_in_ports[3].default_widget);
passes->setValue(1);
passes->setMinimum(1);
addPort<LogicData>(PortType::Out, "Logic", true);
addPort<ImageData>(PortType::Out, "Image", true);
}
void ImageGaussianBlurNode::compute()
{
QImage* image = static_cast<ImageData*>(_in_ports[1].in_value.lock().get())->value_ptr();
QImage new_image = *image;
for (int i = 0; i < defaultPortData<UnsignedIntegerData>(PortType::In, 3)->value(); ++i)
{
GaussianBlur blur(5.0,
std::max(defaultPortData<DecimalData>(PortType::In, 2)->value(), 1.0));
new_image = blur.ApplyGaussianFilterToImage(new_image);
}
_out_ports[0].out_value = std::make_shared<LogicData>(true);
Q_EMIT dataUpdated(0);
_out_ports[1].out_value = std::make_shared<ImageData>(std::move(new_image));
Q_EMIT dataUpdated(1);
}
NodeValidationState ImageGaussianBlurNode::validate()
{
if (!static_cast<ImageData*>(_in_ports[1].in_value.lock().get()))
{
setValidationState(NodeValidationState::Error);
setValidationMessage("Error: failed to evaluate image input.");
return _validation_state;
}
return LogicNodeBase::validate();
}
QJsonObject ImageGaussianBlurNode::save() const
{
QJsonObject json_obj = BaseNode::save();
defaultWidgetToJson(PortType::In, 2, json_obj, "sigma");
return json_obj;
}
void ImageGaussianBlurNode::restore(const QJsonObject& json_obj)
{
BaseNode::restore(json_obj);
defaultWidgetFromJson(PortType::In, 2, json_obj, "sigma");
}

View File

@@ -0,0 +1,38 @@
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#ifndef NOGGIT_IMAGEGAUSSIANBLURNODE_HPP
#define NOGGIT_IMAGEGAUSSIANBLURNODE_HPP
#include <noggit/Red/NodeEditor/Nodes/LogicNodeBase.hpp>
using QtNodes::PortType;
using QtNodes::PortIndex;
using QtNodes::NodeData;
using QtNodes::NodeDataType;
using QtNodes::NodeDataModel;
using QtNodes::NodeValidationState;
namespace noggit
{
namespace Red::NodeEditor::Nodes
{
class ImageGaussianBlurNode : public LogicNodeBase
{
Q_OBJECT
public:
ImageGaussianBlurNode();
void compute() override;
NodeValidationState validate() override;
QJsonObject save() const override;
void restore(QJsonObject const& json_obj) override;
};
}
}
#endif //NOGGIT_IMAGEGAUSSIANBLURNODE_HPP

View File

@@ -0,0 +1,172 @@
#include "gaussianblur.h"
double GaussianBlur::GetNumberOnNormalDistribution(int i, int j, const int center, double sigma) const
{
return (1.0 / (2 * M_PI * pow(sigma, 2)) * exp ( - (pow(i - center, 2) + pow(j - center, 2)) / (2 * pow(sigma, 2))));
}
void GaussianBlur::GetPixelMatrix(const QPoint &center, const QImage &image, QRgb** matrix)
{
QSize image_size = image.size();
for (int i = 0; i < size_; i++)
for (int j = 0; j < size_; j++)
matrix[i][j] = image.pixel(GetCoordinate(QPoint(i, j) + center, image_size));
}
QRgb GaussianBlur::GetNewPixelValue(QRgb **matrix)
{
QRgb result = 0;
double red = 0;
double blue = 0;
double green = 0;
for (int i = 0; i < size_; i++)
for (int j = 0; j < size_; j++) {
red += qRed(matrix[i][j]) * matrix_[i][j];
blue += qBlue(matrix[i][j]) * matrix_[i][j];
green += qGreen(matrix[i][j]) * matrix_[i][j];
}
result = qRgb(red, green, blue);
return result;
}
QPoint GaussianBlur::GetCoordinate(const QPoint &point, const QSize &image_size) {
QPoint result(point);
result.setX(result.x() - radius_);
result.setY(result.y() - radius_);
if (result.x() < 0)
result.setX(0);
if (result.y() < 0)
result.setY(0);
if (result.x() >= image_size.width())
result.setX(image_size.width() - 1);
if (result.y() >= image_size.height())
result.setY(image_size.height() - 1);
return result;
}
void GaussianBlur::NormilizeElementsBySum() {
double sum = GetSumElements();
for (int i = 0; i < size_; i++)
for (int j = 0; j < size_; j++)
matrix_[i][j] /= sum;
}
GaussianBlur::GaussianBlur(int radius, double sigma) :
radius_ (radius),
diviation_ (sigma),
values_is_set_ (false)
{
if (true == (radius_ % 2))
{
size_ = 2 * radius_ + 1;
matrix_ = new double* [size_];
for (int i = 0; i < size_; i++)
matrix_[i] = new double [size_];
for (int i = 0; i < size_; i++)
for (int j = 0; j < size_; j++)
matrix_[i][j] = GetNumberOnNormalDistribution(i, j, radius_, diviation_);
NormilizeElementsBySum();
values_is_set_ = true;
}
}
GaussianBlur::GaussianBlur(const GaussianBlur &original):
radius_(original.radius_),
diviation_(original.diviation_)
{
size_ = 2 * radius_ + 1;
matrix_ = new double* [size_];
for (int i = 0; i < size_; i++)
matrix_[i] = new double [size_];
for (int i = 0; i < size_; i++)
for (int j = 0; j < size_; j++)
matrix_[i][j] = original.matrix_[i][j];
}
GaussianBlur::~GaussianBlur()
{
for (int i = 0; i < size_; i++)
delete[] matrix_[i];
delete[] matrix_;
}
double GaussianBlur::GetSumElements() const
{
double result = 0;
for (int i = 0; i < size_; i++)
for (int j = 0; j < size_; j++)
result += matrix_[i][j];
return result;
}
QImage GaussianBlur::ApplyGaussianFilterToImage(const QImage& input)
{
if (true == values_is_set_)
{
QImage output(input);
QRgb **temp = new QRgb* [size_];
for (int i = 0; i < size_; i++)
temp[i] = new QRgb [size_];
for (int i = 0; i < output.width(); i++)
for (int j = 0; j < output.height(); j++)
{
GetPixelMatrix(QPoint(i, j), output, temp);
output.setPixel(QPoint(i,j), GetNewPixelValue(temp));
}
for (int i = 0; i < size_; i++)
delete[] temp[i];
delete[] temp;
return output;
} else
return input;
}
const GaussianBlur& GaussianBlur::operator = (const GaussianBlur &blur)
{
if (this != &blur)
{
for (int i = 0; i < size_; i ++)
delete[] matrix_[i];
delete[] matrix_;
radius_ = blur.radius_;
diviation_ = blur.diviation_;
size_ = blur.size_;
matrix_ = new double* [size_];
for (int i = 0; i < size_; i++)
matrix_[i] = new double [size_];
for (int i = 0; i < size_; i++)
for (int j = 0; j < size_; j++)
matrix_[i][j] = blur.matrix_[i][j];
}
return *this;
}
QTextStream &operator << (QTextStream &stream, const GaussianBlur &blur) {
stream.setRealNumberPrecision(6);
for (int i = 0; i < blur.size_; i++) {
for (int j = 0; j < blur.size_; j++)
stream << blur.matrix_[i][j] << " ";
stream << endl;
}
return stream;
}

View File

@@ -0,0 +1,37 @@
#ifndef GAUSSIANBLUR_H
#define GAUSSIANBLUR_H
#include <QTextStream>
#include <QImage>
#include <qmath.h>
class GaussianBlur
{
private:
int radius_;
int size_;
double diviation_;
bool values_is_set_;
double **matrix_;
private:
double GetNumberOnNormalDistribution(int x, int y, const int center, double sigma) const;
void GetPixelMatrix(const QPoint &center, const QImage &image, QRgb **matrix);
QRgb GetNewPixelValue(QRgb** matrix);
QPoint GetCoordinate(const QPoint &point, const QSize &image_size);
void NormilizeElementsBySum();
public:
explicit GaussianBlur(int radius = 1, double sigma = 1.0);
GaussianBlur(const GaussianBlur &original);
~GaussianBlur();
double GetSumElements() const;
QImage ApplyGaussianFilterToImage(const QImage& input);
const GaussianBlur &operator = (const GaussianBlur &blur);
friend QTextStream &operator << (QTextStream &stream, const GaussianBlur &blur);
};
#endif // GAUSSIANBLUR_H