lsd直线提取程序说明
函数模型:ntuple_list lsd(image_double image);
直线提取程序是C语言,若使用C++,需要在头文件说明:
extern "C"
{
#include "lsd.h"
};
该程序处理的数据类型是作者自己定义的image_double类型,所以无论编程者将图像存在何种格式下,必须进行类型转化。image_double类型定义如下:
typedef struct image_double_s
{
double * data;
unsigned int xsize,ysize;
} * image_double;
以为图像的坐标原点在左上角,x轴表示宽度,水平向右,y轴表示高度,竖直向下,因此,图像中一点(x,y)的值为image->data[ x + y * image->xsize ]。
因为C++将图像存储为IplImage格式,而C语言的程序中无法使用C++的头文件,因此IplImage与image_double类型的转换必须在函数外进行,无法整合在lsd函数中,因此,在使用该函数前需要一小段代码:
IplImage* im_gray = cvCreateImage(cvSize(m_im->width,m_im->height),IPL_DEPTH_8U,1);
cvCvtColor(m_im,im_gray,CV_RGB2GRAY);
image_double image = new_image_double(im_gray->width, im_gray->height);
BYTE* im_src = (BYTE*)im_gray->imageData;
int xsize = image->xsize;//宽度
int ysize = image->ysize;//高度
int y,x;
for (y = 0;y < ysize;y++)
{
for (x = 0;x < xsize;x++) //x是横坐标,y是纵坐标
{
image->data[y*xsize + x] = im_src[y*im_gray->widthStep + x];
}
}
cvReleaseImage(&im_gray);
上述代码中,m_im是输入的彩色图像,为IplImage* 格式,因为sld函数处理的是灰度图像,所以上述代码中,将输入的彩色图像转化为灰度图像。在这段代码后,image_double格式的image就可以直接送入lsd函数:
detected_lines = lsd(image);
detected_lines中存储着直线检测的结果,类型是作者自己定义的ntuple_list型,该类型的结构定义如下:
typedef struct ntuple_list_s
{
unsigned int size;
unsigned int max_size;
unsigned int dim;
double * values;
} * ntuple_list;
detected_lines中存储的是所有线段的信息,每一条线段都可以看做是一个子单元:detected_lines->size是指子单元的个数,即直线段的条数;
detected_lines->max_size是指最大可以存储的子单元个数(一般不会用到,程序内部自动分配空间,该项数字只是记录作用);
detected_lines->dim是指每一个子单元中数据的维数,一般是5,即线段首端点的x、y坐标,线段尾端点的x、y坐标,线段的宽度(这个指标一般不为1,但是没有用过,一般只是将线段首尾点相连作为提取到的线段);
这些指标具体的值存在value中,detected_lines->values[j*dim+0]和detected_lines->values[j*dim+1]分别是线段一个端点的x、y坐标,detected_lines->values[j*dim+2]和detected_lines->values[j*dim+3]分别是线段另一个端点的x、y坐标,detected_lines->values[j*dim+4]为线段的宽度。其中,j为某一条线段的索引号。
- 主调用例子
#include "IDCardRec.h"
char * configFile = "config.txt";
char* trainSetPosPath = (char *)malloc(200*sizeof(char));
void readConfig(char* configFile, char* trainSetPosPath){
fstream f;
char cstring[1000];
int readS=0;
f.open(configFile, fstream::in);
char param1[200]; strcpy(param1,"");
char param2[200]; strcpy(param2,"");
char param3[200]; strcpy(param3,"");
f.getline(cstring, sizeof(cstring));
readS=sscanf (cstring, "%s %s %s", param1,param2, param3);
strcpy(trainSetPosPath,param3);
}
vector<string> imgNames;
int labelTemp = 0;
void dfsFolder(string folderPath){
_finddata_t FileInfo;
string strfind = folderPath + "\\*";
long Handle = _findfirst(strfind.c_str(), &FileInfo);
if (Handle == -1L){
cerr << "can not match the folder path" << endl;
exit(-1);
}
do{
if (FileInfo.attrib & _A_SUBDIR) {
if( (strcmp(FileInfo.name,".") != 0 ) &&(strcmp(FileInfo.name,"..") != 0)) {
string newPath = folderPath + "\\" + FileInfo.name;
labelTemp = atoi(FileInfo.name);
dfsFolder(newPath);
}
}else {
string finalName = folderPath + "\\" + FileInfo.name;
imgNames.push_back(finalName);
}
}while (_findnext(Handle, &FileInfo) == 0);
_findclose(Handle);
}
void initTrainImage(){
readConfig(configFile, trainSetPosPath);
string folderPath = trainSetPosPath;
dfsFolder(folderPath);
}
void processingTotal(){
initTrainImage();
int imgNum = imgNames.size();
for(int iNum=0;iNum<imgNum;iNum++){
cout<<endl<<iNum<<endl;
cout<<imgNames[iNum].c_str()<<endl;
IplImage * src=cvLoadImage(imgNames[iNum].c_str(),1);
if(!src) continue;
if(1){
cvNamedWindow("image",1);
cvShowImage("image",src);
}
processingOne(src);
}
}
extern IplImage* res_im;
void processingOne(IplImage * src){
cvNamedWindow("dst",1);
// IplImage * dst = jiaoZheng(src);
char *vif = vifLine(src);
// cout<<vif<<endl;
IplImage * dst = jiaozheng2(res_im);
cvShowImage("dst",dst);
cvWaitKey(0);
}
void main()
{
processingTotal();
}
- 矫正部分判断筛选:
#include "jiaoZheng.h"
//===================判断点在矩形内===================,这里把贴边的去掉
bool InRectYesOrNo(CvPoint pt,CvRect rect){
if( ( (pt.x >rect.x)&&(pt.x <(rect.x+rect.width)) )&&((pt.y >rect.y)&&(pt.y <(rect.y+rect.height))) )
return true;
else
return false;
}
//=============计算两点之间的距离
float lenghtOf2P(CvPoint pt1,CvPoint pt2){
return sqrt((float)(pt1.x - pt2.x)*(pt1.x - pt2.x)+(pt1.y - pt2.y)*(pt1.y - pt2.y));
}
//===============根据顶部或者底部线段,筛选出同角度下总长度最长的线段====================/
vector<cv::Vec4i> chooseLine1(IplImage* src,vector<linePtAngle> lineTop){
//第一步:确定所有线段都是首小于尾部(以X轴为参考计算)
for(unsigned int i = 0; i < lineTop.size() ; i++){
if( lineTop[i].startPt.x >lineTop[i].endPt.x ){
CvPoint temp;
temp.x = lineTop[i].startPt.x; temp.y = lineTop[i].startPt.y;
lineTop[i].startPt.x = lineTop[i].endPt.x; lineTop[i].startPt.y = lineTop[i].endPt.y;
lineTop[i].endPt.x = temp.x; lineTop[i].endPt.y = temp.y;
}
}
//显示首尾ok后的线
if(showSteps){
for(unsigned int i = 0; i < lineTop.size() ; i++){
cvLine(src,lineTop[i].startPt,lineTop[i].endPt,CV_RGB(0,255,255),6,CV_AA);
printf("======================== %d,%d\n",lineTop[i].startPt.x,lineTop[i].endPt.x);
}
cvShowImage("src",src);
cvWaitKey(0);
}
//找出堆在一起的平行线,这个一般是银联的标志
int a[20];
for(int n=0;n<20;n++) a[n] = -1;
int n = 0;
//将平行,且两条线段中的一条首与一条尾相距很小时将这两天先合并
for(unsigned int i = 0; i < lineTop.size()-1 ; i++){
for(unsigned int j = i+1; j < lineTop.size() ; j++){
if( (abs(lineTop[i].angle - lineTop[j].angle)<=3 )&&( abs(lineTop[i].startPt.x - lineTop[j].startPt.x )<=10 )&&(abs(lineTop[i].endPt.x - lineTop[j].endPt.x )<=30)&&(i!=j) ){
a[n++] = i;
a[n++] = j;
}
}
}
//显示去掉银联标志后的正确的图
if(showSteps){
for(unsigned int j = 0; j < lineTop.size() ; j++){
if( (j!=a[0])&&(j!=a[1])&&(j!=a[2])&&(j!=a[3])&&(j!=a[4])&&(j!=a[5])&&(j!=a[6])&&(j!=a[7])&&(j!=a[8])
&&(j!=a[9])&&(j!=a[10])&&(j!=a[11])&&(j!=a[12])&&(j!=a[13])&&(j!=a[14])&&(j!=a[15])&&(j!=a[16])&&(j!=a[17])
&&(j!=a[18])&&(j!=a[19]) )
cvLine(src,lineTop[j].startPt,lineTop[j].endPt,CV_RGB(0,0,255),6,CV_AA);
}
cvShowImage("src",src);
cvWaitKey(0);
}
//将堆在一起的平行线,角度赋大值,去掉他们。
for(int n=0;n<20;n++){
for(unsigned int j = 0; j < lineTop.size() ; j++){
if(j == a[n]){
lineTop[j].angle = 45;
}
}
}
/*cvShowImage("src",src);
cvWaitKey(0);*/
将数条平行在一起的线去掉
//vector<linePtAngle>::iterator it;
//int flagNum = 0;
//for(it=lineTop.begin();it!=lineTop.end();++it){
flagNum ++;
// if( flagNum==a[0] ){
// it=lineTop.erase(it);
// break;
// }else
// ++it;
//}
//for(int n=0;n<20;n++){
// int flagNum = 0;
// for(vector<linePtAngle>::iterator it=lineTop.begin(); it!=lineTop.end(); ){
// flagNum ++;
// if(flagNum==a[n]){
// it = lineTop.erase(it); //不能写成arr.erase(it);
// }else{
// ++it;
// }
// }
//}
//
//for(unsigned int i = 0; i < lineTop.size() ; i++){
// cvLine(src,lineTop[i].startPt,lineTop[i].endPt,CV_RGB(255,0,0),6,CV_AA);
//}
//cvShowImage("src",src);
//cvWaitKey(0);
//将平行,且两条线段中的一条首与一条尾相距很小时将这两天先合并
for(unsigned int i = 0; i < lineTop.size()-1 ; i++){
for(unsigned int j = i+1; j < lineTop.size() ; j++){
if( (abs(lineTop[i].angle - lineTop[j].angle)<=3 )&&( abs(lineTop[i].endPt.x - lineTop[j].startPt.x )<=5 )&&(lineTop[i].startPt.x < lineTop[j].startPt.x) ){
lineTop[i].startPt.x = lineTop[i].startPt.x;
lineTop[i].startPt.y = lineTop[i].startPt.y;
lineTop[i].endPt.x = lineTop[j].endPt.x;
lineTop[i].endPt.y = lineTop[j].endPt.y;
}
}
}
float *angleTop;
angleTop = (float *)malloc(lineTop.size()*sizeof(float));
int sumTop[40] = {
0};
for(int jAngle = -20;jAngle<20;jAngle ++){
for(unsigned int i = 0; i < lineTop.size() ; i++){
if( (lineTop[i].angle == jAngle) )
sumTop[jAngle+20] += lineTop[i].lineLength;
}
}
if(showSteps){
for(int jAngle = -20;jAngle<20;jAngle ++)
cout<<sumTop[jAngle+20]<<" ";
}
int maxSumTop = sumTop[0];
for(int jAngle = -20;jAngle<20;jAngle ++){
if (maxSumTop<sumTop[jAngle+20])
{
maxSumTop = sumTop[jAngle+20];
}
}
if(showSteps)
cout<<maxSumTop<<endl;
vector<cv::Vec4i> lines;
for(int jAngle = -20;jAngle<20;jAngle ++){
if(maxSumTop == sumTop[jAngle+20]){
for(int j = 0; j<lineTop.size();j++){
if((jAngle == lineTop[j].angle)||((jAngle+1) == lineTop[j].angle)||((jAngle-1) == lineTop[j].angle)) {
if(showSteps)
cvLine(src,lineTop[j].startPt,lineTop[j].endPt,CV_RGB(255,255,255),6,CV_AA);
Vec4i v;
v[0] = lineTop[j].startPt.x; v[1] = lineTop[j].startPt.y;
v[2] = lineTop[j].endPt.x; v[3] = lineTop[j].endPt.y;
lines.push_back(v);
}
}
}
}
return lines;
}
//===============根据顶部或者底部线段,筛选出同角度下总长度最长的线段=================