Add Chromium-only Blender WebEngine parity work
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
/* SPDX-FileCopyrightText: 2008-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to perform gaussian filtering operations on an image
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "GaussianFilter.h"
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
GaussianFilter::GaussianFilter(float iSigma)
|
||||
{
|
||||
_sigma = iSigma;
|
||||
_mask = nullptr;
|
||||
computeMask();
|
||||
}
|
||||
|
||||
GaussianFilter::GaussianFilter(const GaussianFilter &iBrother)
|
||||
{
|
||||
_sigma = iBrother._sigma;
|
||||
_maskSize = iBrother._maskSize;
|
||||
_bound = iBrother._bound;
|
||||
_storedMaskSize = iBrother._storedMaskSize;
|
||||
_mask = new float[_maskSize * _maskSize];
|
||||
memcpy(_mask, iBrother._mask, _maskSize * _maskSize * sizeof(float));
|
||||
}
|
||||
|
||||
GaussianFilter &GaussianFilter::operator=(const GaussianFilter &iBrother)
|
||||
{
|
||||
_sigma = iBrother._sigma;
|
||||
_maskSize = iBrother._maskSize;
|
||||
_bound = iBrother._bound;
|
||||
_storedMaskSize = iBrother._storedMaskSize;
|
||||
_mask = new float[_storedMaskSize * _storedMaskSize];
|
||||
memcpy(_mask, iBrother._mask, _storedMaskSize * _storedMaskSize * sizeof(float));
|
||||
return *this;
|
||||
}
|
||||
|
||||
GaussianFilter::~GaussianFilter()
|
||||
{
|
||||
delete[] _mask;
|
||||
}
|
||||
|
||||
int GaussianFilter::computeMaskSize(float sigma)
|
||||
{
|
||||
int maskSize = int(floor(4 * sigma)) + 1;
|
||||
if (0 == (maskSize % 2)) {
|
||||
++maskSize;
|
||||
}
|
||||
|
||||
return maskSize;
|
||||
}
|
||||
|
||||
void GaussianFilter::setSigma(float sigma)
|
||||
{
|
||||
_sigma = sigma;
|
||||
computeMask();
|
||||
}
|
||||
|
||||
void GaussianFilter::computeMask()
|
||||
{
|
||||
delete[] _mask;
|
||||
|
||||
_maskSize = computeMaskSize(_sigma);
|
||||
_storedMaskSize = (_maskSize + 1) >> 1;
|
||||
_bound = _storedMaskSize - 1;
|
||||
|
||||
float norm = _sigma * _sigma * 2.0f * M_PI;
|
||||
float invNorm = 1.0f / norm;
|
||||
_mask = new float[_storedMaskSize * _storedMaskSize * sizeof(float)];
|
||||
for (int i = 0; i < _storedMaskSize; ++i) {
|
||||
for (int j = 0; j < _storedMaskSize; ++j) {
|
||||
#if 0
|
||||
_mask[i * _storedMaskSize + j] = exp(-(i * i + j * j) / (2.0 * _sigma * _sigma));
|
||||
#else
|
||||
_mask[i * _storedMaskSize + j] = invNorm * exp(-(i * i + j * j) / (2.0 * _sigma * _sigma));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,132 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to perform gaussian filtering operations on an image
|
||||
*/
|
||||
|
||||
#include <cstdlib> // for abs
|
||||
#include <string.h> // for memcpy
|
||||
|
||||
#include "../system/FreestyleConfig.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
class GaussianFilter {
|
||||
protected:
|
||||
/* The mask is a symmetrical 2d array (with respect to the middle point).
|
||||
* Thus: `M(i,j) = M(-i,j) = M(i,-j) = M(-i,-j)`.
|
||||
* For this reason, to represent a NxN array (N odd),
|
||||
* we only store a `((N+1)/2)x((N+1)/2)` array. */
|
||||
|
||||
/** The sigma value of the gaussian function. */
|
||||
float _sigma;
|
||||
float *_mask;
|
||||
int _bound;
|
||||
/* The real mask size (must be odd), the size of the mask we store is:
|
||||
* `((_maskSize+1)/2)*((_maskSize+1)/2))`. */
|
||||
int _maskSize;
|
||||
int _storedMaskSize; // (_maskSize+1)/2)
|
||||
|
||||
public:
|
||||
GaussianFilter(float iSigma = 1.0f);
|
||||
GaussianFilter(const GaussianFilter &);
|
||||
GaussianFilter &operator=(const GaussianFilter &);
|
||||
virtual ~GaussianFilter();
|
||||
|
||||
/** Returns the value for pixel x,y of image "map" after a gaussian blur, made using the sigma
|
||||
* value. The sigma value determines the mask size (~ 2 x sigma).
|
||||
* \param map: The image we wish to work on.
|
||||
* The Map template must implement the following methods:
|
||||
* - `float pixel(uint x, uint y) const;`
|
||||
* - `uint width() const;`
|
||||
* - `uint height() const;`
|
||||
* \param x: The abscissa of the pixel where we want to evaluate the gaussian blur.
|
||||
* \param y: The ordinate of the pixel where we want to evaluate the gaussian blur.
|
||||
*/
|
||||
template<class Map> float getSmoothedPixel(Map *map, int x, int y);
|
||||
|
||||
/** Compute the mask size and returns the REAL mask size ((2*_maskSize)-1)
|
||||
* This method is provided for convenience.
|
||||
*/
|
||||
static int computeMaskSize(float sigma);
|
||||
|
||||
/** accessors */
|
||||
inline float sigma() const
|
||||
{
|
||||
return _sigma;
|
||||
}
|
||||
|
||||
inline int maskSize() const
|
||||
{
|
||||
return _maskSize;
|
||||
}
|
||||
|
||||
inline int getBound()
|
||||
{
|
||||
return _bound;
|
||||
}
|
||||
|
||||
/** modifiers */
|
||||
void setSigma(float sigma);
|
||||
#if 0
|
||||
void SetMaskSize(int size)
|
||||
{
|
||||
_maskSize = size;
|
||||
_storedMaskSize = (_maskSize + 1) >> 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void computeMask();
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:GaussianFilter")
|
||||
};
|
||||
|
||||
/*
|
||||
* #############################################
|
||||
* #############################################
|
||||
* #############################################
|
||||
* ###### ######
|
||||
* ###### I M P L E M E N T A T I O N ######
|
||||
* ###### ######
|
||||
* #############################################
|
||||
* #############################################
|
||||
* #############################################
|
||||
*/
|
||||
|
||||
template<class Map> float GaussianFilter::getSmoothedPixel(Map *map, int x, int y)
|
||||
{
|
||||
// float sum = 0.0f;
|
||||
float L = 0.0f;
|
||||
int w = int(map->width()); // soc
|
||||
int h = int(map->height()); // soc
|
||||
|
||||
// Current pixel is x,y
|
||||
// Sum surrounding pixels L value:
|
||||
for (int i = -_bound; i <= _bound; ++i) {
|
||||
if ((y + i < 0) || (y + i >= h)) {
|
||||
continue;
|
||||
}
|
||||
for (int j = -_bound; j <= _bound; ++j) {
|
||||
if ((x + j < 0) || (x + j >= w)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float tmpL = map->pixel(x + j, y + i);
|
||||
float m = _mask[abs(i) * _storedMaskSize + abs(j)];
|
||||
L += m * tmpL;
|
||||
// sum += m;
|
||||
}
|
||||
}
|
||||
// L /= sum;
|
||||
return L;
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
401
blender-5.2.0/source/blender/freestyle/intern/image/Image.h
Normal file
401
blender-5.2.0/source/blender/freestyle/intern/image/Image.h
Normal file
@@ -0,0 +1,401 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to encapsulate an array of RGB or Gray level values
|
||||
*/
|
||||
|
||||
#include <string.h> // for memcpy
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
//
|
||||
// Image base class, for all types of images
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** This class allows the storing of part of an image, while allowing a normal access to its pixel
|
||||
* values. You can for example only a rectangle of sw*sh, whose lower-left corner is at (ox, oy),
|
||||
* of an image of size w*h, and access these pixels using x,y coordinates specified in the whole
|
||||
* image coordinate system.
|
||||
*/
|
||||
class FrsImage {
|
||||
public:
|
||||
/** Default constructor */
|
||||
FrsImage()
|
||||
{
|
||||
_storedWidth = 0;
|
||||
_storedHeight = 0;
|
||||
_width = 0;
|
||||
_height = 0;
|
||||
_Ox = 0;
|
||||
_Oy = 0;
|
||||
}
|
||||
|
||||
/** Copy constructor */
|
||||
FrsImage(const FrsImage &brother)
|
||||
{
|
||||
_storedWidth = brother._storedWidth;
|
||||
_storedHeight = brother._storedHeight;
|
||||
_width = brother._width;
|
||||
_height = brother._height;
|
||||
_Ox = brother._Ox;
|
||||
_Oy = brother._Oy;
|
||||
}
|
||||
|
||||
/** Builds an FrsImage from its width and height.
|
||||
* The memory is allocated consequently.
|
||||
*/
|
||||
FrsImage(uint w, uint h)
|
||||
{
|
||||
_width = w;
|
||||
_height = h;
|
||||
_storedWidth = w;
|
||||
_storedHeight = h;
|
||||
_Ox = 0;
|
||||
_Oy = 0;
|
||||
}
|
||||
|
||||
/** Builds a partial-storing image.
|
||||
* \param w:
|
||||
* The width of the complete image
|
||||
* \param h:
|
||||
* The height of the complete image
|
||||
* \param sw:
|
||||
* The width of the rectangle that will actually be stored.
|
||||
* \param sh:
|
||||
* The height of the rectangle that will actually be stored.
|
||||
* \param ox:
|
||||
* The x-abscissa of the origin of the rectangle that will actually be stored.
|
||||
* \param oy:
|
||||
* The x-abscissa of the origin of the rectangle that will actually be stored.
|
||||
*/
|
||||
FrsImage(uint w, uint h, uint sw, uint sh, uint ox, uint oy)
|
||||
{
|
||||
_width = w;
|
||||
_height = h;
|
||||
_storedWidth = sw;
|
||||
_storedHeight = sh;
|
||||
_Ox = ox;
|
||||
_Oy = oy;
|
||||
}
|
||||
|
||||
/** Operator= */
|
||||
FrsImage &operator=(const FrsImage &brother)
|
||||
{
|
||||
_width = brother._width;
|
||||
_height = brother._height;
|
||||
_storedWidth = brother._storedWidth;
|
||||
_storedHeight = brother._storedHeight;
|
||||
_Ox = brother._Ox;
|
||||
_Oy = brother._Oy;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Destructor */
|
||||
virtual ~FrsImage() {}
|
||||
|
||||
/** Returns the width of the complete image */
|
||||
inline uint width() const
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
/** Returns the height of the complete image */
|
||||
inline uint height() const
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
/** Returns the gray value for pixel x,y */
|
||||
virtual float pixel(uint x, uint y) const = 0;
|
||||
|
||||
/** Sets the array.
|
||||
* \param array:
|
||||
* The array containing the values we wish to store.
|
||||
* Its size is sw*sh.
|
||||
* \param width:
|
||||
* The width of the complete image
|
||||
* \param height:
|
||||
* The height of the complete image
|
||||
* \param sw:
|
||||
* The width of the rectangle that will actually be stored.
|
||||
* \param sh:
|
||||
* The height of the rectangle that will actually be stored.
|
||||
* \param x:
|
||||
* The x-abscissa of the origin of the rectangle that will actually be stored.
|
||||
* \param y:
|
||||
* The x-abscissa of the origin of the rectangle that will actually be stored.
|
||||
* \param copy:
|
||||
* If true, the array is copied, otherwise the pointer is copied
|
||||
*/
|
||||
virtual void setArray(float *array,
|
||||
uint width,
|
||||
uint height,
|
||||
uint sw,
|
||||
uint sh,
|
||||
uint x,
|
||||
uint y,
|
||||
bool copy = true) = 0;
|
||||
|
||||
/** Returns the array containing the pixels values.
|
||||
* Its size is sw*sh, i.e. potentially a smaller rectangular part of the complete image.
|
||||
*/
|
||||
virtual float *getArray() = 0;
|
||||
|
||||
protected:
|
||||
uint _width;
|
||||
uint _height;
|
||||
uint _storedWidth;
|
||||
uint _storedHeight;
|
||||
uint _Ox; // origin of the stored part
|
||||
uint _Oy; // origin of the stored part
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:FrsImage")
|
||||
};
|
||||
|
||||
//
|
||||
// RGBImage
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
class RGBImage : public FrsImage {
|
||||
public:
|
||||
RGBImage() : FrsImage()
|
||||
{
|
||||
_rgb = 0;
|
||||
}
|
||||
|
||||
RGBImage(const RGBImage &brother) : FrsImage(brother)
|
||||
{
|
||||
_rgb = new float[3 * _storedWidth * _storedHeight];
|
||||
memcpy(_rgb, brother._rgb, 3 * _storedWidth * _storedHeight * sizeof(float));
|
||||
}
|
||||
|
||||
RGBImage(uint w, uint h) : FrsImage(w, h)
|
||||
{
|
||||
_rgb = new float[3 * _width * _height];
|
||||
}
|
||||
|
||||
RGBImage(float *rgb, uint w, uint h) : FrsImage(w, h)
|
||||
{
|
||||
_rgb = new float[3 * _width * _height];
|
||||
memcpy(_rgb, rgb, 3 * _width * _height * sizeof(float));
|
||||
}
|
||||
|
||||
/** Builds an RGB partial image from the useful part buffer.
|
||||
* \param rgb:
|
||||
* The array of size 3*sw*sh containing the RGB values of the sw*sh pixels we need to stored.
|
||||
* These sw*sh pixels constitute a rectangular part of a bigger
|
||||
* RGB image containing w*h pixels.
|
||||
* \param w:
|
||||
* The width of the complete image
|
||||
* \param h:
|
||||
* The height of the complete image
|
||||
* \param sw:
|
||||
* The width of the part of the image we want to store and work on
|
||||
* \param sh:
|
||||
* The height of the part of the image we want to store and work on
|
||||
*/
|
||||
RGBImage(float *rgb, uint w, uint h, uint sw, uint sh, uint ox, uint oy)
|
||||
: FrsImage(w, h, sw, sh, ox, oy)
|
||||
{
|
||||
_rgb = new float[3 * _storedWidth * _storedHeight];
|
||||
memcpy(_rgb, rgb, 3 * _storedWidth * _storedHeight * sizeof(float));
|
||||
}
|
||||
|
||||
RGBImage &operator=(const RGBImage &brother)
|
||||
{
|
||||
dynamic_cast<FrsImage &>(*this) = brother;
|
||||
_rgb = new float[3 * _storedWidth * _storedHeight];
|
||||
memcpy(_rgb, brother._rgb, 3 * _storedWidth * _storedHeight * sizeof(float));
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual ~RGBImage()
|
||||
{
|
||||
if (_rgb) {
|
||||
delete[] _rgb;
|
||||
}
|
||||
}
|
||||
|
||||
inline float getR(uint x, uint y) const
|
||||
{
|
||||
return _rgb[3 * (y - _Oy) * _storedWidth + (x - _Ox) * 3];
|
||||
}
|
||||
|
||||
inline float getG(uint x, uint y) const
|
||||
{
|
||||
return _rgb[3 * (y - _Oy) * _storedWidth + (x - _Ox) * 3 + 1];
|
||||
}
|
||||
|
||||
inline float getB(uint x, uint y) const
|
||||
{
|
||||
return _rgb[3 * (y - _Oy) * _storedWidth + (x - _Ox) * 3 + 2];
|
||||
}
|
||||
|
||||
virtual void setPixel(uint x, uint y, float r, float g, float b)
|
||||
{
|
||||
float *tmp = &(_rgb[3 * (y - _Oy) * _storedWidth + (x - _Ox) * 3]);
|
||||
*tmp = r;
|
||||
tmp++;
|
||||
*tmp = g;
|
||||
tmp++;
|
||||
*tmp = b;
|
||||
}
|
||||
|
||||
virtual float pixel(uint x, uint y) const
|
||||
{
|
||||
float res = 0.0f;
|
||||
float *tmp = &(_rgb[3 * (y - _Oy) * _storedWidth + (x - _Ox) * 3]);
|
||||
res += 11.0f * (*tmp);
|
||||
tmp++;
|
||||
res += 16.0f * (*tmp);
|
||||
tmp++;
|
||||
res += 5.0f * (*tmp);
|
||||
return res / 32.0f;
|
||||
}
|
||||
|
||||
/** Sets the RGB array.
|
||||
* copy
|
||||
* If true, the array is copied, otherwise the pointer is copied
|
||||
*/
|
||||
virtual void setArray(
|
||||
float *rgb, uint width, uint height, uint sw, uint sh, uint x, uint y, bool copy = true)
|
||||
{
|
||||
_width = width;
|
||||
_height = height;
|
||||
_storedWidth = sw;
|
||||
_storedHeight = sh;
|
||||
_Ox = x;
|
||||
_Oy = y;
|
||||
if (!copy) {
|
||||
_rgb = rgb;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(_rgb, rgb, 3 * _storedWidth * _storedHeight * sizeof(float));
|
||||
}
|
||||
|
||||
virtual float *getArray()
|
||||
{
|
||||
return _rgb;
|
||||
}
|
||||
|
||||
protected:
|
||||
float *_rgb;
|
||||
};
|
||||
|
||||
//
|
||||
// GrayImage
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class GrayImage : public FrsImage {
|
||||
public:
|
||||
GrayImage() : FrsImage()
|
||||
{
|
||||
_lvl = 0;
|
||||
}
|
||||
|
||||
GrayImage(const GrayImage &brother) : FrsImage(brother)
|
||||
{
|
||||
_lvl = new float[_storedWidth * _storedHeight];
|
||||
memcpy(_lvl, brother._lvl, _storedWidth * _storedHeight * sizeof(*_lvl));
|
||||
}
|
||||
|
||||
/** Builds an empty gray image */
|
||||
GrayImage(uint w, uint h) : FrsImage(w, h)
|
||||
{
|
||||
_lvl = new float[_width * _height];
|
||||
}
|
||||
|
||||
GrayImage(float *lvl, uint w, uint h) : FrsImage(w, h)
|
||||
{
|
||||
_lvl = new float[_width * _height];
|
||||
memcpy(_lvl, lvl, _width * _height * sizeof(*_lvl));
|
||||
}
|
||||
|
||||
/** Builds a partial image from the useful part buffer.
|
||||
* \param lvl:
|
||||
* The array of size sw*sh containing the gray values of the sw*sh pixels we need to stored.
|
||||
* These sw*sh pixels constitute a rectangular part of a bigger
|
||||
* gray image containing w*h pixels.
|
||||
* \param w:
|
||||
* The width of the complete image
|
||||
* \param h:
|
||||
* The height of the complete image
|
||||
* \param sw:
|
||||
* The width of the part of the image we want to store and work on
|
||||
* \param sh:
|
||||
* The height of the part of the image we want to store and work on
|
||||
*/
|
||||
GrayImage(float *lvl, uint w, uint h, uint sw, uint sh, uint ox, uint oy)
|
||||
: FrsImage(w, h, sw, sh, ox, oy)
|
||||
{
|
||||
_lvl = new float[_storedWidth * _storedHeight];
|
||||
memcpy(_lvl, lvl, _storedWidth * _storedHeight * sizeof(float));
|
||||
}
|
||||
|
||||
GrayImage &operator=(const GrayImage &brother)
|
||||
{
|
||||
dynamic_cast<FrsImage &>(*this) = brother;
|
||||
_lvl = new float[_storedWidth * _storedHeight];
|
||||
memcpy(_lvl, brother._lvl, _storedWidth * _storedHeight * sizeof(float));
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual ~GrayImage()
|
||||
{
|
||||
if (_lvl) {
|
||||
delete[] _lvl;
|
||||
}
|
||||
}
|
||||
|
||||
inline void setPixel(uint x, uint y, float v)
|
||||
{
|
||||
_lvl[(y - _Oy) * _storedWidth + (x - _Ox)] = v;
|
||||
}
|
||||
|
||||
inline float pixel(uint x, uint y) const
|
||||
{
|
||||
return _lvl[(y - _Oy) * _storedWidth + (x - _Ox)];
|
||||
}
|
||||
|
||||
/** Sets the array.
|
||||
* copy
|
||||
* If true, the array is copied, otherwise the pounsigneder is copied
|
||||
*/
|
||||
void setArray(
|
||||
float *lvl, uint width, uint height, uint sw, uint sh, uint x, uint y, bool copy = true)
|
||||
{
|
||||
_width = width;
|
||||
_height = height;
|
||||
_storedWidth = sw;
|
||||
_storedHeight = sh;
|
||||
_Ox = x;
|
||||
_Oy = y;
|
||||
if (!copy) {
|
||||
_lvl = lvl;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(_lvl, lvl, _storedWidth * _storedHeight * sizeof(float));
|
||||
}
|
||||
|
||||
/** Returns the array containing the gray values. */
|
||||
virtual float *getArray()
|
||||
{
|
||||
return _lvl;
|
||||
}
|
||||
|
||||
protected:
|
||||
float *_lvl;
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,181 @@
|
||||
/* SPDX-FileCopyrightText: 2008-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to represent a pyramid of images
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "GaussianFilter.h"
|
||||
#include "Image.h"
|
||||
#include "ImagePyramid.h"
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
#if 0
|
||||
ImagePyramid::ImagePyramid(const GrayImage &level0, uint nbLevels)
|
||||
{
|
||||
BuildPyramid(level0, nbLevels);
|
||||
}
|
||||
#endif
|
||||
|
||||
ImagePyramid::ImagePyramid(const ImagePyramid & /*iBrother*/)
|
||||
{
|
||||
if (!_levels.empty()) {
|
||||
for (vector<GrayImage *>::iterator im = _levels.begin(), imend = _levels.end(); im != imend;
|
||||
++im)
|
||||
{
|
||||
_levels.push_back(new GrayImage(**im));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImagePyramid::~ImagePyramid()
|
||||
{
|
||||
if (!_levels.empty()) {
|
||||
for (vector<GrayImage *>::iterator im = _levels.begin(), imend = _levels.end(); im != imend;
|
||||
++im)
|
||||
{
|
||||
delete (*im);
|
||||
}
|
||||
_levels.clear();
|
||||
}
|
||||
}
|
||||
|
||||
GrayImage *ImagePyramid::getLevel(int l)
|
||||
{
|
||||
return _levels[l];
|
||||
}
|
||||
|
||||
float ImagePyramid::pixel(int x, int y, int level)
|
||||
{
|
||||
GrayImage *img = _levels[level];
|
||||
if (0 == level) {
|
||||
return img->pixel(x, y);
|
||||
}
|
||||
uint i = 1 << level;
|
||||
uint sx = x >> level;
|
||||
uint sy = y >> level;
|
||||
if (sx >= img->width()) {
|
||||
sx = img->width() - 1;
|
||||
}
|
||||
if (sy >= img->height()) {
|
||||
sy = img->height() - 1;
|
||||
}
|
||||
|
||||
// bilinear interpolation
|
||||
float A = i * (sx + 1) - x;
|
||||
float B = x - i * sx;
|
||||
float C = i * (sy + 1) - y;
|
||||
float D = y - i * sy;
|
||||
|
||||
float P1(0), P2(0);
|
||||
P1 = A * img->pixel(sx, sy);
|
||||
if (sx < img->width() - 1) {
|
||||
if (x % i != 0) {
|
||||
P1 += B * img->pixel(sx + 1, sy);
|
||||
}
|
||||
}
|
||||
else {
|
||||
P1 += B * img->pixel(sx, sy);
|
||||
}
|
||||
if (sy < img->height() - 1) {
|
||||
if (y % i != 0) {
|
||||
P2 = A * img->pixel(sx, sy + 1);
|
||||
if (sx < img->width() - 1) {
|
||||
if (x % i != 0) {
|
||||
P2 += B * img->pixel(sx + 1, sy + 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
P2 += B * img->pixel(sx, sy + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
P2 = P1;
|
||||
}
|
||||
return (1.0f / float(1 << (2 * level))) * (C * P1 + D * P2);
|
||||
}
|
||||
|
||||
int ImagePyramid::width(int level)
|
||||
{
|
||||
return _levels[level]->width();
|
||||
}
|
||||
|
||||
int ImagePyramid::height(int level)
|
||||
{
|
||||
return _levels[level]->height();
|
||||
}
|
||||
|
||||
GaussianPyramid::GaussianPyramid(const GrayImage &level0, uint nbLevels, float iSigma)
|
||||
{
|
||||
_sigma = iSigma;
|
||||
BuildPyramid(level0, nbLevels);
|
||||
}
|
||||
|
||||
GaussianPyramid::GaussianPyramid(GrayImage *level0, uint nbLevels, float iSigma)
|
||||
{
|
||||
_sigma = iSigma;
|
||||
BuildPyramid(level0, nbLevels);
|
||||
}
|
||||
|
||||
GaussianPyramid::GaussianPyramid(const GaussianPyramid &iBrother) : ImagePyramid(iBrother)
|
||||
{
|
||||
_sigma = iBrother._sigma;
|
||||
}
|
||||
|
||||
void GaussianPyramid::BuildPyramid(const GrayImage &level0, uint nbLevels)
|
||||
{
|
||||
GrayImage *pLevel = new GrayImage(level0);
|
||||
BuildPyramid(pLevel, nbLevels);
|
||||
}
|
||||
|
||||
void GaussianPyramid::BuildPyramid(GrayImage *level0, uint nbLevels)
|
||||
{
|
||||
GrayImage *pLevel = level0;
|
||||
_levels.push_back(pLevel);
|
||||
GaussianFilter gf(_sigma);
|
||||
// build the nbLevels:
|
||||
uint w = pLevel->width();
|
||||
uint h = pLevel->height();
|
||||
if (nbLevels != 0) {
|
||||
for (uint i = 0; i < nbLevels; ++i) { // soc
|
||||
w = pLevel->width() >> 1;
|
||||
h = pLevel->height() >> 1;
|
||||
GrayImage *img = new GrayImage(w, h);
|
||||
for (uint y = 0; y < h; ++y) {
|
||||
for (uint x = 0; x < w; ++x) {
|
||||
float v = gf.getSmoothedPixel<GrayImage>(pLevel, 2 * x, 2 * y);
|
||||
img->setPixel(x, y, v);
|
||||
}
|
||||
}
|
||||
_levels.push_back(img);
|
||||
pLevel = img;
|
||||
}
|
||||
}
|
||||
else {
|
||||
while ((w > 1) && (h > 1)) {
|
||||
w = pLevel->width() >> 1;
|
||||
h = pLevel->height() >> 1;
|
||||
GrayImage *img = new GrayImage(w, h);
|
||||
for (uint y = 0; y < h; ++y) {
|
||||
for (uint x = 0; x < w; ++x) {
|
||||
float v = gf.getSmoothedPixel<GrayImage>(pLevel, 2 * x, 2 * y);
|
||||
img->setPixel(x, y, v);
|
||||
}
|
||||
}
|
||||
_levels.push_back(img);
|
||||
pLevel = img;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,94 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to represent a pyramid of images
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "../system/FreestyleConfig.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
class GrayImage;
|
||||
|
||||
class ImagePyramid {
|
||||
protected:
|
||||
std::vector<GrayImage *> _levels;
|
||||
|
||||
public:
|
||||
ImagePyramid() {}
|
||||
ImagePyramid(const ImagePyramid &iBrother);
|
||||
// ImagePyramid(const GrayImage& level0, uint nbLevels);
|
||||
virtual ~ImagePyramid();
|
||||
|
||||
/** Builds the pyramid.
|
||||
* must be overloaded by inherited classes.
|
||||
* if nbLevels==0, the complete pyramid is built
|
||||
*/
|
||||
virtual void BuildPyramid(const GrayImage &level0, uint nbLevels) = 0;
|
||||
|
||||
/** Builds a pyramid without copying the base level */
|
||||
virtual void BuildPyramid(GrayImage *level0, uint nbLevels) = 0;
|
||||
|
||||
virtual GrayImage *getLevel(int l);
|
||||
/** Returns the pixel x,y using bilinear interpolation.
|
||||
* \param x:
|
||||
* the abscissa specified in the finest level coordinate system
|
||||
* \param y:
|
||||
* the ordinate specified in the finest level coordinate system
|
||||
* \param level:
|
||||
* the level from which we want the pixel to be evaluated
|
||||
*/
|
||||
virtual float pixel(int x, int y, int level = 0);
|
||||
|
||||
/** Returns the width of the level-th level image */
|
||||
virtual int width(int level = 0);
|
||||
|
||||
/** Returns the height of the level-th level image */
|
||||
virtual int height(int level = 0);
|
||||
|
||||
/** Returns the number of levels in the pyramid */
|
||||
inline int getNumberOfLevels() const
|
||||
{
|
||||
return _levels.size();
|
||||
}
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:ImagePyramid")
|
||||
};
|
||||
|
||||
class GaussianPyramid : public ImagePyramid {
|
||||
protected:
|
||||
float _sigma;
|
||||
|
||||
public:
|
||||
GaussianPyramid(float iSigma = 1.0f) : ImagePyramid()
|
||||
{
|
||||
_sigma = iSigma;
|
||||
}
|
||||
|
||||
GaussianPyramid(const GrayImage &level0, uint nbLevels, float iSigma = 1.0f);
|
||||
GaussianPyramid(GrayImage *level0, uint nbLevels, float iSigma = 1.0f);
|
||||
GaussianPyramid(const GaussianPyramid &iBrother);
|
||||
virtual ~GaussianPyramid() {}
|
||||
|
||||
virtual void BuildPyramid(const GrayImage &level0, uint nbLevels);
|
||||
virtual void BuildPyramid(GrayImage *level0, uint nbLevels);
|
||||
|
||||
/* accessors */
|
||||
inline float getSigma() const
|
||||
{
|
||||
return _sigma;
|
||||
}
|
||||
|
||||
/* modifiers */
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
Reference in New Issue
Block a user