最近需要利用深度圖做一些東西,就在網上下了一些數據(http://eeeweba.ntu.edu.sg/computervision/people/home/renzhou/HandGesture.htm),該數據集包含了彩色圖及對應的深度圖,但是該數據集沒有以圖像形式存儲深度值,而是用txt文本以行列形式存儲真正的深度值(單位爲mm),所以並不能直觀的看到深度圖像,我需要把這些深度值從txt文本提取出來並把它以圖像的形式呈現出來,網上沒看到現成的解決的方案,所以我只有用現成的輪子自己做一個了。廢話不多說,直接上代碼(代碼很亂,估計也只用這麼一次,所以就沒怎麼注意了。程序的基本流程是:先找到目錄及子目錄下的所有txt文件,再分別讀取txt文件,按行讀取之後再進行字符串分割提取其中的深度值,爲了便於以圖像形式顯示,將深度值歸一化至0~255存入8位單通道的Mat類型數據中,最後以png圖像形式保存至各個目錄):
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <io.h>
#include <direct.h>
#include <fstream>
#include <iostream>
using namespace cv;
using namespace std;
// ******************************************************************
// @refer to [C++文件讀寫操作(二)逐字符讀取文本和逐行讀取文本](http://blog.csdn.net/wangshihui512/article/details/8921924)
// [字符串分割(C++)](http://www.cnblogs.com/MikeZhang/archive/2012/03/24/MySplitFunCPP.html)
// [C++讀取文件夾中所有的文件或者是特定後綴的文件](http://blog.csdn.net/adong76/article/details/39432467)
// [C/C++ 判斷文件夾是否存在以及創建、刪除文件夾 windows以及linux通用](http://blog.csdn.net/u012005313/article/details/50688257)
// [Split a string in C++?](http://stackoverflow.com/questions/236129/split-a-string-in-c)
// [Kinect開發學習筆記之(六)帶遊戲者ID的深度數據的提取](http://blog.csdn.net/zouxy09/article/details/8151044)
// [Depth Map Tutorial](http://www.pages.drexel.edu/~nk752/depthMapTut.html)
// ******************************************************************
// ----- 逐個字符讀取文件 --------
void testByChar()
{
fstream testByCharFile;
char c;
testByCharFile.open("./test.txt",ios::in);
while(!testByCharFile.eof())
{
testByCharFile>>c;
cout<<c;
}
testByCharFile.close();
}
// -------- 逐行讀取文件 -------------------
void testByLine()
{
char buffer[256];
fstream outFile;
outFile.open("./test.txt",ios::in);
while(!outFile.eof())
{
outFile.getline(buffer, 256, '\n');//getline(char *,int,char) 表示該行字符達到256個或遇到換行就結束
cout<<buffer<<endl;
}
outFile.close();
}
// ------- 分割字符串 --------------
void splitString()
{
char buffer[1280];
fstream outFile;
outFile.open("./test.txt",ios::in);
while(!outFile.eof())
{
outFile.getline(buffer, 1280, '\n');//getline(char *,int,char) 表示該行字符達到1280個或遇到換行就結束
cout<<buffer<<endl;
const char *d = " ,*";
char *p;
p = strtok(buffer, d);
while(p)
{
printf("%s\n", p);
p=strtok(NULL, d);
}
}
outFile.close();
}
// 獲取文件夾下指定格式所有文件名
void getAllFormatFiles( string path, string format, vector<string>& files )
{
//文件句柄
long hFile = 0;
//文件信息
struct _finddata_t fileinfo;
string pathName;
if((hFile = _findfirst(pathName.assign(path).append("/*." + format).c_str(),&fileinfo)) != -1)
{
do
{
//如果是目錄,迭代之
//如果不是,加入列表
if((fileinfo.attrib & _A_SUBDIR))
{
if(strcmp(fileinfo.name,".") != 0 && strcmp(fileinfo.name,"..") != 0)
{
//files.push_back(p.assign(path).append("/").append(fileinfo.name) );
getAllFormatFiles( pathName.assign(path).append("/").append(fileinfo.name), format, files);
}
}
else
{
files.push_back(pathName.assign(path).append("/").append(fileinfo.name) );
}
}while(_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}
}
// http://stackoverflow.com/questions/236129/split-a-string-in-c
// ---- stackoverflow上大神的C++版本分割字符串 --------------------
std::vector<std::string> split(const std::string& text, const std::string& delims)
{
std::vector<std::string> tokens;
std::size_t start = text.find_first_not_of(delims), end = 0;
while((end = text.find_first_of(delims, start)) != std::string::npos)
{
tokens.push_back(text.substr(start, end - start));
start = text.find_first_not_of(delims, end);
}
if(start != std::string::npos)
tokens.push_back(text.substr(start));
return tokens;
}
// 創建文件夾及子文件夾
void makeDir(const string &path)
{
std::vector<std::string> tokens;
std::size_t start = 0, end = 0;
while ((end = path.find('/', start)) != std::string::npos)
{
if (end != start)
{
tokens.push_back(path.substr(0, end));
}
start = end + 1;
}
if (end != start)
{
tokens.push_back(path);
}
vector<string>::const_iterator itp = tokens.begin();
while (itp != tokens.end())
{
if (access(itp->c_str(), 0) == -1) // 判斷文件夾是否存在
{
cout<<*itp<<" is not existing"<<endl;
cout<<"now make it"<<endl;
if (mkdir(itp->c_str()) == 0) // 不存在則創建,只能一級一級的創建
{
cout<<"make successfully"<<endl;
}
}
cout << *itp++ <<endl;
}
}
// Txt文件轉opencv Mat(txt文件中存的是以行列形式的深度值)
cv::Mat Txt2DepthMat(const string &txtname)
{
cv::Mat result(480, 640, CV_8UC1, cv::Scalar(0));
char buffer[12800]; // 按行讀取文件
fstream outFile;
const char *d = ","; // 以,爲分割點
char *p; // 分割出的子串
outFile.open(txtname, ios::in);
for (int i = 0; outFile.getline(buffer, 12800, '\n') != NULL && i < result.rows; i++)
{
p = strtok(buffer, d);
for (int j = 0; p && j < result.cols; j++)
{
int realDepth = (atoi(p) & 0xfff8) >> 3; //提取距離信息,高13位
int depth = (int)(256 * realDepth / 0x0fff); //因爲提取的信息是距離信息,爲了便於顯示,這裏歸一化爲0-255
result.at<uchar>(i, j) = cv::saturate_cast<uchar>(depth);
p = strtok(NULL, d);
}
}
outFile.close();
return result;
}
// 以顏色表示深度信息,越暖(紅色)越近,越冷(藍色)越遠
cv::Mat Depth2Color(const cv::Mat &depth)
{
cv::Mat result(depth.size(), CV_8UC3, cv::Scalar::all(0));
int tempDepth, depthRed, depthGreen, depthBlue;
for (int i = 0; i < result.rows; i++)
{
for (int j = 0; j < result.cols; j++)
{
tempDepth = 255 - depth.at<uchar>(i, j);
if(tempDepth < 43)
{
depthRed = tempDepth * 6;
depthGreen = 0;
depthBlue = tempDepth * 6;
}
if(tempDepth > 42 && tempDepth < 85)
{
depthRed = 255 - (tempDepth - 43) * 6;
depthGreen = 0;
depthBlue = 255;
}
if(tempDepth > 84 && tempDepth < 128)
{
depthRed = 0;
depthGreen = (tempDepth - 85) * 6;
depthBlue = 255;
}
if(tempDepth > 127 && tempDepth < 169)
{
depthRed = 0;
depthGreen = 255;
depthBlue = 255 - (tempDepth - 128) * 6;
}
if(tempDepth > 168 && tempDepth < 212)
{
depthRed = (tempDepth - 169) * 6;
depthGreen = 255;
depthBlue = 0;
}
if(tempDepth > 211 && tempDepth < 254)
{
depthRed = 255;
depthGreen = 255 - (tempDepth - 212) * 6;
depthBlue = 0;
}
if(tempDepth > 253)
{
depthRed = 255;
depthGreen = 0;
depthBlue = 0;
}
if (tempDepth == 255)
{
depthRed = 0;
depthGreen = 0;
depthBlue = 0;
}
result.at<Vec3b>(i, j)[0] = depthBlue;
result.at<Vec3b>(i, j)[1] = depthGreen;
result.at<Vec3b>(i, j)[2] = depthRed;
}
}
return result;
}
int main(int argc, char *argv[])
{
string filePath = "C:/Users/XXXXXX/Downloads/NTU-Microsoft-Kinect-HandGesture Dataset/Depth";
vector<string> files;
//讀取所有文件
string format = "*"; // 不知道爲什麼在我電腦讀不了特定文件?
getAllFormatFiles(filePath, format, files);
for (int i = 0; i < files.size(); i++)
{
cv::Mat tempMat = Txt2DepthMat(files[i]);
files[i].replace(0, 66, "../data");
files[i].replace(files[i].find(".txt"), files[i].length() - 1, ".png");
cout<< files[i] << endl;
string tempString = files[i].substr(0, files[i].find_last_of("/"));
makeDir(tempString);
cv::imwrite(files[i], tempMat);
}
cout << "File Size: " << files.size() << endl;
//cv::imshow("test", Depth2Color(Txt2DepthMat("./1.txt")));
cv::waitKey(0);
return 0;
}
最後附一張轉換出來的深度圖:
像素值爲0的點代表深度信息缺失,像素值1~255表示由近到遠,即越黑越近,越白越遠。
參考資料
[1] C++文件讀寫操作(二)逐字符讀取文本和逐行讀取文本(http://blog.csdn.net/wangshihui512/article/details/8921924)
[2] 字符串分割(C++)(http://www.cnblogs.com/MikeZhang/archive/2012/03/24/MySplitFunCPP.html)
[3] C++讀取文件夾中所有的文件或者是特定後綴的文件(http://blog.csdn.net/adong76/article/details/39432467)
[4] C/C++ 判斷文件夾是否存在以及創建、刪除文件夾 windows以及linux通用(http://blog.csdn.net/u012005313/article/details/50688257)
[5] Split a string in C++?(http://stackoverflow.com/questions/236129/split-a-string-in-c)
[6] Kinect開發學習筆記之(六)帶遊戲者ID的深度數據的提取(http://blog.csdn.net/zouxy09/article/details/8151044)
[7] Depth Map Tutorial(http://www.pages.drexel.edu/~nk752/depthMapTut.html)