2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2008-2023 Blender Authors
|
2023-06-14 23:30:43 +10:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2013-01-02 01:55:30 +00:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup freestyle
|
2019-04-30 17:50:57 +10:00
|
|
|
* \brief Convenient access to the steerable ViewMap to which any element of the ViewMap belongs
|
|
|
|
|
* to.
|
2013-01-02 01:55:30 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <sstream>
|
2008-04-30 15:41:54 +00:00
|
|
|
|
2010-04-07 23:28:29 +00:00
|
|
|
#include "Silhouette.h"
|
2008-04-30 15:41:54 +00:00
|
|
|
#include "SteerableViewMap.h"
|
2013-01-02 01:55:30 +00:00
|
|
|
|
2008-04-30 15:41:54 +00:00
|
|
|
#include "../geometry/Geom.h"
|
|
|
|
|
|
2013-01-02 01:55:30 +00:00
|
|
|
#include "../image/Image.h"
|
2020-04-21 12:39:12 +02:00
|
|
|
#include "../image/ImagePyramid.h"
|
2008-05-25 17:34:21 +00:00
|
|
|
|
Cleanup: reduce amount of math-related includes
Using ClangBuildAnalyzer on the whole Blender build, it was pointing
out that BLI_math.h is the heaviest "header hub" (i.e. non tiny file
that is included a lot).
However, there's very little (actually zero) source files in Blender
that need "all the math" (base, colors, vectors, matrices,
quaternions, intersection, interpolation, statistics, solvers and
time). A common use case is source files needing just vectors, or
just vectors & matrices, or just colors etc. Actually, 181 files
were including the whole math thing without needing it at all.
This change removes BLI_math.h completely, and instead in all the
places that need it, includes BLI_math_vector.h or BLI_math_color.h
and so on.
Change from that:
- BLI_math_color.h was included 1399 times -> now 408 (took 114.0sec
to parse -> now 36.3sec)
- BLI_simd.h 1403 -> 418 (109.7sec -> 34.9sec).
Full rebuild of Blender (Apple M1, Xcode, RelWithDebInfo) is not
affected much (342sec -> 334sec). Most of benefit would be when
someone's changing BLI_simd.h or BLI_math_color.h or similar files,
that now there's 3x fewer files result in a recompile.
Pull Request #110944
2023-08-09 11:39:20 +03:00
|
|
|
#include "BLI_math_base.h"
|
2022-10-26 18:58:04 +02:00
|
|
|
#include "BLI_sys_types.h"
|
|
|
|
|
|
2024-02-10 18:25:14 +01:00
|
|
|
#include "BKE_global.hh"
|
2013-01-03 23:27:20 +00:00
|
|
|
|
2024-01-18 22:50:23 +02:00
|
|
|
#include "IMB_imbuf.hh"
|
|
|
|
|
#include "IMB_imbuf_types.hh"
|
2008-04-30 15:41:54 +00:00
|
|
|
|
2013-04-09 00:46:49 +00:00
|
|
|
namespace Freestyle {
|
|
|
|
|
|
2013-01-02 01:55:30 +00:00
|
|
|
using namespace Geometry;
|
|
|
|
|
|
2022-09-25 17:04:52 +10:00
|
|
|
SteerableViewMap::SteerableViewMap(uint nbOrientations)
|
2013-01-02 01:55:30 +00:00
|
|
|
{
|
|
|
|
|
_nbOrientations = nbOrientations;
|
2022-09-25 18:33:28 +10:00
|
|
|
_bound = cos(M_PI / float(_nbOrientations));
|
2022-09-25 17:04:52 +10:00
|
|
|
for (uint i = 0; i < _nbOrientations; ++i) {
|
2022-09-25 18:33:28 +10:00
|
|
|
_directions.emplace_back(cos(float(i) * M_PI / float(_nbOrientations)),
|
|
|
|
|
sin(float(i) * M_PI / float(_nbOrientations)));
|
2013-01-02 01:55:30 +00:00
|
|
|
}
|
|
|
|
|
Build();
|
2008-04-30 15:41:54 +00:00
|
|
|
}
|
|
|
|
|
|
2013-01-02 01:55:30 +00:00
|
|
|
void SteerableViewMap::Build()
|
|
|
|
|
{
|
2013-03-11 06:56:51 +00:00
|
|
|
_imagesPyramids =
|
|
|
|
|
new ImagePyramid *[_nbOrientations + 1]; // one more map to store the complete visible VM
|
2013-01-02 01:55:30 +00:00
|
|
|
memset((_imagesPyramids), 0, (_nbOrientations + 1) * sizeof(ImagePyramid *));
|
2008-04-30 15:41:54 +00:00
|
|
|
}
|
|
|
|
|
|
2013-01-02 01:55:30 +00:00
|
|
|
SteerableViewMap::SteerableViewMap(const SteerableViewMap &iBrother)
|
|
|
|
|
{
|
|
|
|
|
_nbOrientations = iBrother._nbOrientations;
|
2022-09-25 17:04:52 +10:00
|
|
|
uint i;
|
2013-01-02 01:55:30 +00:00
|
|
|
_bound = iBrother._bound;
|
|
|
|
|
_directions = iBrother._directions;
|
|
|
|
|
_mapping = iBrother._mapping;
|
2013-03-11 06:56:51 +00:00
|
|
|
_imagesPyramids =
|
|
|
|
|
new ImagePyramid *[_nbOrientations + 1]; // one more map to store the complete visible VM
|
2019-05-31 22:51:19 +10:00
|
|
|
for (i = 0; i <= _nbOrientations; ++i) {
|
2013-01-02 01:55:30 +00:00
|
|
|
_imagesPyramids[i] = new GaussianPyramid(
|
|
|
|
|
*(dynamic_cast<GaussianPyramid *>(iBrother._imagesPyramids[i])));
|
2019-05-31 22:51:19 +10:00
|
|
|
}
|
2008-04-30 15:41:54 +00:00
|
|
|
}
|
|
|
|
|
|
2013-01-02 01:55:30 +00:00
|
|
|
SteerableViewMap::~SteerableViewMap()
|
|
|
|
|
{
|
|
|
|
|
Clear();
|
2008-04-30 15:41:54 +00:00
|
|
|
}
|
|
|
|
|
|
2013-01-02 01:55:30 +00:00
|
|
|
void SteerableViewMap::Clear()
|
|
|
|
|
{
|
2022-09-25 17:04:52 +10:00
|
|
|
uint i;
|
2013-01-02 01:55:30 +00:00
|
|
|
if (_imagesPyramids) {
|
|
|
|
|
for (i = 0; i <= _nbOrientations; ++i) {
|
2019-05-31 22:51:19 +10:00
|
|
|
if (_imagesPyramids[i]) {
|
2013-01-02 01:55:30 +00:00
|
|
|
delete (_imagesPyramids)[i];
|
2019-05-31 22:51:19 +10:00
|
|
|
}
|
2013-01-02 01:55:30 +00:00
|
|
|
}
|
|
|
|
|
delete[] _imagesPyramids;
|
2020-11-06 17:49:09 +01:00
|
|
|
_imagesPyramids = nullptr;
|
2018-06-17 17:05:14 +02:00
|
|
|
}
|
2013-01-02 01:55:30 +00:00
|
|
|
if (!_mapping.empty()) {
|
2022-09-25 17:04:52 +10:00
|
|
|
for (map<uint, double *>::iterator m = _mapping.begin(), mend = _mapping.end(); m != mend; ++m)
|
2013-01-02 01:55:30 +00:00
|
|
|
{
|
2024-01-02 18:12:54 +01:00
|
|
|
delete[] (*m).second;
|
2013-01-02 01:55:30 +00:00
|
|
|
}
|
|
|
|
|
_mapping.clear();
|
|
|
|
|
}
|
2008-04-30 15:41:54 +00:00
|
|
|
}
|
|
|
|
|
|
2013-01-02 01:55:30 +00:00
|
|
|
void SteerableViewMap::Reset()
|
|
|
|
|
{
|
|
|
|
|
Clear();
|
|
|
|
|
Build();
|
2008-04-30 15:41:54 +00:00
|
|
|
}
|
|
|
|
|
|
2022-09-26 10:04:44 +10:00
|
|
|
double SteerableViewMap::ComputeWeight(const Vec2d &dir, uint i)
|
2013-01-02 01:55:30 +00:00
|
|
|
{
|
|
|
|
|
double dotp = fabs(dir * _directions[i]);
|
2019-05-31 22:51:19 +10:00
|
|
|
if (dotp < _bound) {
|
2013-01-02 01:55:30 +00:00
|
|
|
return 0.0;
|
2019-05-31 22:51:19 +10:00
|
|
|
}
|
|
|
|
|
if (dotp > 1.0) {
|
2013-01-02 01:55:30 +00:00
|
|
|
dotp = 1.0;
|
2019-05-31 22:51:19 +10:00
|
|
|
}
|
2008-04-30 15:41:54 +00:00
|
|
|
|
2022-09-25 18:33:28 +10:00
|
|
|
return cos(float(_nbOrientations) / 2.0 * acos(dotp));
|
2008-04-30 15:41:54 +00:00
|
|
|
}
|
|
|
|
|
|
2013-01-02 01:55:30 +00:00
|
|
|
double *SteerableViewMap::AddFEdge(FEdge *iFEdge)
|
|
|
|
|
{
|
2022-09-26 10:04:44 +10:00
|
|
|
uint i;
|
|
|
|
|
uint id = iFEdge->getId().getFirst();
|
2022-09-25 17:04:52 +10:00
|
|
|
map<uint, double *>::iterator o = _mapping.find(id);
|
2013-01-02 01:55:30 +00:00
|
|
|
if (o != _mapping.end()) {
|
|
|
|
|
return (*o).second;
|
|
|
|
|
}
|
|
|
|
|
double *res = new double[_nbOrientations];
|
|
|
|
|
for (i = 0; i < _nbOrientations; ++i) {
|
|
|
|
|
res[i] = 0.0;
|
|
|
|
|
}
|
|
|
|
|
Vec3r o2d3 = iFEdge->orientation2d();
|
|
|
|
|
Vec2r o2d2(o2d3.x(), o2d3.y());
|
|
|
|
|
real norm = o2d2.norm();
|
|
|
|
|
if (norm < 1.0e-6) {
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
o2d2 /= norm;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-01-02 01:55:30 +00:00
|
|
|
for (i = 0; i < _nbOrientations; ++i) {
|
|
|
|
|
res[i] = ComputeWeight(o2d2, i);
|
|
|
|
|
}
|
|
|
|
|
_mapping[id] = res;
|
|
|
|
|
return res;
|
2008-04-30 15:41:54 +00:00
|
|
|
}
|
|
|
|
|
|
2022-09-26 10:04:44 +10:00
|
|
|
uint SteerableViewMap::getSVMNumber(Vec2f dir)
|
2013-01-02 01:55:30 +00:00
|
|
|
{
|
2023-07-25 12:51:50 +10:00
|
|
|
// soc uint res = 0;
|
2013-01-02 01:55:30 +00:00
|
|
|
real norm = dir.norm();
|
|
|
|
|
if (norm < 1.0e-6) {
|
|
|
|
|
return _nbOrientations + 1;
|
|
|
|
|
}
|
|
|
|
|
dir /= norm;
|
|
|
|
|
double maxw = 0.0f;
|
2022-09-26 10:04:44 +10:00
|
|
|
uint winner = _nbOrientations + 1;
|
2022-09-25 17:04:52 +10:00
|
|
|
for (uint i = 0; i < _nbOrientations; ++i) {
|
2013-01-02 01:55:30 +00:00
|
|
|
double w = ComputeWeight(dir, i);
|
|
|
|
|
if (w > maxw) {
|
|
|
|
|
maxw = w;
|
|
|
|
|
winner = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return winner;
|
2008-04-30 15:41:54 +00:00
|
|
|
}
|
|
|
|
|
|
2022-09-26 10:04:44 +10:00
|
|
|
uint SteerableViewMap::getSVMNumber(uint id)
|
2013-01-02 01:55:30 +00:00
|
|
|
{
|
2022-09-25 17:04:52 +10:00
|
|
|
map<uint, double *>::iterator o = _mapping.find(id);
|
2013-01-02 01:55:30 +00:00
|
|
|
if (o != _mapping.end()) {
|
|
|
|
|
double *wvalues = (*o).second;
|
2018-06-17 17:05:14 +02:00
|
|
|
double maxw = 0.0;
|
2022-09-26 10:04:44 +10:00
|
|
|
uint winner = _nbOrientations + 1;
|
|
|
|
|
for (uint i = 0; i < _nbOrientations; ++i) {
|
2013-01-02 01:55:30 +00:00
|
|
|
double w = wvalues[i];
|
|
|
|
|
if (w > maxw) {
|
|
|
|
|
maxw = w;
|
|
|
|
|
winner = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return winner;
|
|
|
|
|
}
|
|
|
|
|
return _nbOrientations + 1;
|
2008-04-30 15:41:54 +00:00
|
|
|
}
|
|
|
|
|
|
2013-01-02 01:55:30 +00:00
|
|
|
void SteerableViewMap::buildImagesPyramids(GrayImage **steerableBases,
|
|
|
|
|
bool copy,
|
2022-09-26 10:04:44 +10:00
|
|
|
uint iNbLevels,
|
2013-01-02 01:55:30 +00:00
|
|
|
float iSigma)
|
|
|
|
|
{
|
2022-09-25 17:04:52 +10:00
|
|
|
for (uint i = 0; i <= _nbOrientations; ++i) {
|
2013-01-02 01:55:30 +00:00
|
|
|
ImagePyramid *svm = (_imagesPyramids)[i];
|
2020-07-03 16:32:12 +02:00
|
|
|
delete svm;
|
2019-05-31 22:51:19 +10:00
|
|
|
if (copy) {
|
2013-01-02 01:55:30 +00:00
|
|
|
svm = new GaussianPyramid(*(steerableBases[i]), iNbLevels, iSigma);
|
2019-05-31 22:51:19 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2013-01-02 01:55:30 +00:00
|
|
|
svm = new GaussianPyramid(steerableBases[i], iNbLevels, iSigma);
|
2019-05-31 22:51:19 +10:00
|
|
|
}
|
2013-01-02 01:55:30 +00:00
|
|
|
_imagesPyramids[i] = svm;
|
|
|
|
|
}
|
2008-04-30 15:41:54 +00:00
|
|
|
}
|
|
|
|
|
|
2022-09-26 10:04:44 +10:00
|
|
|
float SteerableViewMap::readSteerableViewMapPixel(uint iOrientation, int iLevel, int x, int y)
|
2013-01-02 01:55:30 +00:00
|
|
|
{
|
|
|
|
|
ImagePyramid *pyramid = _imagesPyramids[iOrientation];
|
2013-01-03 23:27:20 +00:00
|
|
|
if (!pyramid) {
|
|
|
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
|
|
|
cout << "Warning: this steerable ViewMap level doesn't exist" << endl;
|
|
|
|
|
}
|
|
|
|
|
return 0.0f;
|
2013-01-02 01:55:30 +00:00
|
|
|
}
|
2019-05-31 22:51:19 +10:00
|
|
|
if ((x < 0) || (x >= pyramid->width()) || (y < 0) || (y >= pyramid->height())) {
|
2013-01-02 01:55:30 +00:00
|
|
|
return 0;
|
2019-05-31 22:51:19 +10:00
|
|
|
}
|
2019-04-30 17:50:57 +10:00
|
|
|
// float v = pyramid->pixel(x, pyramid->height() - 1 - y, iLevel) * 255.0f;
|
|
|
|
|
// We encode both the directionality and the lines counting on 8 bits (because of frame buffer).
|
|
|
|
|
// Thus, we allow until 8 lines to pass through the same pixel, so that we can discretize the
|
|
|
|
|
// Pi/_nbOrientations angle into 32 slices. Therefore, for example, in the vertical direction, a
|
|
|
|
|
// vertical line will have the value 32 on each pixel it passes through.
|
2013-01-02 01:55:30 +00:00
|
|
|
float v = pyramid->pixel(x, pyramid->height() - 1 - y, iLevel) / 32.0f;
|
|
|
|
|
return v;
|
2008-04-30 15:41:54 +00:00
|
|
|
}
|
|
|
|
|
|
2013-01-02 01:55:30 +00:00
|
|
|
float SteerableViewMap::readCompleteViewMapPixel(int iLevel, int x, int y)
|
|
|
|
|
{
|
|
|
|
|
return readSteerableViewMapPixel(_nbOrientations, iLevel, x, y);
|
2008-04-30 15:41:54 +00:00
|
|
|
}
|
|
|
|
|
|
2022-09-25 17:04:52 +10:00
|
|
|
uint SteerableViewMap::getNumberOfPyramidLevels() const
|
2013-01-02 01:55:30 +00:00
|
|
|
{
|
2019-05-31 22:51:19 +10:00
|
|
|
if (_imagesPyramids[0]) {
|
2013-01-02 01:55:30 +00:00
|
|
|
return _imagesPyramids[0]->getNumberOfLevels();
|
2019-05-31 22:51:19 +10:00
|
|
|
}
|
2013-01-02 01:55:30 +00:00
|
|
|
return 0;
|
2008-04-30 15:41:54 +00:00
|
|
|
}
|
2008-05-18 13:01:52 +00:00
|
|
|
|
2013-01-02 01:55:30 +00:00
|
|
|
void SteerableViewMap::saveSteerableViewMap() const
|
|
|
|
|
{
|
2022-09-25 17:04:52 +10:00
|
|
|
for (uint i = 0; i <= _nbOrientations; ++i) {
|
2020-11-06 17:49:09 +01:00
|
|
|
if (_imagesPyramids[i] == nullptr) {
|
2013-03-11 06:56:51 +00:00
|
|
|
cerr << "SteerableViewMap warning: orientation " << i
|
|
|
|
|
<< " of steerable View Map whas not been computed yet" << endl;
|
2013-01-02 01:55:30 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
int ow = _imagesPyramids[i]->width(0);
|
|
|
|
|
int oh = _imagesPyramids[i]->height(0);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-30 17:50:57 +10:00
|
|
|
// soc QString base("SteerableViewMap");
|
2013-01-02 01:55:30 +00:00
|
|
|
string base("SteerableViewMap");
|
2023-05-03 14:13:27 +10:00
|
|
|
stringstream filepath;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-30 17:50:57 +10:00
|
|
|
for (int j = 0; j < _imagesPyramids[i]->getNumberOfLevels(); ++j) { // soc
|
2013-01-02 01:55:30 +00:00
|
|
|
float coeff = 1.0f; // 1 / 255.0f; // 100 * 255; // * pow(2, j);
|
2019-04-30 17:50:57 +10:00
|
|
|
// soc QImage qtmp(ow, oh, QImage::Format_RGB32);
|
2025-03-03 17:11:45 +01:00
|
|
|
ImBuf *ibuf = IMB_allocImBuf(ow, oh, 32, IB_byte_data);
|
2013-01-02 01:55:30 +00:00
|
|
|
int rowbytes = ow * 4;
|
2023-05-18 10:19:01 +02:00
|
|
|
uchar *pix;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-30 17:50:57 +10:00
|
|
|
for (int y = 0; y < oh; ++y) { // soc
|
|
|
|
|
for (int x = 0; x < ow; ++x) { // soc
|
2022-09-25 18:33:28 +10:00
|
|
|
int c = int(coeff * _imagesPyramids[i]->pixel(x, y, j));
|
2019-05-31 22:51:19 +10:00
|
|
|
if (c > 255) {
|
2013-01-02 01:55:30 +00:00
|
|
|
c = 255;
|
2019-05-31 22:51:19 +10:00
|
|
|
}
|
2023-07-25 12:51:50 +10:00
|
|
|
// int c = int(_imagesPyramids[i]->pixel(x, y, j));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-30 17:50:57 +10:00
|
|
|
// soc qtmp.setPixel(x, y, qRgb(c, c, c));
|
2023-05-18 10:19:01 +02:00
|
|
|
pix = ibuf->byte_buffer.data + y * rowbytes + x * 4;
|
2013-03-11 06:56:51 +00:00
|
|
|
pix[0] = pix[1] = pix[2] = c;
|
2013-01-02 01:55:30 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-30 17:50:57 +10:00
|
|
|
// soc qtmp.save(base+QString::number(i)+"-"+QString::number(j)+".png", "PNG");
|
2023-05-03 14:13:27 +10:00
|
|
|
filepath << base;
|
|
|
|
|
filepath << i << "-" << j << ".png";
|
2015-07-13 13:58:17 +02:00
|
|
|
ibuf->ftype = IMB_FTYPE_PNG;
|
2025-03-27 13:12:54 +01:00
|
|
|
IMB_save_image(ibuf, const_cast<char *>(filepath.str().c_str()), 0);
|
2013-01-02 01:55:30 +00:00
|
|
|
}
|
|
|
|
|
#if 0
|
|
|
|
|
QString base("SteerableViewMap");
|
2023-07-25 12:51:50 +10:00
|
|
|
for (uint j = 0; j < _imagesPyramids[i]->getNumberOfLevels(); ++j) {
|
2013-01-02 01:55:30 +00:00
|
|
|
GrayImage *img = _imagesPyramids[i]->getLevel(j);
|
|
|
|
|
int ow = img->width();
|
|
|
|
|
int oh = img->height();
|
2019-04-17 08:24:14 +02:00
|
|
|
float coeff = 1.0f; // 100 * 255; // * pow(2, j);
|
2013-01-02 01:55:30 +00:00
|
|
|
QImage qtmp(ow, oh, 32);
|
2023-07-25 12:51:50 +10:00
|
|
|
for (uint y = 0; y < oh; ++y) {
|
|
|
|
|
for (uint x = 0; x < ow; ++x) {
|
|
|
|
|
int c = int(coeff * img->pixel(x, y));
|
2019-05-31 23:21:16 +10:00
|
|
|
if (c > 255) {
|
2013-01-02 01:55:30 +00:00
|
|
|
c = 255;
|
2019-05-31 23:21:16 +10:00
|
|
|
}
|
2023-08-09 10:47:43 +10:00
|
|
|
// int c = int(_imagesPyramids[i]->pixel(x, y, j));
|
2013-01-02 01:55:30 +00:00
|
|
|
qtmp.setPixel(x, y, qRgb(c, c, c));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
qtmp.save(base + QString::number(i) + "-" + QString::number(j) + ".png", "PNG");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2008-04-30 15:41:54 +00:00
|
|
|
}
|
2013-04-09 00:46:49 +00:00
|
|
|
|
|
|
|
|
} /* namespace Freestyle */
|