mirror of https://github.com/opencv/opencv.git
Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
549 lines
18 KiB
549 lines
18 KiB
#include <iostream> |
|
#include <map> |
|
|
|
#ifdef _WIN32 |
|
#define WIN32_LEAN_AND_MEAN 1 |
|
#define NOMINMAX 1 |
|
#include <windows.h> |
|
#endif |
|
|
|
#if defined(__APPLE__) |
|
#include <OpenGL/gl.h> |
|
#include <OpenGL/glu.h> |
|
#else |
|
#include <GL/gl.h> |
|
#include <GL/glu.h> |
|
#endif |
|
|
|
#include "opencv2/core.hpp" |
|
#include "opencv2/core/opengl.hpp" |
|
#include "opencv2/3d.hpp" |
|
#include "opencv2/imgproc.hpp" |
|
#include "opencv2/highgui.hpp" |
|
|
|
using namespace std; |
|
using namespace cv; |
|
using namespace cv::cuda; |
|
|
|
// model data should be identical to the code from tests |
|
enum class ModelType |
|
{ |
|
Empty = 0, |
|
File = 1, |
|
Clipping = 2, |
|
Color = 3, |
|
Centered = 4 |
|
}; |
|
|
|
static void generateNormals(const std::vector<Vec3f>& points, const std::vector<std::vector<int>>& indices, |
|
std::vector<Vec3f>& normals) |
|
{ |
|
std::vector<std::vector<Vec3f>> preNormals(points.size(), std::vector<Vec3f>()); |
|
|
|
for (const auto& tri : indices) |
|
{ |
|
Vec3f p0 = points[tri[0]]; |
|
Vec3f p1 = points[tri[1]]; |
|
Vec3f p2 = points[tri[2]]; |
|
|
|
Vec3f cross = cv::normalize((p1 - p0).cross(p2 - p0)); |
|
for (int i = 0; i < 3; i++) |
|
{ |
|
preNormals[tri[i]].push_back(cross); |
|
} |
|
} |
|
|
|
normals.reserve(points.size()); |
|
for (const auto& pn : preNormals) |
|
{ |
|
Vec3f sum { }; |
|
for (const auto& n : pn) |
|
{ |
|
sum += n; |
|
} |
|
normals.push_back(cv::normalize(sum)); |
|
} |
|
} |
|
|
|
class ModelData |
|
{ |
|
public: |
|
ModelData(ModelType type = ModelType::Empty, std::string objPath = { }) |
|
{ |
|
switch (type) |
|
{ |
|
case ModelType::Empty: |
|
{ |
|
position = Vec3d(0.0, 0.0, 0.0); |
|
lookat = Vec3d(0.0, 0.0, 0.0); |
|
upVector = Vec3d(0.0, 1.0, 0.0); |
|
|
|
fovy = 45.0; |
|
zNear = 0.1; |
|
zFar = 50; |
|
scaleCoeff = 1000.0; |
|
|
|
vertices = std::vector<Vec3f>(4, {2.0f, 0, -2.0f}); |
|
colors = std::vector<Vec3f>(4, {0, 0, 1.0f}); |
|
indices = { }; |
|
} |
|
break; |
|
case ModelType::File: |
|
{ |
|
position = Vec3d( 1.9, 0.4, 1.3); |
|
lookat = Vec3d( 0.0, 0.0, 0.0); |
|
upVector = Vec3d( 0.0, 1.0, 0.0); |
|
|
|
fovy = 45.0; |
|
zNear = 0.1; |
|
zFar = 50; |
|
scaleCoeff = 1000.0; |
|
|
|
objectPath = objPath; |
|
std::vector<vector<int>> indvec; |
|
loadMesh(objectPath, vertices, indvec); |
|
// using per-vertex normals as colors |
|
generateNormals(vertices, indvec, colors); |
|
if (vertices.size() != colors.size()) |
|
{ |
|
std::runtime_error("Model should contain normals for each vertex"); |
|
} |
|
for (const auto &vec : indvec) |
|
{ |
|
indices.push_back({vec[0], vec[1], vec[2]}); |
|
} |
|
|
|
for (auto &color : colors) |
|
{ |
|
color = Vec3f(abs(color[0]), abs(color[1]), abs(color[2])); |
|
} |
|
} |
|
break; |
|
case ModelType::Clipping: |
|
{ |
|
position = Vec3d(0.0, 0.0, 5.0); |
|
lookat = Vec3d(0.0, 0.0, 0.0); |
|
upVector = Vec3d(0.0, 1.0, 0.0); |
|
|
|
fovy = 45.0; |
|
zNear = 0.1; |
|
zFar = 50; |
|
scaleCoeff = 1000.0; |
|
|
|
vertices = |
|
{ |
|
{ 2.0, 0.0, -2.0}, { 0.0, -6.0, -2.0}, {-2.0, 0.0, -2.0}, |
|
{ 3.5, -1.0, -5.0}, { 2.5, -2.5, -5.0}, {-1.0, 1.0, -5.0}, |
|
{-6.5, -1.0, -3.0}, {-2.5, -2.0, -3.0}, { 1.0, 1.0, -5.0}, |
|
}; |
|
|
|
indices = { {0, 1, 2}, {3, 4, 5}, {6, 7, 8} }; |
|
|
|
Vec3f col1(217.0, 238.0, 185.0); |
|
Vec3f col2(185.0, 217.0, 238.0); |
|
Vec3f col3(150.0, 10.0, 238.0); |
|
|
|
col1 *= (1.f / 255.f); |
|
col2 *= (1.f / 255.f); |
|
col3 *= (1.f / 255.f); |
|
|
|
colors = |
|
{ |
|
col1, col2, col3, |
|
col2, col3, col1, |
|
col3, col1, col2, |
|
}; |
|
} |
|
break; |
|
case ModelType::Centered: |
|
{ |
|
position = Vec3d(0.0, 0.0, 5.0); |
|
lookat = Vec3d(0.0, 0.0, 0.0); |
|
upVector = Vec3d(0.0, 1.0, 0.0); |
|
|
|
fovy = 45.0; |
|
zNear = 0.1; |
|
zFar = 50; |
|
scaleCoeff = 1000.0; |
|
|
|
vertices = |
|
{ |
|
{ 2.0, 0.0, -2.0}, { 0.0, -2.0, -2.0}, {-2.0, 0.0, -2.0}, |
|
{ 3.5, -1.0, -5.0}, { 2.5, -1.5, -5.0}, {-1.0, 0.5, -5.0}, |
|
}; |
|
|
|
indices = { {0, 1, 2}, {3, 4, 5} }; |
|
|
|
Vec3f col1(217.0, 238.0, 185.0); |
|
Vec3f col2(185.0, 217.0, 238.0); |
|
|
|
col1 *= (1.f / 255.f); |
|
col2 *= (1.f / 255.f); |
|
|
|
colors = |
|
{ |
|
col1, col2, col1, |
|
col2, col1, col2, |
|
}; |
|
} |
|
break; |
|
case ModelType::Color: |
|
{ |
|
position = Vec3d(0.0, 0.0, 5.0); |
|
lookat = Vec3d(0.0, 0.0, 0.0); |
|
upVector = Vec3d(0.0, 1.0, 0.0); |
|
|
|
fovy = 60.0; |
|
zNear = 0.1; |
|
zFar = 50; |
|
scaleCoeff = 1000.0; |
|
|
|
vertices = |
|
{ |
|
{ 2.0, 0.0, -2.0}, |
|
{ 0.0, 2.0, -3.0}, |
|
{-2.0, 0.0, -2.0}, |
|
{ 0.0, -2.0, 1.0}, |
|
}; |
|
|
|
indices = { {0, 1, 2}, {0, 2, 3} }; |
|
|
|
colors = |
|
{ |
|
{ 0.0f, 0.0f, 1.0f}, |
|
{ 0.0f, 1.0f, 0.0f}, |
|
{ 1.0f, 0.0f, 0.0f}, |
|
{ 0.0f, 1.0f, 0.0f}, |
|
}; |
|
} |
|
break; |
|
|
|
default: |
|
CV_Error(Error::StsBadArg, "Unknown model type"); |
|
break; |
|
} |
|
} |
|
|
|
ModelData(std::string modelPath, double fov, double nearPlane, double farPlane, double scale, Vec3d pos, Vec3d center, Vec3d up) |
|
{ |
|
objectPath = modelPath; |
|
position = pos; |
|
lookat = center; |
|
upVector = up; |
|
fovy = fov; |
|
zNear = nearPlane; |
|
zFar = farPlane; |
|
scaleCoeff = scale; |
|
|
|
std::vector<vector<int>> indvec; |
|
|
|
loadMesh(objectPath, vertices, indvec, noArray(), colors); |
|
if (vertices.size() != colors.size()) |
|
{ |
|
std::runtime_error("Model should contain normals for each vertex"); |
|
} |
|
for (const auto &vec : indvec) |
|
{ |
|
indices.push_back({vec[0], vec[1], vec[2]}); |
|
} |
|
} |
|
|
|
Vec3d position; |
|
Vec3d lookat; |
|
Vec3d upVector; |
|
|
|
double fovy, zNear, zFar, scaleCoeff; |
|
|
|
std::vector<Vec3f> vertices; |
|
std::vector<Vec3i> indices; |
|
std::vector<Vec3f> colors; |
|
|
|
string objectPath; |
|
}; |
|
|
|
|
|
struct DrawData |
|
{ |
|
ogl::Arrays arr; |
|
ogl::Buffer indices; |
|
}; |
|
|
|
void draw(void* userdata); |
|
|
|
void draw(void* userdata) |
|
{ |
|
DrawData* data = static_cast<DrawData*>(userdata); |
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
|
ogl::render(data->arr, data->indices, ogl::TRIANGLES); |
|
} |
|
|
|
static void generateImage(cv::Size imgSz, TriangleShadingType shadingType, TriangleCullingMode cullingMode, |
|
const ModelData& modelData, cv::Mat& colorImage, cv::Mat& depthImage) |
|
{ |
|
namedWindow("OpenGL", WINDOW_OPENGL); |
|
resizeWindow("OpenGL", imgSz.width, imgSz.height); |
|
|
|
DrawData data; |
|
|
|
std::vector<Vec3f> vertices; |
|
std::vector<Vec4f> colors4f; |
|
std::vector<int> idxLinear; |
|
|
|
if (shadingType == RASTERIZE_SHADING_FLAT) |
|
{ |
|
// rearrange vertices and colors for flat shading |
|
int ctr = 0; |
|
for (const auto& idx : modelData.indices) |
|
{ |
|
for (int i = 0; i < 3; i++) |
|
{ |
|
vertices.push_back(modelData.vertices[idx[i]]); |
|
idxLinear.push_back(ctr++); |
|
} |
|
|
|
Vec3f ci = modelData.colors[idx[0]]; |
|
for (int i = 0; i < 3; i++) |
|
{ |
|
colors4f.emplace_back(ci[0], ci[1], ci[2], 1.f); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
vertices = modelData.vertices; |
|
for (const auto& c : modelData.colors) |
|
{ |
|
Vec3f ci = (shadingType == RASTERIZE_SHADING_SHADED) ? c: cv::Vec3f::all(1.f); |
|
colors4f.emplace_back(ci[0], ci[1], ci[2], 1.0); |
|
} |
|
|
|
for (const auto& idx : modelData.indices) |
|
{ |
|
for (int i = 0; i < 3; i++) |
|
{ |
|
idxLinear.push_back(idx[i]); |
|
} |
|
} |
|
} |
|
|
|
data.arr.setVertexArray(vertices); |
|
data.arr.setColorArray(colors4f); |
|
data.indices.copyFrom(idxLinear); |
|
|
|
glMatrixMode(GL_PROJECTION); |
|
glLoadIdentity(); |
|
gluPerspective(modelData.fovy, (double)imgSz.width / imgSz.height, modelData.zNear, modelData.zFar); |
|
|
|
glMatrixMode(GL_MODELVIEW); |
|
glLoadIdentity(); |
|
|
|
//gluLookAt(0, 0, 5, 0, 0, 0, 0, 1, 0); |
|
gluLookAt(modelData.position[0], modelData.position[1], modelData.position[2], |
|
modelData.lookat [0], modelData.lookat [1], modelData.lookat [2], |
|
modelData.upVector[0], modelData.upVector[1], modelData.upVector[2]); |
|
|
|
if (cullingMode == RASTERIZE_CULLING_NONE) |
|
{ |
|
glDisable(GL_CULL_FACE); |
|
} |
|
else |
|
{ |
|
glEnable(GL_CULL_FACE); |
|
glCullFace(GL_FRONT); |
|
if (cullingMode == RASTERIZE_CULLING_CW) |
|
{ |
|
glFrontFace(GL_CW); |
|
} |
|
else |
|
{ |
|
glFrontFace(GL_CCW); |
|
} |
|
} |
|
|
|
glEnable(GL_DEPTH_TEST); |
|
|
|
cv::setOpenGlDrawCallback("OpenGL", draw, &data); |
|
|
|
const int framesToSkip = 10; |
|
for (int f = 0; f < framesToSkip; f++) |
|
{ |
|
updateWindow("OpenGL"); |
|
|
|
colorImage = cv::Mat(imgSz.height, imgSz.width, CV_8UC3); |
|
glReadPixels(0, 0, imgSz.width, imgSz.height, GL_RGB, GL_UNSIGNED_BYTE, colorImage.data); |
|
cv::cvtColor(colorImage, colorImage, cv::COLOR_RGB2BGR); |
|
cv::flip(colorImage, colorImage, 0); |
|
|
|
depthImage = cv::Mat(imgSz.height, imgSz.width, CV_32F); |
|
glReadPixels(0, 0, imgSz.width, imgSz.height, GL_DEPTH_COMPONENT, GL_FLOAT, depthImage.data); |
|
// map from [0, 1] to [zNear, zFar] |
|
for (auto it = depthImage.begin<float>(); it != depthImage.end<float>(); ++it) |
|
{ |
|
*it = (float)(modelData.zNear * modelData.zFar / (double(*it) * (modelData.zNear - modelData.zFar) + modelData.zFar)); |
|
} |
|
cv::flip(depthImage, depthImage, 0); |
|
depthImage.convertTo(depthImage, CV_16U, modelData.scaleCoeff); |
|
|
|
char key = (char)waitKey(40); |
|
if (key == 27) |
|
break; |
|
} |
|
|
|
cv::setOpenGlDrawCallback("OpenGL", 0, 0); |
|
cv::destroyAllWindows(); |
|
} |
|
|
|
|
|
int main(int argc, char* argv[]) |
|
{ |
|
cv::CommandLineParser parser(argc, argv, |
|
"{ help h usage ? | | show this message }" |
|
"{ outPath | | output path for generated images }" |
|
"{ modelPath | | path to 3d model to render }" |
|
"{ custom | | pass it to use custom camera parameters instead of iterating through test parameters }" |
|
"{ fov | 45.0 | (if custom parameters are used) field of view }" |
|
"{ posx | 1.0 | (if custom parameters are used) camera position x }" |
|
"{ posy | 1.0 | (if custom parameters are used) camera position y }" |
|
"{ posz | 1.0 | (if custom parameters are used) camera position z }" |
|
"{ lookatx | 0.0 | (if custom parameters are used) lookup camera direction x }" |
|
"{ lookaty | 0.0 | (if custom parameters are used) lookup camera direction y }" |
|
"{ lookatz | 0.0 | (if custom parameters are used) lookup camera direction z }" |
|
"{ upx | 0.0 | (if custom parameters are used) up camera direction x }" |
|
"{ upy | 1.0 | (if custom parameters are used) up camera direction y }" |
|
"{ upz | 0.0 | (if custom parameters are used) up camera direction z }" |
|
"{ resx | 640 | (if custom parameters are used) camera resolution x }" |
|
"{ resy | 480 | (if custom parameters are used) camera resolution y }" |
|
"{ zNear | 0.1 | (if custom parameters are used) near z clipping plane }" |
|
"{ zFar | 50 | (if custom parameters are used) far z clipping plane }" |
|
"{ scaleCoeff | 1000 | (if custom parameters are used) scale coefficient for saving depth }" |
|
"{ shading | | (if custom parameters are used) shading type: white/flat/shaded }" |
|
"{ culling | | (if custom parameters are used) culling type: none/cw/ccw }" |
|
"{ colorPath | | (if custom parameters are used) output path for color image }" |
|
"{ depthPath | | (if custom parameters are used) output path for depth image }" |
|
); |
|
parser.about("This app is used to generate test data for triangleRasterize() function"); |
|
|
|
if (parser.has("help")) |
|
{ |
|
parser.printMessage(); |
|
return 0; |
|
} |
|
|
|
std::string modelPath = parser.get<std::string>("modelPath"); |
|
if (modelPath.empty()) |
|
{ |
|
std::cout << "No model path given" << std::endl; |
|
return -1; |
|
} |
|
|
|
if (parser.has("custom")) |
|
{ |
|
double fov = parser.get<double>("fov"); |
|
Vec3d position, lookat, upVector; |
|
position[0] = parser.get<double>("posx"); |
|
position[1] = parser.get<double>("posy"); |
|
position[2] = parser.get<double>("posz"); |
|
lookat[0] = parser.get<double>("lookatx"); |
|
lookat[1] = parser.get<double>("lookaty"); |
|
lookat[2] = parser.get<double>("lookatz"); |
|
upVector[0] = parser.get<double>("upx"); |
|
upVector[1] = parser.get<double>("upy"); |
|
upVector[2] = parser.get<double>("upz"); |
|
Size res; |
|
res.width = parser.get<int>("resx"); |
|
res.height = parser.get<int>("resy"); |
|
double zNear = parser.get<double>("zNear"); |
|
double zFar = parser.get<double>("zFar"); |
|
double scaleCoeff = parser.get<double>("scaleCoeff"); |
|
|
|
std::map<std::string, cv::TriangleShadingType> shadingTxt = { |
|
{ "white", RASTERIZE_SHADING_WHITE }, |
|
{ "flat", RASTERIZE_SHADING_FLAT }, |
|
{ "shaded", RASTERIZE_SHADING_SHADED }, |
|
}; |
|
cv::TriangleShadingType shadingType = shadingTxt.at(parser.get<std::string>("shading")); |
|
|
|
std::map<std::string, cv::TriangleCullingMode> cullingTxt = { |
|
{ "none", RASTERIZE_CULLING_NONE }, |
|
{ "cw", RASTERIZE_CULLING_CW }, |
|
{ "ccw", RASTERIZE_CULLING_CCW }, |
|
}; |
|
cv::TriangleCullingMode cullingMode = cullingTxt.at(parser.get<std::string>("culling")); |
|
|
|
std::string colorPath = parser.get<std::string>("colorPath"); |
|
std::string depthPath = parser.get<std::string>("depthPath"); |
|
|
|
Mat colorImage, depthImage; |
|
ModelData modelData(modelPath, fov, zNear, zFar, scaleCoeff, position, lookat, upVector); |
|
generateImage(res, shadingType, cullingMode, modelData, colorImage, depthImage); |
|
|
|
cv::imwrite(colorPath, colorImage); |
|
cv::imwrite(depthPath, depthImage); |
|
} |
|
else |
|
{ |
|
std::string outPath = parser.get<std::string>("outPath"); |
|
if (outPath.empty()) |
|
{ |
|
std::cout << "No output path given" << std::endl; |
|
return -1; |
|
} |
|
|
|
std::array<cv::Size, 4> resolutions = {cv::Size{700, 700}, cv::Size{640, 480}, cv::Size(256, 256), cv::Size(320, 240)}; |
|
std::vector<std::pair<cv::TriangleShadingType, std::string>> shadingTxt = { |
|
{RASTERIZE_SHADING_WHITE, "White"}, |
|
{RASTERIZE_SHADING_FLAT, "Flat"}, |
|
{RASTERIZE_SHADING_SHADED, "Shaded"}, |
|
}; |
|
std::vector<std::pair<cv::TriangleCullingMode, std::string>> cullingTxt = { |
|
{RASTERIZE_CULLING_NONE, "None"}, |
|
{RASTERIZE_CULLING_CW, "CW"}, |
|
{RASTERIZE_CULLING_CCW, "CCW"}, |
|
}; |
|
std::vector<std::pair<ModelType, std::string>> modelTxt = { |
|
{ModelType::File, "File"}, |
|
{ModelType::Clipping, "Clipping"}, |
|
{ModelType::Color, "Color"}, |
|
{ModelType::Centered, "Centered"}, |
|
}; |
|
|
|
for (const auto& res : resolutions) |
|
{ |
|
for (const auto shadingPair : shadingTxt) |
|
{ |
|
cv::TriangleShadingType shadingType = shadingPair.first; |
|
std::string shadingName = shadingPair.second; |
|
|
|
for (const auto cullingPair : cullingTxt) |
|
{ |
|
cv::TriangleCullingMode cullingMode = cullingPair.first; |
|
std::string cullingName = cullingPair.second; |
|
|
|
for (const auto modelPair : modelTxt) |
|
{ |
|
ModelType modelType = modelPair.first; |
|
std::string modelName = modelPair.second; |
|
|
|
std::string suffix = cv::format("%s_%dx%d_Cull%s", modelName.c_str(), res.width, res.height, cullingName.c_str()); |
|
|
|
std::cout << suffix + "_" + shadingName << "..." << std::endl; |
|
|
|
cv::Mat colorImage, depthImage; |
|
|
|
ModelData modelData(modelType, modelPath); |
|
generateImage(res, shadingType, cullingMode, modelData, colorImage, depthImage); |
|
|
|
std::string gtPathColor = outPath + "/example_image_" + suffix + "_" + shadingName + ".png"; |
|
std::string gtPathDepth = outPath + "/depth_image_" + suffix + ".png"; |
|
|
|
cv::imwrite(gtPathColor, colorImage); |
|
cv::imwrite(gtPathDepth, depthImage); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
return 0; |
|
}
|
|
|