정형/비정형 물체에 대한 체적 알고리즘 코드를 받아서, 윈도우 환경에 포팅하고 UI 상에서 동작확인이 필요하다.
UI 상에서 3가지 정보가 확인되어야 한다.
알고리즘 계산에는 opencv/pcl 라이브러리가 사용되고, 고객은 mfc 프로그램을 기대하고 있는걸로 보인다. 관련해서 프로그램을 작성한다.
pcl-1.14.1 을 사용 (pcl-1.13.x 대에선 임베드시 랜더링에 문제가 있었다)
Debug 모드에서는 new 가 Debug_New 로 define 되어 있으므로 pcl 객체를 할당할때는
다음 방법중 하나를 사용해야 한다.
#ifdef _DEBUG
#undef new
#endif
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
전처리기에 다음 매크로를 선언
NOMINMAX
PostMessage 의 경우 비동기 메시지 처리이므로, 스레드나 자원이 정리된 후에 실행되는 상황이 발생할 수 있다.
따라서 정리 로직과 메시지 처리 로직은 서로의 시나리오를 이해하고, 이에 맞게 작성해야 한다.
SendMessage 의 경우 동기 메시지 처리이므로, 특정 시나리오에서 메시지가 오지 않으면 스레드가 계속 block 되어 있게 된다.
따라서 적절한 Timeout 값을 사용해서 UI 갱신에 영향이 없게 구성한다.
pclvisualizer 기동시 eigen free 에러가 발생하는 경우가 있다.
알고리즘을 개발하는 인턴은 센서보드가 없어서, ply 파일을 통해 depth 정보를 획득하고 있었다.
실 개발에서도, depth 정보를 센서 보드에서 얻을지 ply 파일에서 얻을지를 선택할 수 있었으면 좋겠다.

readDepth() 외에도 finalize() 작업도 선택이 필요하다.
이 작업은 Viewer 스레드에서 하지않고, Dialog 클래스에서 처리해야 한다.

알고리즘도 변경이 가능했으면 좋겠다.


알고리즘을 실행하려면 Viewer 스레드에서 Algorithm 스레드로 cloud 를 넘길 방법이 필요하다.
//// ConcurrentQueue.h
#pragma once
#include <queue>
#include <mutex>
#include <condition_variable>
template <typename T>
class ConcurrentQueue
{
public:
bool push(T value) {
if (stop_)
return false;
std::lock_guard<std::mutex> lock(mtx_);
q_.push(std::move(value));
cv_.notify_one();
return true;
}
bool pop(T& value) {
std::unique_lock<std::mutex> lock(mtx_);
cv_.wait(lock, [this] { return !q_.empty() || stop_; });
// false : shutdown() called and empty queue
if (stop_ && q_.empty())
return false;
// true : queue has data or shutdown() not called
value = std::move(q_.front());
q_.pop();
return true;
}
bool try_pop(T& value) {
std::lock_guard<std::mutex> lock(mtx_);
if (stop_ || q_.empty()) {
return false;
}
value = std::move(q_.front());
q_.pop();
return true;
}
void shutdown() {
std::lock_guard<std::mutex> lock(mtx_);
std::queue<T>().swap(q_); // remove all data
stop_ = true;
cv_.notify_all();
}
void reset() {
stop_ = false;
}
private:
std::queue<T> q_;
std::mutex mtx_;
std::condition_variable cv_;
bool stop_ = false; // pop() exit block
};
//// CloudQueue.h
#pragma once
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include "ConcurrentQueue.h"
using PointXYZ = pcl::PointXYZ;
using PointCloudXYZ = pcl::PointCloud<pcl::PointXYZ>;
using PointCloudXYZPtr = PointCloudXYZ::Ptr;
using CloudQueue = ConcurrentQueue<PointCloudXYZPtr>;
using PointCloudXYZRGB = pcl::PointCloud<pcl::PointXYZRGB>;
using PointCloudXYZRGBPtr = PointCloudXYZRGB::Ptr;
using PointXYZRGB = pcl::PointXYZRGB;
using CloudRGBQueue = ConcurrentQueue<PointCloudXYZRGBPtr>;
프로그램을 실행하면, 작업관리자에서 메모리가 빠른 속도로 (초당 10MB) 증가하는게 관측되는데 viewer 스레드에서 문제인지, algorithm 스레드에서 문제인지 모르겠다.
문제 시나리오는 다음과 같다.
1) viewer 스레드는 CloudQueue 에 넣을 cloud 데이터를 동적할당한다. (화살표 지점)
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->width = width;
cloud->height = height;
cloud->is_dense = true;
cloud->points.reserve(cloud->width * cloud->height); // <--
2) 동적할당한 cloud 데이터는 640x480 해상도의 xyz 데이터로, 약 3MB 의 메모리를 사용한다.
3) algorithm 스레드가 직접 메모리에 접근하면, 그때 증가한다.
이 문제는 기본적으로 "viewer 스레드 push 속도 > algirhtm 스레드 pop 속도" 여서
발생한 현상이고, cloud memory pool 을 사용하는 방식으로 개선하였다.
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud = pool->acquire();
if (!cloud) { // <--
std::this_thread::sleep_for(std::chrono::milliseconds(1));
continue;
}
알고리즘 결과는 체적 text 와 pclvisualizer 로 확인이 되어야 한다.
pclvisualizer 를 mfc picture control 에 임베드할때는 다음 방법을 사용한다.
void CvolumeviewerrefactorDlg::initCloudPicCtrl()
{
// pclvisualizer 객체를 스마트포인터에 할당
m_viewer.reset(new pcl::visualization::PCLVisualizer("viewer", false));
// renderer window 를 mfc picture control 에 연결
m_renderWindow = m_viewer->getRenderWindow();
m_renderWindow->SetParentId(m_picCloud.GetSafeHwnd());
// vtk interactor 객체를 스마트포인터에 만들고, render window 에 연결한다
vtkNew<vtkRenderWindowInteractor> interactor;
m_viewer->setupInteractor(interactor, m_renderWindow);
// 이제 마우스 동작이 된다
m_interactor = interactor;
m_interactor->Initialize();
// mfc picture control 크기를 구한다
CRect rect;
m_picCloud.GetClientRect(&rect);
// render window 크기를 mfc picture control 크기에 맞춘다
m_renderWindow = m_viewer->getRenderWindow();
m_renderWindow->SetSize(rect.Width(), rect.Height());
// pclvisualizer 화면설정을 수행한다
m_viewer->setBackgroundColor(0.15, 0.15, 0.15);
m_viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "sample cloud");
m_viewer->addCoordinateSystem(100.0);
// resetCamera 를 호출하려면 한번 getCameraParameters 를 호출해야 한다
m_viewer->getCameraParameters(m_cam_init);
m_viewer->resetCamera();
// 30ms 마다 pclvisualizer spinOnce() 와 renderwer window Render() 를 수행
SetTimer(1, 30, NULL);
}
void CvolumeviewerrefactorDlg::OnTimer(UINT_PTR nIDEvent)
{
if (nIDEvent == 1 && m_viewer)
{
PointCloudXYZRGBPtr cloud = nullptr;
// 이번에 그릴 cloud 를 획득
latest_cloud_mutex.lock();
cloud = latest_cloud;
latest_cloud_mutex.unlock();
// cloud 가 존재하면 update 시도
if (cloud && m_checkCloudUpdate.GetCheck() == BST_CHECKED) {
if (!m_viewer->updatePointCloud(cloud, "sample cloud")) {
m_viewer->addPointCloud<pcl::PointXYZRGB>(cloud, "sample cloud");
m_viewer->resetCamera();
}
}
// pclvisualizer 응답을 위한 호출
m_viewer->spinOnce(1);
// render window Render() 를 호출해주어야 그림이 그려지는 현상이 있었다
m_renderWindow->Render();
}
...
CDialogEx::OnTimer(nIDEvent);
}
https://gitlab.com/feather973/volume_viewer_refactor
C:\Program Files\PCL 1.14.1
C:\opencv