//.................................................................................
//采用QHD分辨率使用kinect2_calibration,完成QHD图像校正
参考“http://www.bubuko.com/infodetail-2151412.html”
1.修改kinect2_calibration.cpp文件如下:
a)K2_TOPIC_HD 改为 K2_TOPIC_QHD
b)把文件中涉及到分辨率1920x1080都改成960x540,这一步非常重要,不然校正出来的内参还是按照HD分辨率进行校正
我的文件如下:
/** * Copyright 2014 University of Bremen, Institute for Artificial Intelligence * Author: Thiemo Wiedemeyer <[email protected]> * * 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 <stdlib.h> #include <stdio.h> #include <iostream> #include <sstream> #include <fstream> #include <string> #include <vector> #include <mutex> #include <thread> #include <dirent.h> #include <sys/stat.h> #include <opencv2/opencv.hpp> #include <ros/ros.h> #include <ros/spinner.h> #include <sensor_msgs/CameraInfo.h> #include <sensor_msgs/Image.h> #include <cv_bridge/cv_bridge.h> #include <image_transport/image_transport.h> #include <image_transport/subscriber_filter.h> #include <message_filters/subscriber.h> #include <message_filters/synchronizer.h> #include <message_filters/sync_policies/exact_time.h> #include <kinect2_calibration/kinect2_calibration_definitions.h> #include <kinect2_bridge/kinect2_definitions.h> enum Mode { RECORD, CALIBRATE }; enum Source { COLOR, IR, SYNC }; class Recorder { private: const bool circleBoard; int circleFlags; const cv::Size boardDims; const float boardSize; const Source mode; const std::string path; const std::string topicColor, topicIr, topicDepth; std::mutex lock; bool update; bool foundColor, foundIr; cv::Mat color, ir, irGrey, depth; size_t frame; std::vector<int> params; std::vector<cv::Point3f> board; std::vector<cv::Point2f> pointsColor, pointsIr; typedef message_filters::sync_policies::ExactTime<sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image> ColorIrDepthSyncPolicy; ros::NodeHandle nh; ros::AsyncSpinner spinner; image_transport::ImageTransport it; image_transport::SubscriberFilter *subImageColor, *subImageIr, *subImageDepth; message_filters::Synchronizer<ColorIrDepthSyncPolicy> *sync; int minIr, maxIr; cv::Ptr<cv::CLAHE> clahe; public: Recorder(const std::string &path, const std::string &topicColor, const std::string &topicIr, const std::string &topicDepth, const Source mode, const bool circleBoard, const bool symmetric, const cv::Size &boardDims, const float boardSize) : circleBoard(circleBoard), boardDims(boardDims), boardSize(boardSize), mode(mode), path(path), topicColor(topicColor), topicIr(topicIr), topicDepth(topicDepth), update(false), foundColor(false), foundIr(false), frame(0), nh("~"), spinner(0), it(nh), minIr(0), maxIr(0x7FFF) { if(symmetric) { circleFlags = cv::CALIB_CB_SYMMETRIC_GRID + cv::CALIB_CB_CLUSTERING; } else { circleFlags = cv::CALIB_CB_ASYMMETRIC_GRID + cv::CALIB_CB_CLUSTERING; } params.push_back(CV_IMWRITE_PNG_COMPRESSION); params.push_back(9); board.resize(boardDims.width * boardDims.height); for(size_t r = 0, i = 0; r < (size_t)boardDims.height; ++r) { for(size_t c = 0; c < (size_t)boardDims.width; ++c, ++i) { board[i] = cv::Point3f(c * boardSize, r * boardSize, 0); } } clahe = cv::createCLAHE(1.5, cv::Size(32, 32)); } ~Recorder() { } void run() { startRecord(); display(); stopRecord(); } private: void startRecord() { OUT_INFO("Controls:" << std::endl << FG_YELLOW " [ESC, q]" NO_COLOR " - Exit" << std::endl << FG_YELLOW " [SPACE, s]" NO_COLOR " - Save current frame" << std::endl << FG_YELLOW " [l]" NO_COLOR " - decrease min and max value for IR value range" << std::endl << FG_YELLOW " [h]" NO_COLOR " - increase min and max value for IR value range" << std::endl << FG_YELLOW " [1]" NO_COLOR " - decrease min value for IR value range" << std::endl << FG_YELLOW " [2]" NO_COLOR " - increase min value for IR value range" << std::endl << FG_YELLOW " [3]" NO_COLOR " - decrease max value for IR value range" << std::endl << FG_YELLOW " [4]" NO_COLOR " - increase max value for IR value range"); image_transport::TransportHints hints("compressed"); subImageColor = new image_transport::SubscriberFilter(it, topicColor, 4, hints); subImageIr = new image_transport::SubscriberFilter(it, topicIr, 4, hints); subImageDepth = new image_transport::SubscriberFilter(it, topicDepth, 4, hints); sync = new message_filters::Synchronizer<ColorIrDepthSyncPolicy>(ColorIrDepthSyncPolicy(4), *subImageColor, *subImageIr, *subImageDepth); sync->registerCallback(boost::bind(&Recorder::callback, this, _1, _2, _3)); spinner.start(); } void stopRecord() { spinner.stop(); delete sync; delete subImageColor; delete subImageIr; delete subImageDepth; } void convertIr(const cv::Mat &ir, cv::Mat &grey) { const float factor = 255.0f / (maxIr - minIr); grey.create(ir.rows, ir.cols, CV_8U); #pragma omp parallel for for(size_t r = 0; r < (size_t)ir.rows; ++r) { const uint16_t *itI = ir.ptr<uint16_t>(r); uint8_t *itO = grey.ptr<uint8_t>(r); for(size_t c = 0; c < (size_t)ir.cols; ++c, ++itI, ++itO) { *itO = std::min(std::max(*itI - minIr, 0) * factor, 255.0f); } } clahe->apply(grey, grey); } void findMinMax(const cv::Mat &ir, const std::vector<cv::Point2f> &pointsIr) { minIr = 0xFFFF; maxIr = 0; for(size_t i = 0; i < pointsIr.size(); ++i) { const cv::Point2f &p = pointsIr[i]; cv::Rect roi(std::max(0, (int)p.x - 2), std::max(0, (int)p.y - 2), 9, 9); roi.width = std::min(roi.width, ir.cols - roi.x); roi.height = std::min(roi.height, ir.rows - roi.y); findMinMax(ir(roi)); } } void findMinMax(const cv::Mat &ir) { for(size_t r = 0; r < (size_t)ir.rows; ++r) { const uint16_t *it = ir.ptr<uint16_t>(r); for(size_t c = 0; c < (size_t)ir.cols; ++c, ++it) { minIr = std::min(minIr, (int) * it); maxIr = std::max(maxIr, (int) * it); } } } void callback(const sensor_msgs::Image::ConstPtr imageColor, const sensor_msgs::Image::ConstPtr imageIr, const sensor_msgs::Image::ConstPtr imageDepth) { std::vector<cv::Point2f> pointsColor, pointsIr; cv::Mat color, ir, irGrey, irScaled, depth; bool foundColor = false; bool foundIr = false; if(mode == COLOR || mode == SYNC) { readImage(imageColor, color); } if(mode == IR || mode == SYNC) { readImage(imageIr, ir); readImage(imageDepth, depth); cv::resize(ir, irScaled, cv::Size(), 2.0, 2.0, cv::INTER_CUBIC); convertIr(irScaled, irGrey); } if(circleBoard) { switch(mode) { case COLOR: foundColor = cv::findCirclesGrid(color, boardDims, pointsColor, circleFlags); break; case IR: foundIr = cv::findCirclesGrid(irGrey, boardDims, pointsIr, circleFlags); break; case SYNC: foundColor = cv::findCirclesGrid(color, boardDims, pointsColor, circleFlags); foundIr = cv::findCirclesGrid(irGrey, boardDims, pointsIr, circleFlags); break; } } else { const cv::TermCriteria termCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::COUNT, 100, DBL_EPSILON); switch(mode) { case COLOR: foundColor = cv::findChessboardCorners(color, boardDims, pointsColor, cv::CALIB_CB_FAST_CHECK); break; case IR: foundIr = cv::findChessboardCorners(irGrey, boardDims, pointsIr, cv::CALIB_CB_ADAPTIVE_THRESH); break; case SYNC: foundColor = cv::findChessboardCorners(color, boardDims, pointsColor, cv::CALIB_CB_FAST_CHECK); foundIr = cv::findChessboardCorners(irGrey, boardDims, pointsIr, cv::CALIB_CB_ADAPTIVE_THRESH); break; } if(foundColor) { cv::cornerSubPix(color, pointsColor, cv::Size(11, 11), cv::Size(-1, -1), termCriteria); } if(foundIr) { cv::cornerSubPix(irGrey, pointsIr, cv::Size(11, 11), cv::Size(-1, -1), termCriteria); } } if(foundIr) { // Update min and max ir value based on checkerboard values findMinMax(irScaled, pointsIr); } lock.lock(); this->color = color; this->ir = ir; this->irGrey = irGrey; this->depth = depth; this->foundColor = foundColor; this->foundIr = foundIr; this->pointsColor = pointsColor; this->pointsIr = pointsIr; update = true; lock.unlock(); } void display() { std::vector<cv::Point2f> pointsColor, pointsIr; cv::Mat color, ir, irGrey, depth; cv::Mat colorDisp, irDisp; bool foundColor = false; bool foundIr = false; bool save = false; bool running = true; std::chrono::milliseconds duration(1); while(!update && ros::ok()) { std::this_thread::sleep_for(duration); } for(; ros::ok() && running;) { if(update) { lock.lock(); color = this->color; ir = this->ir; irGrey = this->irGrey; depth = this->depth; foundColor = this->foundColor; foundIr = this->foundIr; pointsColor = this->pointsColor; pointsIr = this->pointsIr; update = false; lock.unlock(); if(mode == COLOR || mode == SYNC) { cv::cvtColor(color, colorDisp, CV_GRAY2BGR); cv::drawChessboardCorners(colorDisp, boardDims, pointsColor, foundColor); //cv::resize(colorDisp, colorDisp, cv::Size(), 0.5, 0.5); //cv::flip(colorDisp, colorDisp, 1); } if(mode == IR || mode == SYNC) { cv::cvtColor(irGrey, irDisp, CV_GRAY2BGR); cv::drawChessboardCorners(irDisp, boardDims, pointsIr, foundIr); //cv::resize(irDisp, irDisp, cv::Size(), 0.5, 0.5); //cv::flip(irDisp, irDisp, 1); } } switch(mode) { case COLOR: cv::imshow("color", colorDisp); break; case IR: cv::imshow("ir", irDisp); break; case SYNC: cv::imshow("color", colorDisp); cv::imshow("ir", irDisp); break; } int key = cv::waitKey(10); switch(key & 0xFF) { case ‘ ‘: case ‘s‘: save = true; break; case 27: case ‘q‘: running = false; break; case ‘1‘: minIr = std::max(0, minIr - 100); break; case ‘2‘: minIr = std::min(maxIr - 1, minIr + 100); break; case ‘3‘: maxIr = std::max(minIr + 1, maxIr - 100); break; case ‘4‘: maxIr = std::min(0xFFFF, maxIr + 100); break; case ‘l‘: minIr = std::max(0, minIr - 100); maxIr = std::max(minIr + 1, maxIr - 100); break; case ‘h‘: maxIr = std::min(0x7FFF, maxIr + 100); minIr = std::min(maxIr - 1, minIr + 100); break; } if(save && ((mode == COLOR && foundColor) || (mode == IR && foundIr) || (mode == SYNC && foundColor && foundIr))) { store(color, ir, irGrey, depth, pointsColor, pointsIr); save = false; } } cv::destroyAllWindows(); cv::waitKey(100); } 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 store(const cv::Mat &color, const cv::Mat &ir, const cv::Mat &irGrey, const cv::Mat &depth, const std::vector<cv::Point2f> &pointsColor, std::vector<cv::Point2f> &pointsIr) { std::ostringstream oss; oss << std::setfill(‘0‘) << std::setw(4) << frame++; const std::string frameNumber(oss.str()); OUT_INFO("storing frame: " << frameNumber); std::string base = path + frameNumber; for(size_t i = 0; i < pointsIr.size(); ++i) { pointsIr[i].x /= 2.0; pointsIr[i].y /= 2.0; } if(mode == SYNC) { base += CALIB_SYNC; } if(mode == COLOR || mode == SYNC) { cv::imwrite(base + CALIB_FILE_COLOR, color, params); cv::FileStorage file(base + CALIB_POINTS_COLOR, cv::FileStorage::WRITE); file << "points" << pointsColor; } if(mode == IR || mode == SYNC) { cv::imwrite(base + CALIB_FILE_IR, ir, params); cv::imwrite(base + CALIB_FILE_IR_GREY, irGrey, params); cv::imwrite(base + CALIB_FILE_DEPTH, depth, params); cv::FileStorage file(base + CALIB_POINTS_IR, cv::FileStorage::WRITE); file << "points" << pointsIr; } } }; class CameraCalibration { private: const bool circleBoard; const cv::Size boardDims; const float boardSize; const int flags; const Source mode; const std::string path; std::vector<cv::Point3f> board; std::vector<std::vector<cv::Point3f> > pointsBoard; std::vector<std::vector<cv::Point2f> > pointsColor; std::vector<std::vector<cv::Point2f> > pointsIr; cv::Size sizeColor; cv::Size sizeIr; cv::Mat cameraMatrixColor, distortionColor, rotationColor, translationColor, projectionColor; cv::Mat cameraMatrixIr, distortionIr, rotationIr, translationIr, projectionIr; cv::Mat rotation, translation, essential, fundamental, disparity; std::vector<cv::Mat> rvecsColor, tvecsColor; std::vector<cv::Mat> rvecsIr, tvecsIr; public: CameraCalibration(const std::string &path, const Source mode, const bool circleBoard, const cv::Size &boardDims, const float boardSize, const bool rational) : circleBoard(circleBoard), boardDims(boardDims), boardSize(boardSize), flags(rational ? cv::CALIB_RATIONAL_MODEL : 0), mode(mode), path(path), sizeColor(1920, 1080), sizeIr(512, 424) { board.resize(boardDims.width * boardDims.height); for(size_t r = 0, i = 0; r < (size_t)boardDims.height; ++r) { for(size_t c = 0; c < (size_t)boardDims.width; ++c, ++i) { board[i] = cv::Point3f(c * boardSize, r * boardSize, 0); } } } ~CameraCalibration() { } bool restore() { std::vector<std::string> filesSync; std::vector<std::string> filesColor; std::vector<std::string> filesIr; DIR *dp; struct dirent *dirp; size_t posColor, posIr, posSync; if((dp = opendir(path.c_str())) == NULL) { OUT_ERROR("Error opening: " << path); return false; } while((dirp = readdir(dp)) != NULL) { std::string filename = dirp->d_name; if(dirp->d_type != DT_REG) { continue; } posSync = filename.rfind(CALIB_SYNC); posColor = filename.rfind(CALIB_FILE_COLOR); if(posSync != std::string::npos) { if(posColor != std::string::npos) { std::string frameName = filename.substr(0, posColor); filesSync.push_back(frameName); filesColor.push_back(frameName); filesIr.push_back(frameName); } continue; } if(posColor != std::string::npos) { std::string frameName = filename.substr(0, posColor); filesColor.push_back(frameName); continue; } posIr = filename.rfind(CALIB_FILE_IR_GREY); if(posIr != std::string::npos) { std::string frameName = filename.substr(0, posIr); filesIr.push_back(frameName); continue; } } closedir(dp); std::sort(filesColor.begin(), filesColor.end()); std::sort(filesIr.begin(), filesIr.end()); std::sort(filesSync.begin(), filesSync.end()); bool ret = true; switch(mode) { case COLOR: if(filesColor.empty()) { OUT_ERROR("no files found!"); return false; } pointsColor.resize(filesColor.size()); pointsBoard.resize(filesColor.size(), board); ret = ret && readFiles(filesColor, CALIB_POINTS_COLOR, pointsColor); break; case IR: if(filesIr.empty()) { OUT_ERROR("no files found!"); return false; } pointsIr.resize(filesIr.size()); pointsBoard.resize(filesIr.size(), board); ret = ret && readFiles(filesIr, CALIB_POINTS_IR, pointsIr); break; case SYNC: if(filesColor.empty() || filesIr.empty()) { OUT_ERROR("no files found!"); return false; } pointsColor.resize(filesColor.size()); pointsIr.resize(filesSync.size()); pointsColor.resize(filesSync.size()); pointsBoard.resize(filesSync.size(), board); ret = ret && readFiles(filesSync, CALIB_POINTS_COLOR, pointsColor); ret = ret && readFiles(filesSync, CALIB_POINTS_IR, pointsIr); ret = ret && checkSyncPointsOrder(); ret = ret && loadCalibration(); break; } return ret; } void calibrate() { switch(mode) { case COLOR: calibrateIntrinsics(sizeColor, pointsBoard, pointsColor, cameraMatrixColor, distortionColor, rotationColor, projectionColor, rvecsColor, tvecsColor); break; case IR: calibrateIntrinsics(sizeIr, pointsBoard, pointsIr, cameraMatrixIr, distortionIr, rotationIr, projectionIr, rvecsIr, tvecsIr); break; case SYNC: calibrateExtrinsics(); break; } storeCalibration(); } private: bool readFiles(const std::vector<std::string> &files, const std::string &ext, std::vector<std::vector<cv::Point2f> > &points) const { bool ret = true; #pragma omp parallel for for(size_t i = 0; i < files.size(); ++i) { std::string pointsname = path + files[i] + ext; #pragma omp critical OUT_INFO("restoring file: " << files[i] << ext); cv::FileStorage file(pointsname, cv::FileStorage::READ); if(!file.isOpened()) { #pragma omp critical { ret = false; OUT_ERROR("couldn‘t open file: " << files[i] << ext); } } else { file["points"] >> points[i]; } } return ret; } bool checkSyncPointsOrder() { if(pointsColor.size() != pointsIr.size()) { OUT_ERROR("number of detected color and ir patterns does not match!"); return false; } for(size_t i = 0; i < pointsColor.size(); ++i) { const std::vector<cv::Point2f> &pColor = pointsColor[i]; const std::vector<cv::Point2f> &pIr = pointsIr[i]; if(pColor.front().y > pColor.back().y || pColor.front().x > pColor.back().x) { std::reverse(pointsColor[i].begin(), pointsColor[i].end()); } if(pIr.front().y > pIr.back().y || pIr.front().x > pIr.back().x) { std::reverse(pointsIr[i].begin(), pointsIr[i].end()); } } return true; } void calibrateIntrinsics(const cv::Size &size, const std::vector<std::vector<cv::Point3f> > &pointsBoard, const std::vector<std::vector<cv::Point2f> > &points, cv::Mat &cameraMatrix, cv::Mat &distortion, cv::Mat &rotation, cv::Mat &projection, std::vector<cv::Mat> &rvecs, std::vector<cv::Mat> &tvecs) { if(points.empty()) { OUT_ERROR("no data for calibration provided!"); return; } const cv::TermCriteria termCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 50, DBL_EPSILON); double error; OUT_INFO("calibrating intrinsics..."); error = cv::calibrateCamera(pointsBoard, points, size, cameraMatrix, distortion, rvecs, tvecs, flags, termCriteria); OUT_INFO("re-projection error: " << error << std::endl); OUT_INFO("Camera Matrix:" << std::endl << cameraMatrix); OUT_INFO("Distortion Coeeficients:" << std::endl << distortion << std::endl); rotation = cv::Mat::eye(3, 3, CV_64F); projection = cv::Mat::eye(4, 4, CV_64F); cameraMatrix.copyTo(projection(cv::Rect(0, 0, 3, 3))); } void calibrateExtrinsics() { if(pointsColor.size() != pointsIr.size()) { OUT_ERROR("number of detected color and ir patterns does not match!"); return; } if(pointsColor.empty() || pointsIr.empty()) { OUT_ERROR("no data for calibration provided!"); return; } const cv::TermCriteria termCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 100, DBL_EPSILON); double error; OUT_INFO("Camera Matrix Color:" << std::endl << cameraMatrixColor); OUT_INFO("Distortion Coeeficients Color:" << std::endl << distortionColor << std::endl); OUT_INFO("Camera Matrix Ir:" << std::endl << cameraMatrixIr); OUT_INFO("Distortion Coeeficients Ir:" << std::endl << distortionIr << std::endl); OUT_INFO("calibrating Color and Ir extrinsics..."); #if CV_MAJOR_VERSION == 2 error = cv::stereoCalibrate(pointsBoard, pointsIr, pointsColor, cameraMatrixIr, distortionIr, cameraMatrixColor, distortionColor, sizeColor, rotation, translation, essential, fundamental, termCriteria, cv::CALIB_FIX_INTRINSIC); #elif CV_MAJOR_VERSION == 3 error = cv::stereoCalibrate(pointsBoard, pointsIr, pointsColor, cameraMatrixIr, distortionIr, cameraMatrixColor, distortionColor, sizeColor, rotation, translation, essential, fundamental, cv::CALIB_FIX_INTRINSIC, termCriteria); #endif OUT_INFO("re-projection error: " << error << std::endl); OUT_INFO("Rotation:" << std::endl << rotation); OUT_INFO("Translation:" << std::endl << translation); OUT_INFO("Essential:" << std::endl << essential); OUT_INFO("Fundamental:" << std::endl << fundamental << std::endl); } void storeCalibration() { cv::FileStorage fs; switch(mode) { case SYNC: fs.open(path + K2_CALIB_POSE, cv::FileStorage::WRITE); break; case COLOR: fs.open(path + K2_CALIB_COLOR, cv::FileStorage::WRITE); break; case IR: fs.open(path + K2_CALIB_IR, cv::FileStorage::WRITE); break; } if(!fs.isOpened()) { OUT_ERROR("couldn‘t store calibration data!"); return; } switch(mode) { case SYNC: fs << K2_CALIB_ROTATION << rotation; fs << K2_CALIB_TRANSLATION << translation; fs << K2_CALIB_ESSENTIAL << essential; fs << K2_CALIB_FUNDAMENTAL << fundamental; break; case COLOR: fs << K2_CALIB_CAMERA_MATRIX << cameraMatrixColor; fs << K2_CALIB_DISTORTION << distortionColor; fs << K2_CALIB_ROTATION << rotationColor; fs << K2_CALIB_PROJECTION << projectionColor; break; case IR: fs << K2_CALIB_CAMERA_MATRIX << cameraMatrixIr; fs << K2_CALIB_DISTORTION << distortionIr; fs << K2_CALIB_ROTATION << rotationIr; fs << K2_CALIB_PROJECTION << projectionIr; break; } fs.release(); } bool loadCalibration() { cv::FileStorage fs; if(fs.open(path + K2_CALIB_COLOR, cv::FileStorage::READ)) { fs[K2_CALIB_CAMERA_MATRIX] >> cameraMatrixColor; fs[K2_CALIB_DISTORTION] >> distortionColor; fs[K2_CALIB_ROTATION] >> rotationColor; fs[K2_CALIB_PROJECTION] >> projectionColor; fs.release(); } else { OUT_ERROR("couldn‘t load color calibration data!"); return false; } if(fs.open(path + K2_CALIB_IR, cv::FileStorage::READ)) { fs[K2_CALIB_CAMERA_MATRIX] >> cameraMatrixIr; fs[K2_CALIB_DISTORTION] >> distortionIr; fs[K2_CALIB_ROTATION] >> rotationIr; fs[K2_CALIB_PROJECTION] >> projectionIr; fs.release(); } else { OUT_ERROR("couldn‘t load ir calibration data!"); return false; } return true; } }; class DepthCalibration { private: const std::string path; std::vector<cv::Point3f> board; std::vector<std::vector<cv::Point2f> > points; std::vector<std::string> images; cv::Size size; cv::Mat cameraMatrix, distortion, rotation, translation; cv::Mat mapX, mapY; double fx, fy, cx, cy; std::ofstream plot; public: DepthCalibration(const std::string &path, const cv::Size &boardDims, const float boardSize) : path(path), size(512, 424) { board.resize(boardDims.width * boardDims.height); for(size_t r = 0, i = 0; r < (size_t)boardDims.height; ++r) { for(size_t c = 0; c < (size_t)boardDims.width; ++c, ++i) { board[i] = cv::Point3f(c * boardSize, r * boardSize, 0); } } } ~DepthCalibration() { } bool restore() { std::vector<std::string> files; DIR *dp; struct dirent *dirp; size_t pos; if((dp = opendir(path.c_str())) == NULL) { OUT_ERROR("Error opening: " << path); return false; } while((dirp = readdir(dp)) != NULL) { std::string filename = dirp->d_name; if(dirp->d_type != DT_REG) { continue; } /*pos = filename.rfind(CALIB_SYNC); if(pos != std::string::npos) { continue; }*/ pos = filename.rfind(CALIB_FILE_IR_GREY); if(pos != std::string::npos) { std::string frameName = filename.substr(0, pos); files.push_back(frameName); continue; } } closedir(dp); std::sort(files.begin(), files.end()); if(files.empty()) { OUT_ERROR("no files found!"); return false; } bool ret = readFiles(files); ret = ret && loadCalibration(); if(ret) { cv::initUndistortRectifyMap(cameraMatrix, distortion, cv::Mat(), cameraMatrix, size, CV_32FC1, mapX, mapY); fx = cameraMatrix.at<double>(0, 0); fy = cameraMatrix.at<double>(1, 1); cx = cameraMatrix.at<double>(0, 2); cy = cameraMatrix.at<double>(1, 2); } return ret; } void calibrate() { plot.open(path + "plot.dat", std::ios_base::trunc); if(!plot.is_open()) { OUT_ERROR("couldn‘t open ‘plot.dat‘!"); return; } if(images.empty()) { OUT_ERROR("no images found!"); return; } plot << "# Columns:" << std::endl << "# 1: X" << std::endl << "# 2: Y" << std::endl << "# 3: computed depth" << std::endl << "# 4: measured depth" << std::endl << "# 5: difference between computed and measured depth" << std::endl; std::vector<double> depthDists, imageDists; for(size_t i = 0; i < images.size(); ++i) { OUT_INFO("frame: " << images[i]); plot << "# frame: " << images[i] << std::endl; cv::Mat depth, planeNormal, region; double planeDistance; cv::Rect roi; depth = cv::imread(images[i], cv::IMREAD_ANYDEPTH); if(depth.empty()) { OUT_ERROR("couldn‘t load image ‘" << images[i] << "‘!"); return; } cv::remap(depth, depth, mapX, mapY, cv::INTER_NEAREST); computeROI(depth, points[i], region, roi); getPlane(i, planeNormal, planeDistance); computePointDists(planeNormal, planeDistance, region, roi, depthDists, imageDists); } compareDists(imageDists, depthDists); } private: void compareDists(const std::vector<double> &imageDists, const std::vector<double> &depthDists) const { if(imageDists.size() != depthDists.size()) { OUT_ERROR("number of real and computed distance samples does not match!"); return; } if(imageDists.empty() || depthDists.empty()) { OUT_ERROR("no distance sample data!"); return; } double avg = 0, sqavg = 0, var = 0, stddev = 0; std::vector<double> diffs(imageDists.size()); for(size_t i = 0; i < imageDists.size(); ++i) { diffs[i] = imageDists[i] - depthDists[i]; avg += diffs[i]; sqavg += diffs[i] * diffs[i]; } sqavg = sqrt(sqavg / imageDists.size()); avg /= imageDists.size(); for(size_t i = 0; i < imageDists.size(); ++i) { const double diff = diffs[i] - avg; var += diff * diff; } var = var / (imageDists.size()); stddev = sqrt(var); std::sort(diffs.begin(), diffs.end()); OUT_INFO("stats on difference:" << std::endl << " avg: " << avg << std::endl << " var: " << var << std::endl << " stddev: " << stddev << std::endl << " rms: " << sqavg << std::endl << " median: " << diffs[diffs.size() / 2]); storeCalibration(avg * 1000.0); } void computePointDists(const cv::Mat &normal, const double distance, const cv::Mat ®ion, const cv::Rect &roi, std::vector<double> &depthDists, std::vector<double> &imageDists) { for(int r = 0; r < region.rows; ++r) { const uint16_t *itD = region.ptr<uint16_t>(r); cv::Point p(roi.x, roi.y + r); for(int c = 0; c < region.cols; ++c, ++itD, ++p.x) { const double dDist = *itD / 1000.0; if(dDist < 0.1) { continue; } const double iDist = computeDistance(p, normal, distance); const double diff = iDist - dDist; if(std::abs(diff) > 0.08) { continue; } depthDists.push_back(dDist); imageDists.push_back(iDist); plot << p.x << ‘ ‘ << p.y << ‘ ‘ << iDist << ‘ ‘ << dDist << ‘ ‘ << diff << std::endl; } } } double computeDistance(const cv::Point &pointImage, const cv::Mat &normal, const double distance) const { cv::Mat point = cv::Mat(3, 1, CV_64F); point.at<double>(0) = (pointImage.x - cx) / fx; point.at<double>(1) = (pointImage.y - cy) / fy; point.at<double>(2) = 1; double t = distance / normal.dot(point); point = point * t; return point.at<double>(2); } void getPlane(const size_t index, cv::Mat &normal, double &distance) const { cv::Mat rvec, rotation, translation; //cv::solvePnP(board, points[index], cameraMatrix, distortion, rvec, translation, false, cv::EPNP); #if CV_MAJOR_VERSION == 2 cv::solvePnPRansac(board, points[index], cameraMatrix, distortion, rvec, translation, false, 300, 0.05, board.size(), cv::noArray(), cv::ITERATIVE); #elif CV_MAJOR_VERSION == 3 cv::solvePnPRansac(board, points[index], cameraMatrix, distortion, rvec, translation, false, 300, 0.05, 0.99, cv::noArray(), cv::SOLVEPNP_ITERATIVE); #endif cv::Rodrigues(rvec, rotation); normal = cv::Mat(3, 1, CV_64F); normal.at<double>(0) = 0; normal.at<double>(1) = 0; normal.at<double>(2) = 1; normal = rotation * normal; distance = normal.dot(translation); } void computeROI(const cv::Mat &depth, const std::vector<cv::Point2f> &points, cv::Mat ®ion, cv::Rect &roi) const { std::vector<cv::Point2f> norm; std::vector<cv::Point> undist, hull; cv::undistortPoints(points, norm, cameraMatrix, distortion); undist.reserve(norm.size()); for(size_t i = 0; i < norm.size(); ++i) { cv::Point p; p.x = (int)round(norm[i].x * fx + cx); p.y = (int)round(norm[i].y * fy + cy); if(p.x >= 0 && p.x < depth.cols && p.y >= 0 && p.y < depth.rows) { undist.push_back(p); } } roi = cv::boundingRect(undist); cv::Mat mask = cv::Mat::zeros(depth.rows, depth.cols, CV_8U); cv::convexHull(undist, hull); cv::fillConvexPoly(mask, hull, CV_RGB(255, 255, 255)); cv::Mat tmp; depth.copyTo(tmp, mask); tmp(roi).copyTo(region); } bool readFiles(const std::vector<std::string> &files) { points.resize(files.size()); images.resize(files.size()); bool ret = true; #pragma omp parallel for for(size_t i = 0; i < files.size(); ++i) { std::string pointsname = path + files[i] + CALIB_POINTS_IR; #pragma omp critical OUT_INFO("restoring file: " << files[i]); cv::FileStorage file(pointsname, cv::FileStorage::READ); if(!file.isOpened()) { #pragma omp critical { OUT_ERROR("couldn‘t read ‘" << pointsname << "‘!"); ret = false; } } else { file["points"] >> points[i]; file.release(); images[i] = path + files[i] + CALIB_FILE_DEPTH; } } return ret; } bool loadCalibration() { cv::FileStorage fs; if(fs.open(path + K2_CALIB_IR, cv::FileStorage::READ)) { fs[K2_CALIB_CAMERA_MATRIX] >> cameraMatrix; fs[K2_CALIB_DISTORTION] >> distortion; fs.release(); } else { OUT_ERROR("couldn‘t read calibration ‘" << path + K2_CALIB_IR << "‘!"); return false; } return true; } void storeCalibration(const double depthShift) const { cv::FileStorage fs; if(fs.open(path + K2_CALIB_DEPTH, cv::FileStorage::WRITE)) { fs << K2_CALIB_DEPTH_SHIFT << depthShift; fs.release(); } else { OUT_ERROR("couldn‘t store depth calibration!"); } } }; 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 "‘record‘" NO_COLOR " or " FG_YELLOW "‘calibrate‘" << std::endl << FG_GREEN " source" NO_COLOR ": " FG_YELLOW "‘color‘" NO_COLOR ", " FG_YELLOW "‘ir‘" NO_COLOR ", " FG_YELLOW "‘sync‘" NO_COLOR ", " FG_YELLOW "‘depth‘" << std::endl << FG_GREEN " board" NO_COLOR ":" << std::endl << FG_YELLOW " ‘circle<WIDTH>x<HEIGHT>x<SIZE>‘ " NO_COLOR "for symmetric circle grid" << std::endl << FG_YELLOW " ‘acircle<WIDTH>x<HEIGHT>x<SIZE>‘ " NO_COLOR "for asymmetric circle grid" << std::endl << FG_YELLOW " ‘chess<WIDTH>x<HEIGHT>x<SIZE>‘ " NO_COLOR "for chessboard pattern" << std::endl << FG_GREEN " distortion model" NO_COLOR ": " FG_YELLOW "‘rational‘" NO_COLOR " for using model with 8 instead of 5 coefficients" << std::endl << FG_GREEN " output path" NO_COLOR ": " FG_YELLOW "‘-path <PATH>‘" NO_COLOR << 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 Mode mode = RECORD; Source source = SYNC; bool circleBoard = false; bool symmetric = true; bool rational = false; bool calibDepth = false; cv::Size boardDims = cv::Size(7, 6); float boardSize = 0.108; std::string ns = K2_DEFAULT_NS; std::string path = "./"; ros::init(argc, argv, "kinect2_calib", ros::init_options::AnonymousName); if(!ros::ok()) { return 0; } for(int argI = 1; argI < argc; ++ argI) { std::string arg(argv[argI]); if(arg == "--help" || arg == "--h" || arg == "-h" || arg == "-?" || arg == "--?") { help(argv[0]); ros::shutdown(); return 0; } else if(arg == "record") { mode = RECORD; } else if(arg == "calibrate") { mode = CALIBRATE; } else if(arg == "color") { source = COLOR; } else if(arg == "ir") { source = IR; } else if(arg == "sync") { source = SYNC; } else if(arg == "depth") { calibDepth = true; } else if(arg == "rational") { rational = true; } else if(arg.find("circle") == 0 && arg.find(‘x‘) != arg.rfind(‘x‘) && arg.rfind(‘x‘) != std::string::npos) { circleBoard = true; const size_t start = 6; const size_t leftX = arg.find(‘x‘); const size_t rightX = arg.rfind(‘x‘); const size_t end = arg.size(); int width = atoi(arg.substr(start, leftX - start).c_str()); int height = atoi(arg.substr(leftX + 1, rightX - leftX + 1).c_str()); boardSize = atof(arg.substr(rightX + 1, end - rightX + 1).c_str()); boardDims = cv::Size(width, height); } else if((arg.find("circle") == 0 || arg.find("acircle") == 0) && arg.find(‘x‘) != arg.rfind(‘x‘) && arg.rfind(‘x‘) != std::string::npos) { symmetric = arg.find("circle") == 0; circleBoard = true; const size_t start = 6 + (symmetric ? 0 : 1); const size_t leftX = arg.find(‘x‘); const size_t rightX = arg.rfind(‘x‘); const size_t end = arg.size(); int width = atoi(arg.substr(start, leftX - start).c_str()); int height = atoi(arg.substr(leftX + 1, rightX - leftX + 1).c_str()); boardSize = atof(arg.substr(rightX + 1, end - rightX + 1).c_str()); boardDims = cv::Size(width, height); } else if(arg.find("chess") == 0 && arg.find(‘x‘) != arg.rfind(‘x‘) && arg.rfind(‘x‘) != std::string::npos) { circleBoard = false; const size_t start = 5; const size_t leftX = arg.find(‘x‘); const size_t rightX = arg.rfind(‘x‘); const size_t end = arg.size(); int width = atoi(arg.substr(start, leftX - start).c_str()); int height = atoi(arg.substr(leftX + 1, rightX - leftX + 1).c_str()); boardSize = atof(arg.substr(rightX + 1, end - rightX + 1).c_str()); boardDims = cv::Size(width, height); } else if(arg == "-path" && ++argI < argc) { arg = argv[argI]; struct stat fileStat; if(stat(arg.c_str(), &fileStat) == 0 && S_ISDIR(fileStat.st_mode)) { path = arg; } else { OUT_ERROR("Unknown path: " << arg); help(argv[0]); ros::shutdown(); return 0; } } else { ns = arg; } } std::string topicColor = "/" + ns + K2_TOPIC_QHD + K2_TOPIC_IMAGE_MONO; std::string topicIr = "/" + ns + K2_TOPIC_SD + K2_TOPIC_IMAGE_IR; std::string topicDepth = "/" + ns + K2_TOPIC_SD + K2_TOPIC_IMAGE_DEPTH; OUT_INFO("Start settings:" << std::endl << " Mode: " FG_CYAN << (mode == RECORD ? "record" : "calibrate") << NO_COLOR << std::endl << " Source: " FG_CYAN << (calibDepth ? "depth" : (source == COLOR ? "color" : (source == IR ? "ir" : "sync"))) << NO_COLOR << std::endl << " Board: " FG_CYAN << (circleBoard ? "circles" : "chess") << NO_COLOR << std::endl << " Dimensions: " FG_CYAN << boardDims.width << " x " << boardDims.height << NO_COLOR << std::endl << " Field size: " FG_CYAN << boardSize << NO_COLOR << std::endl << "Dist. model: " FG_CYAN << (rational ? ‘8‘ : ‘5‘) << " coefficients" << NO_COLOR << std::endl << "Topic color: " FG_CYAN << topicColor << NO_COLOR << std::endl << " Topic ir: " FG_CYAN << topicIr << NO_COLOR << std::endl << "Topic depth: " FG_CYAN << topicDepth << NO_COLOR << std::endl << " Path: " FG_CYAN << path << NO_COLOR << std::endl); if(!ros::master::check()) { OUT_ERROR("checking ros master failed."); return -1; } if(mode == RECORD) { Recorder recorder(path, topicColor, topicIr, topicDepth, source, circleBoard, symmetric, boardDims, boardSize); OUT_INFO("starting recorder..."); recorder.run(); OUT_INFO("stopped recording..."); } else if(calibDepth) { DepthCalibration calib(path, boardDims, boardSize); OUT_INFO("restoring files..."); calib.restore(); OUT_INFO("starting calibration..."); calib.calibrate(); } else { CameraCalibration calib(path, source, circleBoard, boardDims, boardSize, rational); OUT_INFO("restoring files..."); calib.restore(); OUT_INFO("starting calibration..."); calib.calibrate(); } return 0; }
2.编译程序
cd ~/catkin_ws
catkin_make -DCMAKE_BUILD_TYPE="Release"
//.................................................................................
....................................................................
时间: 2024-10-14 05:42:35