KinectV2保存图片序列, 仅仅控制开始结束, 例如, 按键B(Begin)开始保存, 按键E(End)结束保存. 保存路径为当前工作路径,在此为~/catkin_ws/
首先,简单理解运行Kinect2显示图像时的代码
rosrun kinect2_viewer kinect2_viewer
意义为,运行kinect2_viewer包中的kinect2_viewer节点,因此,进入这个包的路径/home/junwang/catkin_ws/src/iai_kinect2/kinect2_viewer/src
即可看到该节点的源码viewer.cpp
代码路径:/home/junwang/catkin_ws/src/iai_kinect2/kinect2_viewer/src
参考:https://blog.csdn.net/RainVictor1/article/details/70547015
详解参考:https://blog.csdn.net/sunbibei/article/details/51594824#保存图片
以下代码亲测可直接复制运行
/**
* Copyright 2014 University of Bremen, Institute for Artificial Intelligence
* Author: Thiemo Wiedemeyer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class Receiver
{
public:
enum Mode
{
IMAGE = 0,
CLOUD,
BOTH
};
private:
std::mutex lock;
const std::string topicColor, topicDepth;
const bool useExact, useCompressed;
bool updateImage, updateCloud;
bool save;
bool save_seq;
bool running;
size_t frame;
const size_t queueSize;
cv::Mat color, depth;
cv::Mat cameraMatrixColor, cameraMatrixDepth;
cv::Mat lookupX, lookupY;
typedef message_filters::sync_policies::ExactTime ExactSyncPolicy;
typedef message_filters::sync_policies::ApproximateTime ApproximateSyncPolicy;
ros::NodeHandle nh;
ros::AsyncSpinner spinner;
image_transport::ImageTransport it;
image_transport::SubscriberFilter *subImageColor, *subImageDepth;
message_filters::Subscriber *subCameraInfoColor, *subCameraInfoDepth;
message_filters::Synchronizer *syncExact;
message_filters::Synchronizer *syncApproximate;
std::thread imageViewerThread;
Mode mode;
pcl::PointCloud::Ptr cloud;
pcl::PCDWriter writer;
std::ostringstream oss;
std::vector params;
public:
Receiver(const std::string &topicColor, const std::string &topicDepth, const bool useExact, const bool useCompressed)
: topicColor(topicColor), topicDepth(topicDepth), useExact(useExact), useCompressed(useCompressed),
updateImage(false), updateCloud(false), save(false), save_seq(false),running(false), frame(0), queueSize(5),
nh("~"), spinner(0), it(nh), mode(CLOUD)
{
cameraMatrixColor = cv::Mat::zeros(3, 3, CV_64F);
cameraMatrixDepth = cv::Mat::zeros(3, 3, CV_64F);
params.push_back(cv::IMWRITE_JPEG_QUALITY);
params.push_back(100);
params.push_back(cv::IMWRITE_PNG_COMPRESSION);
params.push_back(1);
params.push_back(cv::IMWRITE_PNG_STRATEGY);
params.push_back(cv::IMWRITE_PNG_STRATEGY_RLE);
params.push_back(0);
}
~Receiver()
{
}
void run(const Mode mode)
{
start(mode);
stop();
}
private:
void start(const Mode mode)
{
this->mode = mode;
running = true;
std::string topicCameraInfoColor = topicColor.substr(0, topicColor.rfind('/')) + "/camera_info";
std::string topicCameraInfoDepth = topicDepth.substr(0, topicDepth.rfind('/')) + "/camera_info";
image_transport::TransportHints hints(useCompressed ? "compressed" : "raw");
subImageColor = new image_transport::SubscriberFilter(it, topicColor, queueSize, hints);
subImageDepth = new image_transport::SubscriberFilter(it, topicDepth, queueSize, hints);
subCameraInfoColor = new message_filters::Subscriber(nh, topicCameraInfoColor, queueSize);
subCameraInfoDepth = new message_filters::Subscriber(nh, topicCameraInfoDepth, queueSize);
if(useExact)
{
syncExact = new message_filters::Synchronizer(ExactSyncPolicy(queueSize), *subImageColor, *subImageDepth, *subCameraInfoColor, *subCameraInfoDepth);
syncExact->registerCallback(boost::bind(&Receiver::callback, this, _1, _2, _3, _4));
}
else
{
syncApproximate = new message_filters::Synchronizer(ApproximateSyncPolicy(queueSize), *subImageColor, *subImageDepth, *subCameraInfoColor, *subCameraInfoDepth);
syncApproximate->registerCallback(boost::bind(&Receiver::callback, this, _1, _2, _3, _4));
}
spinner.start();
std::chrono::milliseconds duration(1);
while(!updateImage || !updateCloud)
{
if(!ros::ok())
{
return;
}
std::this_thread::sleep_for(duration);
}
cloud = pcl::PointCloud::Ptr(new pcl::PointCloud());
cloud->height = color.rows;
cloud->width = color.cols;
cloud->is_dense = false;
cloud->points.resize(cloud->height * cloud->width);
createLookup(this->color.cols, this->color.rows);
switch(mode)
{
case CLOUD:
cloudViewer();
break;
case IMAGE:
imageViewer();
break;
case BOTH:
imageViewerThread = std::thread(&Receiver::imageViewer, this);
cloudViewer();
break;
}
}
void stop()
{
spinner.stop();
if(useExact)
{
delete syncExact;
}
else
{
delete syncApproximate;
}
delete subImageColor;
delete subImageDepth;
delete subCameraInfoColor;
delete subCameraInfoDepth;
running = false;
if(mode == BOTH)
{
imageViewerThread.join();
}
}
void callback(const sensor_msgs::Image::ConstPtr imageColor, const sensor_msgs::Image::ConstPtr imageDepth,
const sensor_msgs::CameraInfo::ConstPtr cameraInfoColor, const sensor_msgs::CameraInfo::ConstPtr cameraInfoDepth)
{
cv::Mat color, depth;
readCameraInfo(cameraInfoColor, cameraMatrixColor);
readCameraInfo(cameraInfoDepth, cameraMatrixDepth);
readImage(imageColor, color);
readImage(imageDepth, depth);
// IR image input
if(color.type() == CV_16U)
{
cv::Mat tmp;
color.convertTo(tmp, CV_8U, 0.02);
cv::cvtColor(tmp, color, CV_GRAY2BGR);
}
lock.lock();
this->color = color;
this->depth = depth;
updateImage = true;
updateCloud = true;
lock.unlock();
}
void imageViewer()
{
cv::Mat color, depth, depthDisp, combined;
std::chrono::time_point start, now;
double fps = 0;
size_t frameCount = 0;
std::ostringstream oss;
const cv::Point pos(5, 15);
const cv::Scalar colorText = CV_RGB(255, 255, 255);
const double sizeText = 0.5;
const int lineText = 1;
const int font = cv::FONT_HERSHEY_SIMPLEX;
cv::namedWindow("Image Viewer");
oss << "starting...";
start = std::chrono::high_resolution_clock::now();
for(; running && ros::ok();)
{
if(updateImage)
{
lock.lock();
color = this->color;
depth = this->depth;
updateImage = false;
lock.unlock();
++frameCount;
now = std::chrono::high_resolution_clock::now();
double elapsed = std::chrono::duration_cast(now - start).count() / 1000.0;
if(elapsed >= 1.0)
{
fps = frameCount / elapsed;
oss.str("");
oss << "fps: " << fps << " ( " << elapsed / frameCount * 1000.0 << " ms)";
start = now;
frameCount = 0;
}
dispDepth(depth, depthDisp, 12000.0f);
combine(color, depthDisp, combined);
//combined = color;
cv::putText(combined, oss.str(), pos, font, sizeText, colorText, lineText, CV_AA);
cv::imshow("Image Viewer", combined);
}
int key = cv::waitKey(1);
switch(key & 0xFF)
{
case 27:
case 'q':
running = false;
break;
case 'b': save_seq = true; save = true; break;
case 'e': save_seq = false; save = false; break;
case ' ':
case 's':
if (save_seq) break;
if(mode == IMAGE)
{
createCloud(depth, color, cloud);
saveCloudAndImages(cloud, color, depth, depthDisp);
}
else
{
save = true;
}
break;
}
if (save_seq) {
createCloud(depth, color, cloud);
saveCloudAndImages(cloud, color, depth, depthDisp);
}
}
cv::destroyAllWindows();
cv::waitKey(100);
}
void cloudViewer()
{
cv::Mat color, depth;
pcl::visualization::PCLVisualizer::Ptr visualizer(new pcl::visualization::PCLVisualizer("Cloud Viewer"));
const std::string cloudName = "rendered";
lock.lock();
color = this->color;
depth = this->depth;
updateCloud = false;
lock.unlock();
createCloud(depth, color, cloud);
visualizer->addPointCloud(cloud, cloudName);
visualizer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, cloudName);
visualizer->initCameraParameters();
visualizer->setBackgroundColor(0, 0, 0);
visualizer->setPosition(mode == BOTH ? color.cols : 0, 0);
visualizer->setSize(color.cols, color.rows);
visualizer->setShowFPS(true);
visualizer->setCameraPosition(0, 0, 0, 0, -1, 0);
visualizer->registerKeyboardCallback(&Receiver::keyboardEvent, *this);
for(; running && ros::ok();)
{
if(updateCloud)
{
lock.lock();
color = this->color;
depth = this->depth;
updateCloud = false;
lock.unlock();
createCloud(depth, color, cloud);
visualizer->updatePointCloud(cloud, cloudName);
}
if(save||save_seq)
{
save = false;
cv::Mat depthDisp;
dispDepth(depth, depthDisp, 12000.0f);
saveCloudAndImages(cloud, color, depth, depthDisp);
}
visualizer->spinOnce(10);
}
visualizer->close();
}
void keyboardEvent(const pcl::visualization::KeyboardEvent &event, void *)
{
if(event.keyUp())
{
switch(event.getKeyCode())
{
case 27:
case 'q':
running = false;
break;
case ' ':
case 's':
save = true;
break;
case 'b':
save_seq = true;
break;
case 'e':
save_seq = false;
break;
}
}
}
void readImage(const sensor_msgs::Image::ConstPtr msgImage, cv::Mat &image) const
{
cv_bridge::CvImageConstPtr pCvImage;
pCvImage = cv_bridge::toCvShare(msgImage, msgImage->encoding);
pCvImage->image.copyTo(image);
}
void readCameraInfo(const sensor_msgs::CameraInfo::ConstPtr cameraInfo, cv::Mat &cameraMatrix) const
{
double *itC = cameraMatrix.ptr(0, 0);
for(size_t i = 0; i < 9; ++i, ++itC)
{
*itC = cameraInfo->K[i];
}
}
void dispDepth(const cv::Mat &in, cv::Mat &out, const float maxValue)
{
cv::Mat tmp = cv::Mat(in.rows, in.cols, CV_8U);
const uint32_t maxInt = 255;
#pragma omp parallel for
for(int r = 0; r < in.rows; ++r)
{
const uint16_t *itI = in.ptr(r);
uint8_t *itO = tmp.ptr(r);
for(int c = 0; c < in.cols; ++c, ++itI, ++itO)
{
*itO = (uint8_t)std::min((*itI * maxInt / maxValue), 255.0f);
}
}
cv::applyColorMap(tmp, out, cv::COLORMAP_JET);
}
void combine(const cv::Mat &inC, const cv::Mat &inD, cv::Mat &out)
{
out = cv::Mat(inC.rows, inC.cols, CV_8UC3);
#pragma omp parallel for
for(int r = 0; r < inC.rows; ++r)
{
const cv::Vec3b
*itC = inC.ptr(r),
*itD = inD.ptr(r);
cv::Vec3b *itO = out.ptr(r);
for(int c = 0; c < inC.cols; ++c, ++itC, ++itD, ++itO)
{
itO->val[0] = (itC->val[0] + itD->val[0]) >> 1;
itO->val[1] = (itC->val[1] + itD->val[1]) >> 1;
itO->val[2] = (itC->val[2] + itD->val[2]) >> 1;
}
}
}
void createCloud(const cv::Mat &depth, const cv::Mat &color, pcl::PointCloud::Ptr &cloud) const
{
const float badPoint = std::numeric_limits::quiet_NaN();
#pragma omp parallel for
for(int r = 0; r < depth.rows; ++r)
{
pcl::PointXYZRGBA *itP = &cloud->points[r * depth.cols];
const uint16_t *itD = depth.ptr(r);
const cv::Vec3b *itC = color.ptr(r);
const float y = lookupY.at(0, r);
const float *itX = lookupX.ptr();
for(size_t c = 0; c < (size_t)depth.cols; ++c, ++itP, ++itD, ++itC, ++itX)
{
register const float depthValue = *itD / 1000.0f;
// Check for invalid measurements
if(*itD == 0)
{
// not valid
itP->x = itP->y = itP->z = badPoint;
itP->rgba = 0;
continue;
}
itP->z = depthValue;
itP->x = *itX * depthValue;
itP->y = y * depthValue;
itP->b = itC->val[0];
itP->g = itC->val[1];
itP->r = itC->val[2];
itP->a = 255;
}
}
}
void saveCloudAndImages(const pcl::PointCloud::ConstPtr cloud, const cv::Mat &color, const cv::Mat &depth, const cv::Mat &depthColored)
{
oss.str("");
oss << "./" << std::setfill('0') << std::setw(4) << frame;
const std::string baseName = oss.str();
const std::string cloudName = baseName + "_cloud.pcd";
const std::string colorName = baseName + "_color.jpg";
const std::string depthName = baseName + "_depth.png";
const std::string depthColoredName = baseName + "_depth_colored.png";
OUT_INFO("saving cloud: " << cloudName);
writer.writeBinary(cloudName, *cloud);
OUT_INFO("saving color: " << colorName);
cv::imwrite(colorName, color, params);
OUT_INFO("saving depth: " << depthName);
cv::imwrite(depthName, depth, params);
OUT_INFO("saving depth: " << depthColoredName);
cv::imwrite(depthColoredName, depthColored, params);
OUT_INFO("saving complete!");
++frame;
}
void createLookup(size_t width, size_t height)
{
const float fx = 1.0f / cameraMatrixColor.at(0, 0);
const float fy = 1.0f / cameraMatrixColor.at(1, 1);
const float cx = cameraMatrixColor.at(0, 2);
const float cy = cameraMatrixColor.at(1, 2);
float *it;
lookupY = cv::Mat(1, height, CV_32F);
it = lookupY.ptr();
for(size_t r = 0; r < height; ++r, ++it)
{
*it = (r - cy) * fy;
}
lookupX = cv::Mat(1, width, CV_32F);
it = lookupX.ptr();
for(size_t c = 0; c < width; ++c, ++it)
{
*it = (c - cx) * fx;
}
}
};
void help(const std::string &path)
{
std::cout << path << FG_BLUE " [options]" << std::endl
<< FG_GREEN " name" NO_COLOR ": " FG_YELLOW "'any string'" NO_COLOR " equals to the kinect2_bridge topic base name" << std::endl
<< FG_GREEN " mode" NO_COLOR ": " FG_YELLOW "'qhd'" NO_COLOR ", " FG_YELLOW "'hd'" NO_COLOR ", " FG_YELLOW "'sd'" NO_COLOR " or " FG_YELLOW "'ir'" << std::endl
<< FG_GREEN " visualization" NO_COLOR ": " FG_YELLOW "'image'" NO_COLOR ", " FG_YELLOW "'cloud'" NO_COLOR " or " FG_YELLOW "'both'" << std::endl
<< FG_GREEN " options" NO_COLOR ":" << std::endl
<< FG_YELLOW " 'compressed'" NO_COLOR " use compressed instead of raw topics" << std::endl
<< FG_YELLOW " 'approx'" NO_COLOR " use approximate time synchronization" << std::endl;
}
int main(int argc, char **argv)
{
#if EXTENDED_OUTPUT
ROSCONSOLE_AUTOINIT;
if(!getenv("ROSCONSOLE_FORMAT"))
{
ros::console::g_formatter.tokens_.clear();
ros::console::g_formatter.init("[${severity}] ${message}");
}
#endif
ros::init(argc, argv, "kinect2_viewer", ros::init_options::AnonymousName);
if(!ros::ok())
{
return 0;
}
std::string ns = K2_DEFAULT_NS;
std::string topicColor = K2_TOPIC_QHD K2_TOPIC_IMAGE_COLOR K2_TOPIC_IMAGE_RECT;
std::string topicDepth = K2_TOPIC_QHD K2_TOPIC_IMAGE_DEPTH K2_TOPIC_IMAGE_RECT;
bool useExact = true;
bool useCompressed = false;
Receiver::Mode mode = Receiver::CLOUD;
for(size_t i = 1; i < (size_t)argc; ++i)
{
std::string param(argv[i]);
if(param == "-h" || param == "--help" || param == "-?" || param == "--?")
{
help(argv[0]);
ros::shutdown();
return 0;
}
else if(param == "qhd")
{
topicColor = K2_TOPIC_QHD K2_TOPIC_IMAGE_COLOR K2_TOPIC_IMAGE_RECT;
topicDepth = K2_TOPIC_QHD K2_TOPIC_IMAGE_DEPTH K2_TOPIC_IMAGE_RECT;
}
else if(param == "hd")
{
topicColor = K2_TOPIC_HD K2_TOPIC_IMAGE_COLOR K2_TOPIC_IMAGE_RECT;
topicDepth = K2_TOPIC_HD K2_TOPIC_IMAGE_DEPTH K2_TOPIC_IMAGE_RECT;
}
else if(param == "ir")
{
topicColor = K2_TOPIC_SD K2_TOPIC_IMAGE_IR K2_TOPIC_IMAGE_RECT;
topicDepth = K2_TOPIC_SD K2_TOPIC_IMAGE_DEPTH K2_TOPIC_IMAGE_RECT;
}
else if(param == "sd")
{
topicColor = K2_TOPIC_SD K2_TOPIC_IMAGE_COLOR K2_TOPIC_IMAGE_RECT;
topicDepth = K2_TOPIC_SD K2_TOPIC_IMAGE_DEPTH K2_TOPIC_IMAGE_RECT;
}
else if(param == "approx")
{
useExact = false;
}
else if(param == "compressed")
{
useCompressed = true;
}
else if(param == "image")
{
mode = Receiver::IMAGE;
}
else if(param == "cloud")
{
mode = Receiver::CLOUD;
}
else if(param == "both")
{
mode = Receiver::BOTH;
}
else
{
ns = param;
}
}
topicColor = "/" + ns + topicColor;
topicDepth = "/" + ns + topicDepth;
OUT_INFO("topic color: " FG_CYAN << topicColor << NO_COLOR);
OUT_INFO("topic depth: " FG_CYAN << topicDepth << NO_COLOR);
Receiver receiver(topicColor, topicDepth, useExact, useCompressed);
OUT_INFO("starting receiver...");
receiver.run(mode);
ros::shutdown();
return 0;
}
所在路径:/home/junwang/catkin_ws/src/iai_kinect2/kinect2_viewer
在CMakeList.txt的最后, 添加如下指令:
add_executable(save_seq src/save_seq.cpp)
target_link_libraries(save_seq
${catkin_LIBRARIES}
${OpenCV_LIBRARIES}
${PCL_LIBRARIES}
${kinect2_bridge_LIBRARIES}
)
参考:https://blog.csdn.net/sunbibei/article/details/51594824#保存图片
初次修改完需要先编译一下
cd ~/catkin_ws/
catkin_make
之后运行
cd ~/catkin_ws/
roslaunch kinect2_bridge kinect2_bridge.launch
再打开一个新的终端,运行
rosrun kinect2_viewer save_seq hd cloud