vendor: OpenCV 5.0.0 snapshot at 40738fb16ceddb5fb3fea747585f7ce6abb0605b

This commit is contained in:
Gitea Mirror Bot
2026-08-22 00:10:33 +08:00
commit f7f077da11
6933 changed files with 2335208 additions and 0 deletions
+412
View File
@@ -0,0 +1,412 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#include "precomp.hpp"
namespace cv {
TriangleRasterizeSettings::TriangleRasterizeSettings()
{
shadingType = RASTERIZE_SHADING_SHADED;
cullingMode = RASTERIZE_CULLING_CW;
glCompatibleMode = RASTERIZE_COMPAT_DISABLED;
}
static void drawTriangle(Vec4f verts[3], Vec3f colors[3], Mat& depthBuf, Mat& colorBuf,
TriangleRasterizeSettings settings)
{
// this will be useful during refactoring
// if there's gonna be more supported data types
CV_DbgAssert(depthBuf.empty() || depthBuf.type() == CV_32FC1);
CV_DbgAssert(colorBuf.empty() || colorBuf.type() == CV_32FC3);
// any of buffers can be empty
int width = std::max(colorBuf.cols, depthBuf.cols);
int height = std::max(colorBuf.rows, depthBuf.rows);
Point minPt(width, height), maxPt(0, 0);
for (int i = 0; i < 3; i++)
{
// round down to cover the whole pixel
int x = (int)(verts[i][0]), y = (int)(verts[i][1]);
minPt.x = std::min( x, minPt.x);
minPt.y = std::min( y, minPt.y);
maxPt.x = std::max(x + 1, maxPt.x);
maxPt.y = std::max(y + 1, maxPt.y);
}
minPt.x = std::max(minPt.x, 0); maxPt.x = std::min(maxPt.x, width);
minPt.y = std::max(minPt.y, 0); maxPt.y = std::min(maxPt.y, height);
Point2f a(verts[0][0], verts[0][1]), b(verts[1][0], verts[1][1]), c(verts[2][0], verts[2][1]);
Point2f bc = b - c, ac = a - c;
float d = ac.x*bc.y - ac.y*bc.x;
// culling and degenerated triangle removal
if ((settings.cullingMode == RASTERIZE_CULLING_CW && d <= 0) ||
(settings.cullingMode == RASTERIZE_CULLING_CCW && d >= 0) ||
(abs(d) < 1e-6))
{
return;
}
float invd = 1.f / d;
Vec3f zinv { verts[0][2], verts[1][2], verts[2][2] };
Vec3f w { verts[0][3], verts[1][3], verts[2][3] };
for (int y = minPt.y; y < maxPt.y; y++)
{
for (int x = minPt.x; x < maxPt.x; x++)
{
Point2f p(x + 0.5f, y + 0.5f), pc = p - c;
// barycentric coordinates
Vec3f f;
f[0] = ( pc.x * bc.y - pc.y * bc.x) * invd;
f[1] = ( pc.y * ac.x - pc.x * ac.y) * invd;
f[2] = 1.f - f[0] - f[1];
// if inside the triangle
if ((f[0] >= 0) && (f[1] >= 0) && (f[2] >= 0))
{
bool update = false;
if (!depthBuf.empty())
{
float zCurrent = depthBuf.at<float>(height - 1 - y, x);
float zNew = f[0] * zinv[0] + f[1] * zinv[1] + f[2] * zinv[2];
if (zNew < zCurrent)
{
update = true;
depthBuf.at<float>(height - 1 - y, x) = zNew;
}
}
else // RASTERIZE_SHADING_WHITE
{
update = true;
}
if (!colorBuf.empty() && update)
{
Vec3f color;
if (settings.shadingType == RASTERIZE_SHADING_WHITE)
{
color = { 1.f, 1.f, 1.f };
}
else if (settings.shadingType == RASTERIZE_SHADING_FLAT)
{
color = colors[0];
}
else // TriangleShadingType::Shaded
{
float zInter = 1.0f / (f[0] * w[0] + f[1] * w[1] + f[2] * w[2]);
color = { 0, 0, 0 };
for (int j = 0; j < 3; j++)
{
color += (f[j] * w[j]) * colors[j];
}
color *= zInter;
}
colorBuf.at<Vec3f>(height - 1 - y, x) = color;
}
}
}
}
}
// values outside of [zNear, zFar] have to be restored
// [0, 1] -> [zNear, zFar]
static void linearizeDepth(const Mat& inbuf, const Mat& validMask, Mat outbuf, double zFar, double zNear)
{
CV_Assert(inbuf.type() == CV_32FC1);
CV_Assert(validMask.type() == CV_8UC1 || validMask.type() == CV_8SC1 || validMask.type() == CV_BoolC1);
CV_Assert(outbuf.type() == CV_32FC1);
CV_Assert(outbuf.size() == inbuf.size());
float scaleNear = (float)(1.0 / zNear);
float scaleFar = (float)(1.0 / zFar);
for (int y = 0; y < inbuf.rows; y++)
{
const float* inp = inbuf.ptr<float>(y);
const uchar * validPtr = validMask.ptr<uchar>(y);
float * outp = outbuf.ptr<float>(y);
for (int x = 0; x < inbuf.cols; x++)
{
if (validPtr[x])
{
float d = inp[x];
// precision-optimized version of this:
//float z = - zFar * zNear / (d * (zFar - zNear) - zFar);
float z = 1.f / ((1.f - d) * scaleNear + d * scaleFar );
outp[x] = z;
}
}
}
}
// [zNear, zFar] -> [0, 1]
static void invertDepth(const Mat& inbuf, Mat& outbuf, Mat& validMask, double zNear, double zFar)
{
CV_Assert(inbuf.type() == CV_32FC1);
outbuf.create(inbuf.size(), CV_32FC1);
validMask.create(inbuf.size(), CV_8UC1);
float fNear = (float)zNear, fFar = (float)zFar;
float zadd = (float)(zFar / (zFar - zNear));
float zmul = (float)(-zNear * zFar / (zFar - zNear));
for (int y = 0; y < inbuf.rows; y++)
{
const float * inp = inbuf.ptr<float>(y);
float * outp = outbuf.ptr<float>(y);
uchar * validPtr = validMask.ptr<uchar>(y);
for (int x = 0; x < inbuf.cols; x++)
{
float z = inp[x];
uchar m = (z >= fNear) && (z <= fFar);
z = std::max(std::min(z, fFar), fNear);
// precision-optimized version of this:
// outp[x] = (z - zNear) / z * zFar / (zFar - zNear);
outp[x] = zadd + zmul / z;
validPtr[x] = m;
}
}
}
static void triangleRasterizeInternal(InputArray _vertices, InputArray _indices, InputArray _colors,
Mat& colorBuf, Mat& depthBuf,
InputArray world2cam, double fovyRadians, double zNear, double zFar,
const TriangleRasterizeSettings& settings)
{
CV_Assert(world2cam.type() == CV_32FC1 || world2cam.type() == CV_64FC1);
CV_Assert((world2cam.size() == Size {4, 3}) || (world2cam.size() == Size {4, 4}));
CV_Assert((fovyRadians > 0) && (fovyRadians < CV_PI));
CV_Assert(zNear > 0);
CV_Assert(zFar > zNear);
Mat cpMat;
world2cam.getMat().convertTo(cpMat, CV_64FC1);
Matx44d camPoseMat = Matx44d::eye();
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
camPoseMat(i, j) = cpMat.at<double>(i, j);
}
}
if(_indices.empty())
{
return;
}
CV_CheckFalse(_vertices.empty(), "No vertices provided along with indices array");
Mat vertices, colors, triangles;
int nVerts = 0, nColors = 0, nTriangles = 0;
int vertexType = _vertices.type();
CV_Assert(vertexType == CV_32FC1 || vertexType == CV_32FC3);
vertices = _vertices.getMat();
// transform 3xN matrix to Nx3, except 3x3
if ((_vertices.channels() == 1) && (_vertices.rows() == 3) && (_vertices.cols() != 3))
{
vertices = vertices.t();
}
// This transposition is performed on 1xN matrix so it's almost free in terms of performance
vertices = vertices.reshape(3, 1).t();
nVerts = (int)vertices.total();
int indexType = _indices.type();
CV_Assert(indexType == CV_32SC1 || indexType == CV_32SC3);
triangles = _indices.getMat();
// transform 3xN matrix to Nx3, except 3x3
if ((_indices.channels() == 1) && (_indices.rows() == 3) && (_indices.cols() != 3))
{
triangles = triangles.t();
}
// This transposition is performed on 1xN matrix so it's almost free in terms of performance
triangles = triangles.reshape(3, 1).t();
nTriangles = (int)triangles.total();
if (!_colors.empty())
{
int colorType = _colors.type();
CV_Assert(colorType == CV_32FC1 || colorType == CV_32FC3);
colors = _colors.getMat();
// transform 3xN matrix to Nx3, except 3x3
if ((_colors.channels() == 1) && (_colors.rows() == 3) && (_colors.cols() != 3))
{
colors = colors.t();
}
colors = colors.reshape(3, 1).t();
nColors = (int)colors.total();
CV_Assert(nColors == nVerts);
}
// any of buffers can be empty
Size imgSize {std::max(colorBuf.cols, depthBuf.cols), std::max(colorBuf.rows, depthBuf.rows)};
// world-to-camera coord system
Matx44d lookAtMatrix = camPoseMat;
double ys = 1.0 / std::tan(fovyRadians / 2);
double xs = ys / (double)imgSize.width * (double)imgSize.height;
double zz = (zNear + zFar) / (zNear - zFar);
double zw = 2.0 * zFar * zNear / (zNear - zFar);
// camera to NDC: [-1, 1]^3
Matx44d perspectMatrix (xs, 0, 0, 0,
0, ys, 0, 0,
0, 0, zz, zw,
0, 0, -1, 0);
Matx44f mvpMatrix = perspectMatrix * lookAtMatrix;
// vertex transform stage
Mat screenVertices(vertices.size(), CV_32FC4);
for (int i = 0; i < nVerts; i++)
{
Vec3f vglobal = vertices.at<Vec3f>(i);
float x_num = std::fma(mvpMatrix(0,0), vglobal[0],
std::fma(mvpMatrix(0,1), vglobal[1],
std::fma(mvpMatrix(0,2), vglobal[2], mvpMatrix(0,3))));
float y_num = std::fma(mvpMatrix(1,0), vglobal[0],
std::fma(mvpMatrix(1,1), vglobal[1],
std::fma(mvpMatrix(1,2), vglobal[2], mvpMatrix(1,3))));
float z_num = std::fma(mvpMatrix(2,0), vglobal[0],
std::fma(mvpMatrix(2,1), vglobal[1],
std::fma(mvpMatrix(2,2), vglobal[2], mvpMatrix(2,3))));
float w_num = std::fma(mvpMatrix(3,0), vglobal[0],
std::fma(mvpMatrix(3,1), vglobal[1],
std::fma(mvpMatrix(3,2), vglobal[2], mvpMatrix(3,3))));
float invw = 1.f / w_num;
// [-1, 1]^3 => [0, width] x [0, height] x [0, 1]
Vec4f vscreen = {
std::fma(x_num * invw, 0.5f * (float)imgSize.width, 0.5f * (float)imgSize.width),
std::fma(y_num * invw, 0.5f * (float)imgSize.height, 0.5f * (float)imgSize.height),
std::fma(z_num * invw, 0.5f, 0.5f),
invw
};
screenVertices.at<Vec4f>(i) = vscreen;
}
// draw stage
for (int t = 0; t < nTriangles; t++)
{
Vec3i tri = triangles.at<Vec3i>(t);
Vec3f col[3];
Vec4f ver[3];
for (int i = 0; i < 3; i++)
{
int idx = tri[i];
CV_DbgAssert(idx >= 0 && idx < nVerts);
col[i] = colors.empty() ? Vec3f::all(0) : colors.at<Vec3f>(idx);
ver[i] = screenVertices.at<Vec4f>(idx);
}
drawTriangle(ver, col, depthBuf, colorBuf, settings);
}
}
void triangleRasterizeDepth(InputArray _vertices, InputArray _indices, InputOutputArray _depthBuf,
InputArray world2cam, double fovY, double zNear, double zFar,
const TriangleRasterizeSettings& settings)
{
CV_Assert(!_depthBuf.empty());
CV_Assert(_depthBuf.type() == CV_32FC1);
Mat emptyColorBuf;
// out-of-range values from user-provided depthBuf should not be altered, let's mark them
Mat_<uchar> validMask;
Mat depthBuf;
if (settings.glCompatibleMode == RASTERIZE_COMPAT_INVDEPTH)
{
depthBuf = _depthBuf.getMat();
}
else // RASTERIZE_COMPAT_DISABLED
{
invertDepth(_depthBuf.getMat(), depthBuf, validMask, zNear, zFar);
}
triangleRasterizeInternal(_vertices, _indices, noArray(), emptyColorBuf, depthBuf, world2cam, fovY, zNear, zFar, settings);
if (settings.glCompatibleMode == RASTERIZE_COMPAT_DISABLED)
{
linearizeDepth(depthBuf, validMask, _depthBuf.getMat(), zFar, zNear);
}
}
void triangleRasterizeColor(InputArray _vertices, InputArray _indices, InputArray _colors, InputOutputArray _colorBuf,
InputArray world2cam, double fovY, double zNear, double zFar,
const TriangleRasterizeSettings& settings)
{
CV_Assert(!_colorBuf.empty());
CV_Assert(_colorBuf.type() == CV_32FC3);
Mat colorBuf = _colorBuf.getMat();
Mat depthBuf;
if (_colors.empty())
{
// full white shading does not require depth test
CV_Assert(settings.shadingType == RASTERIZE_SHADING_WHITE);
}
else
{
// internal depth buffer is not exposed outside
depthBuf.create(_colorBuf.size(), CV_32FC1);
depthBuf.setTo(1.0);
}
triangleRasterizeInternal(_vertices, _indices, _colors, colorBuf, depthBuf, world2cam, fovY, zNear, zFar, settings);
}
void triangleRasterize(InputArray _vertices, InputArray _indices, InputArray _colors,
InputOutputArray _colorBuffer, InputOutputArray _depthBuffer,
InputArray world2cam, double fovyRadians, double zNear, double zFar,
const TriangleRasterizeSettings& settings)
{
if (_colors.empty())
{
CV_Assert(settings.shadingType == RASTERIZE_SHADING_WHITE);
}
CV_Assert(!_colorBuffer.empty());
CV_Assert(_colorBuffer.type() == CV_32FC3);
CV_Assert(!_depthBuffer.empty());
CV_Assert(_depthBuffer.type() == CV_32FC1);
CV_Assert(_depthBuffer.size() == _colorBuffer.size());
Mat colorBuf = _colorBuffer.getMat();
// out-of-range values from user-provided depthBuf should not be altered, let's mark them
Mat_<uchar> validMask;
Mat depthBuf;
if (settings.glCompatibleMode == RASTERIZE_COMPAT_INVDEPTH)
{
depthBuf = _depthBuffer.getMat();
}
else // RASTERIZE_COMPAT_DISABLED
{
invertDepth(_depthBuffer.getMat(), depthBuf, validMask, zNear, zFar);
}
triangleRasterizeInternal(_vertices, _indices, _colors, colorBuf, depthBuf, world2cam, fovyRadians, zNear, zFar, settings);
if (settings.glCompatibleMode == RASTERIZE_COMPAT_DISABLED)
{
linearizeDepth(depthBuf, validMask, _depthBuffer.getMat(), zFar, zNear);
}
}
} // namespace cv