├── GOCVHelper.h ├── GOCVHelper0.8.cpp ├── GOCVHelper_2018_10_09.cpp ├── GOCVHelper_2020_04_20.cpp ├── GoCvHelper0.7b.cpp └── README.md /GOCVHelper.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsxyhelu/GOCvHelper/7d8b19decb86c672fd8026bba8e86a8a0ca5b2f6/GOCVHelper.h -------------------------------------------------------------------------------- /GOCVHelper0.8.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsxyhelu/GOCvHelper/7d8b19decb86c672fd8026bba8e86a8a0ca5b2f6/GOCVHelper0.8.cpp -------------------------------------------------------------------------------- /GOCVHelper_2018_10_09.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | //名称:GOCVHelper0.9.cpp 3 | //功能:图像处理和MFC增强 4 | //作者:jsxyhelu(1755311380@qq.com http://jsxyhelu.cnblogs.com) 5 | //组织:GREENOPEN 6 | //日期:2018-10-06 7 | ///////////////////////////////////////////////////////////////////////////// 8 | #include "stdafx.h" 9 | #include 10 | #include 11 | #include 12 | #include "GoCvHelper.h" 13 | #include "opencv/cv.h" 14 | #include "atlstr.h" 15 | RNG rng(12345); 16 | #define DEBUG TRUE 17 | //2016年1月26日GoCvHelper添加string 相关操作函数到其他操作中 18 | //2016年1月28日10:45:22 GOCVHelper基于颜色直方图的CBIR到图像操作中去 19 | //2016年8月12日08:27:03 添加关于excel操作相关函数 20 | //2017年6月28日11:04:35 修改一个轮廓排序的BUG 21 | //2018年6月26日08:50:09 解决unicode问题,并且文件改名字了(最主要的问题是将项目设置为 未设置) 22 | //2018年8月7日20:28:22 添加了更为高效的GetPointLineDistance等 23 | namespace GO{ 24 | 25 | #pragma region 图像增强 26 | //读取灰度或彩色图片到灰度 27 | Mat imread2gray(string path){ 28 | Mat src = imread(path); 29 | Mat srcClone = src.clone(); 30 | if (CV_8UC3 == srcClone.type() ) 31 | cvtColor(srcClone,srcClone,CV_BGR2GRAY); 32 | return srcClone; 33 | } 34 | 35 | //带有上下限的threshold 36 | Mat threshold2(Mat src,int minvalue,int maxvalue){ 37 | Mat thresh1; 38 | Mat thresh2; 39 | Mat dst; 40 | threshold(src,thresh1,minvalue,255, THRESH_BINARY); 41 | threshold(src,thresh2,maxvalue,255,THRESH_BINARY_INV); 42 | dst = thresh1 & thresh2; 43 | return dst; 44 | } 45 | 46 | //自适应门限的canny算法 47 | //canny2 48 | Mat canny2(Mat src){ 49 | Mat imagetmp = src.clone(); 50 | double low_thresh = 0.0; 51 | double high_thresh = 0.0; 52 | AdaptiveFindThreshold(imagetmp,&low_thresh,&high_thresh); 53 | Canny(imagetmp,imagetmp,low_thresh,high_thresh); 54 | return imagetmp;} 55 | void AdaptiveFindThreshold( Mat src,double *low,double *high,int aperture_size){ 56 | const int cn = src.channels(); 57 | Mat dx(src.rows,src.cols,CV_16SC(cn)); 58 | Mat dy(src.rows,src.cols,CV_16SC(cn)); 59 | Sobel(src,dx,CV_16S,1,0,aperture_size,1,0,BORDER_REPLICATE); 60 | Sobel(src,dy,CV_16S,0,1,aperture_size,1,0,BORDER_REPLICATE); 61 | CvMat _dx = dx; 62 | CvMat _dy = dy; 63 | _AdaptiveFindThreshold(&_dx, &_dy, low, high); } 64 | void _AdaptiveFindThreshold(CvMat *dx, CvMat *dy, double *low, double *high){ 65 | CvSize size; 66 | IplImage *imge=0; 67 | int i,j; 68 | CvHistogram *hist; 69 | int hist_size = 255; 70 | float range_0[]={0,256}; 71 | float* ranges[] = { range_0 }; 72 | double PercentOfPixelsNotEdges = 0.7; 73 | size = cvGetSize(dx); 74 | imge = cvCreateImage(size, IPL_DEPTH_32F, 1); 75 | // 计算边缘的强度, 并存于图像中 76 | float maxv = 0; 77 | for(i = 0; i < size.height; i++ ){ 78 | const short* _dx = (short*)(dx->data.ptr + dx->step*i); 79 | const short* _dy = (short*)(dy->data.ptr + dy->step*i); 80 | float* _image = (float *)(imge->imageData + imge->widthStep*i); 81 | for(j = 0; j < size.width; j++){ 82 | _image[j] = (float)(abs(_dx[j]) + abs(_dy[j])); 83 | maxv = maxv < _image[j] ? _image[j]: maxv;}} 84 | if(maxv == 0){ 85 | *high = 0; 86 | *low = 0; 87 | cvReleaseImage( &imge ); 88 | return;} 89 | // 计算直方图 90 | range_0[1] = maxv; 91 | hist_size = (int)(hist_size > maxv ? maxv:hist_size); 92 | hist = cvCreateHist(1, &hist_size, CV_HIST_ARRAY, ranges, 1); 93 | cvCalcHist( &imge, hist, 0, NULL ); 94 | int total = (int)(size.height * size.width * PercentOfPixelsNotEdges); 95 | float sum=0; 96 | int icount = hist->mat.dim[0].size; 97 | float *h = (float*)cvPtr1D( hist->bins, 0 ); 98 | for(i = 0; i < icount; i++){ 99 | sum += h[i]; 100 | if( sum > total ) 101 | break; } 102 | // 计算高低门限 103 | *high = (i+1) * maxv / hist_size ; 104 | *low = *high * 0.4; 105 | cvReleaseImage( &imge ); 106 | cvReleaseHist(&hist); } 107 | // end of canny2 108 | 109 | //填充孔洞 110 | //使用例子 111 | Mat fillHoles(Mat src){ 112 | Mat dst = getInnerHoles(src); 113 | threshold(dst,dst,0,255,THRESH_BINARY_INV); 114 | dst = src + dst; 115 | return dst; 116 | } 117 | //获得图像中白色的比率 118 | float getWhiteRate(Mat src){ 119 | int iWhiteSum = 0; 120 | for (int x =0;x(x,y) != 0) 123 | iWhiteSum = iWhiteSum +1; 124 | } 125 | } 126 | return (float)iWhiteSum/(float)(src.rows*src.cols); 127 | } 128 | //获得内部孔洞图像 129 | Mat getInnerHoles(Mat src){ 130 | Mat clone = src.clone(); 131 | srand((unsigned)time(NULL)); // 生成时间种子 132 | float fPreRate = getWhiteRate(clone); 133 | float fAftRate = 0; 134 | do { 135 | clone = src.clone(); 136 | // x y 对于 cols rows 137 | floodFill(clone,Point((int)rand()%src.cols,(int)rand()%src.rows),Scalar(255)); 138 | fAftRate = getWhiteRate(clone); 139 | } while ( fAftRate < 0.6); 140 | return clone; 141 | } 142 | // end of fillHoles 143 | 144 | //顶帽去光差,radius为模板半径 145 | Mat moveLightDiff(Mat src,int radius){ 146 | Mat dst; 147 | Mat srcclone = src.clone(); 148 | Mat mask = Mat::zeros(radius*2,radius*2,CV_8U); 149 | circle(mask,Point(radius,radius),radius,Scalar(255),-1); 150 | //顶帽 151 | erode(srcclone,srcclone,mask); 152 | dilate(srcclone,srcclone,mask); 153 | dst = src - srcclone; 154 | return dst; 155 | } 156 | 157 | //将 DEPTH_8U型二值图像进行细化 经典的Zhang并行快速细化算法 158 | //细化算法 159 | void thin(const Mat &src, Mat &dst, const int iterations){ 160 | const int height =src.rows -1; 161 | const int width =src.cols -1; 162 | //拷贝一个数组给另一个数组 163 | if(src.data != dst.data) 164 | src.copyTo(dst); 165 | int n = 0,i = 0,j = 0; 166 | Mat tmpImg; 167 | uchar *pU, *pC, *pD; 168 | bool isFinished =FALSE; 169 | for(n=0; n(i-1); 175 | pC = tmpImg.ptr(i); 176 | pD = tmpImg.ptr(i+1); 177 | for(int j=1; j 0){ 179 | int ap=0; 180 | int p2 = (pU[j] >0); 181 | int p3 = (pU[j+1] >0); 182 | if (p2==0 && p3==1) 183 | ap++; 184 | int p4 = (pC[j+1] >0); 185 | if(p3==0 && p4==1) 186 | ap++; 187 | int p5 = (pD[j+1] >0); 188 | if(p4==0 && p5==1) 189 | ap++; 190 | int p6 = (pD[j] >0); 191 | if(p5==0 && p6==1) 192 | ap++; 193 | int p7 = (pD[j-1] >0); 194 | if(p6==0 && p7==1) 195 | ap++; 196 | int p8 = (pC[j-1] >0); 197 | if(p7==0 && p8==1) 198 | ap++; 199 | int p9 = (pU[j-1] >0); 200 | if(p8==0 && p9==1) 201 | ap++; 202 | if(p9==0 && p2==1) 203 | ap++; 204 | if((p2+p3+p4+p5+p6+p7+p8+p9)>1 && (p2+p3+p4+p5+p6+p7+p8+p9)<7){ 205 | if(ap==1){ 206 | if((p2*p4*p6==0)&&(p4*p6*p8==0)){ 207 | dst.ptr(i)[j]=0; 208 | isFinished =TRUE; 209 | } 210 | } 211 | } 212 | } 213 | 214 | } //扫描过程一 结束 215 | dst.copyTo(tmpImg); 216 | //扫描过程二 开始 217 | for(i=1; i(i-1); 219 | pC = tmpImg.ptr(i); 220 | pD = tmpImg.ptr(i+1); 221 | for(int j=1; j 0){ 223 | int ap=0; 224 | int p2 = (pU[j] >0); 225 | int p3 = (pU[j+1] >0); 226 | if (p2==0 && p3==1) 227 | ap++; 228 | int p4 = (pC[j+1] >0); 229 | if(p3==0 && p4==1) 230 | ap++; 231 | int p5 = (pD[j+1] >0); 232 | if(p4==0 && p5==1) 233 | ap++; 234 | int p6 = (pD[j] >0); 235 | if(p5==0 && p6==1) 236 | ap++; 237 | int p7 = (pD[j-1] >0); 238 | if(p6==0 && p7==1) 239 | ap++; 240 | int p8 = (pC[j-1] >0); 241 | if(p7==0 && p8==1) 242 | ap++; 243 | int p9 = (pU[j-1] >0); 244 | if(p8==0 && p9==1) 245 | ap++; 246 | if(p9==0 && p2==1) 247 | ap++; 248 | if((p2+p3+p4+p5+p6+p7+p8+p9)>1 && (p2+p3+p4+p5+p6+p7+p8+p9)<7){ 249 | if(ap==1){ 250 | if((p2*p4*p8==0)&&(p2*p6*p8==0)){ 251 | dst.ptr(i)[j]=0; 252 | isFinished =TRUE; 253 | } 254 | } 255 | } 256 | } 257 | } 258 | } //一次 先行后列扫描完成 259 | //如果在扫描过程中没有删除点,则提前退出 260 | if(isFinished ==FALSE) 261 | break; 262 | } 263 | } 264 | } 265 | // end of thin 266 | 267 | //使得rect区域半透明 268 | Mat translucence(Mat src,Rect rect,int idepth){ 269 | Mat dst = src.clone(); 270 | Mat roi = dst(rect); 271 | roi += Scalar(idepth,idepth,idepth); 272 | return dst; 273 | } 274 | 275 | //使得rect区域打上马赛克 276 | Mat mosaic(Mat src,Rect rect,int W,int H){ 277 | Mat dst = src.clone(); 278 | Mat roi = dst(rect); 279 | for (int i=W; i(j-H/2,(i-W/2)*3); 282 | uchar s1=roi.at(j-H/2,(i-W/2)*3+1); 283 | uchar s2=roi.at(j-H/2,(i-W/2)*3+2); 284 | for (int ii=i-W; ii<=i; ii++) { 285 | for (int jj=j-H; jj<=j; jj++) { 286 | roi.at(jj,ii*3+0)=s; 287 | roi.at(jj,ii*3+1)=s1; 288 | roi.at(jj,ii*3+2)=s2; 289 | } 290 | } 291 | } 292 | } 293 | return dst; 294 | } 295 | 296 | 297 | //基于颜色直方图的距离计算 298 | double GetHsVDistance(Mat src_base,Mat src_test1){ 299 | Mat hsv_base; 300 | Mat hsv_test1; 301 | /// Convert to HSV 302 | cvtColor( src_base, hsv_base, COLOR_BGR2HSV ); 303 | cvtColor( src_test1, hsv_test1, COLOR_BGR2HSV ); 304 | /// Using 50 bins for hue and 60 for saturation 305 | int h_bins = 50; int s_bins = 60; 306 | int histSize[] = { h_bins, s_bins }; 307 | // hue varies from 0 to 179, saturation from 0 to 255 308 | float h_ranges[] = { 0, 180 }; 309 | float s_ranges[] = { 0, 256 }; 310 | const float* ranges[] = { h_ranges, s_ranges }; 311 | // Use the o-th and 1-st channels 312 | int channels[] = { 0, 1 }; 313 | /// Histograms 314 | MatND hist_base; 315 | MatND hist_test1; 316 | /// Calculate the histograms for the HSV images 317 | calcHist( &hsv_base, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false ); 318 | normalize( hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat() ); 319 | calcHist( &hsv_test1, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false ); 320 | normalize( hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat() ); 321 | /// Apply the histogram comparison methods 322 | double base_test1 = compareHist( hist_base, hist_test1, 0 ); 323 | return base_test1; 324 | } 325 | // Multiply 正片叠底 326 | void Multiply(Mat& src1, Mat& src2, Mat& dst) 327 | { 328 | for(int index_row=0; index_row(index_row, index_col)[index_c]= 334 | src1.at(index_row, index_col)[index_c]* 335 | src2.at(index_row, index_col)[index_c]; 336 | } 337 | } 338 | } 339 | // Color_Burn 颜色加深 340 | void Color_Burn(Mat& src1, Mat& src2, Mat& dst) 341 | { 342 | for(int index_row=0; index_row(index_row, index_col)[index_c]=1- 348 | (1-src1.at(index_row, index_col)[index_c])/ 349 | src2.at(index_row, index_col)[index_c]; 350 | } 351 | } 352 | } 353 | // 线性增强 354 | void Linear_Burn(Mat& src1, Mat& src2, Mat& dst) 355 | { 356 | for(int index_row=0; index_row(index_row, index_col)[index_c]=max( 362 | src1.at(index_row, index_col)[index_c]+ 363 | src2.at(index_row, index_col)[index_c]-1, (float)0.0); 364 | } 365 | } 366 | } 367 | 368 | 369 | //点乘法 elementWiseMultiplication 370 | Mat EWM(Mat m1,Mat m2){ 371 | Mat dst=m1.mul(m2); 372 | return dst; 373 | } 374 | //图像局部对比度增强算法 375 | Mat ACE(Mat src,int C,int n,int MaxCG){ 376 | Mat meanMask; 377 | Mat varMask; 378 | Mat meanGlobal; 379 | Mat varGlobal; 380 | Mat dst; 381 | Mat tmp; 382 | Mat tmp2; 383 | blur(src.clone(),meanMask,Size(50,50));//meanMask为局部均值 384 | tmp = src - meanMask; 385 | varMask = EWM(tmp,tmp); 386 | blur(varMask,varMask,Size(50,50)); //varMask为局部方差 387 | //换算成局部标准差 388 | varMask.convertTo(varMask,CV_32F); 389 | for (int i=0;i(i,j) = (float)sqrt(varMask.at(i,j)); 392 | } 393 | } 394 | meanStdDev(src,meanGlobal,varGlobal); //meanGlobal为全局均值 varGlobal为全局标准差 395 | tmp2 = varGlobal/varMask; 396 | for (int i=0;i(i,j)>MaxCG){ 399 | tmp2.at(i,j) = MaxCG; 400 | } 401 | } 402 | } 403 | tmp2.convertTo(tmp2,CV_8U); 404 | tmp2 = EWM(tmp2,tmp); 405 | dst = meanMask + tmp2; 406 | imshow("D方法",dst); 407 | dst = meanMask + C*tmp; 408 | imshow("C方法",dst); 409 | return dst; 410 | } 411 | 412 | //Local Normalization input is 32f1u 413 | Mat LocalNormalization(Mat float_gray,float sigma1,float sigma2){ 414 | Mat gray, blur, num, den; 415 | float_gray.convertTo(float_gray, CV_32F, 1.0/255.0); 416 | // numerator = img - gauss_blur(img) 417 | boxFilter(float_gray,blur,float_gray.depth(),Size(sigma1,sigma1)); 418 | num = float_gray - blur; 419 | boxFilter(num.mul(num),blur,num.depth(),Size(sigma2,sigma2)); 420 | // denominator = sqrt(gauss_blur(img^2)) 421 | pow(blur, 0.5, den); 422 | // output = numerator / denominator 423 | gray = num / den; 424 | // normalize output into [0,1] 425 | normalize(gray, gray, 0.0, 1.0, NORM_MINMAX, -1); 426 | return gray; 427 | } 428 | #pragma endregion 图像增强 429 | 430 | #pragma region 图像处理 431 | //寻找最大的轮廓 432 | VP FindBigestContour(Mat src){ 433 | int imax = 0; //代表最大轮廓的序号 434 | int imaxcontour = -1; //代表最大轮廓的大小 435 | std::vector>contours; 436 | findContours(src,contours,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE); 437 | for (int i=0;i c1,std::vector c2) { return (contourArea(c1)>contourArea(c2)); } 449 | VP FindnthContour(Mat src,int ith ){ 450 | std::vector>contours; 451 | findContours(src,contours,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE); 452 | std::sort(contours.begin(),contours.end(),sortfunction); 453 | return contours[ith]; 454 | } 455 | //寻找并绘制出彩色联通区域 456 | vector connection2(Mat src,Mat& draw){ 457 | draw = Mat::zeros(src.rows,src.cols,CV_8UC3); 458 | vectorcontours; 459 | findContours(src.clone(),contours,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE); 460 | //由于给大的区域着色会覆盖小的区域,所以首先进行排序操作 461 | //冒泡排序,由小到大排序 462 | VP vptmp; 463 | for(int i=1;i=i;j--){ 465 | if (contourArea(contours[j]) < contourArea(contours[j-1])) 466 | { 467 | vptmp = contours[j-1]; 468 | contours[j-1] = contours[j]; 469 | contours[j] = vptmp; 470 | } 471 | } 472 | } 473 | //打印结果 474 | for (int i=contours.size()-1;i>=0;i--){ 475 | Scalar color = Scalar(rng.uniform(0,255),rng.uniform(0,255),rng.uniform(0,255)); 476 | drawContours(draw,contours,i,color,-1); 477 | } 478 | return contours; 479 | } 480 | vector connection2(Mat src){ 481 | Mat draw; 482 | return connection2(src,draw); 483 | } 484 | 485 | //根据轮廓的面积大小进行选择 486 | vector selectShapeArea(Mat src,Mat& draw,vector contours,int minvalue,int maxvalue){ 487 | vector result_contours; 488 | draw = Mat::zeros(src.rows,src.cols,CV_8UC3); 489 | for (int i=0;iminvalue && countour_area selectShapeArea(vector contours,int minvalue,int maxvalue) 510 | { 511 | vector result_contours; 512 | for (int i=0;iminvalue && countour_area selectShapeCircularity(Mat src,Mat& draw,vector contours,float minvalue,float maxvalue){ 521 | vector result_contours; 522 | draw = Mat::zeros(src.rows,src.cols,CV_8UC3); 523 | for (int i=0;i=minvalue && fcompare <=maxvalue) 526 | result_contours.push_back(contours[i]); 527 | } 528 | for (int i=0;i selectShapeCircularity(vector contours,float minvalue,float maxvalue){ 535 | vector result_contours; 536 | for (int i=0;i=minvalue && fcompare <=maxvalue) 539 | result_contours.push_back(contours[i]); 540 | } 541 | return result_contours; 542 | } 543 | //计算轮廓的圆的特性 544 | float calculateCircularity(VP contour){ 545 | Point2f center; 546 | float radius = 0; 547 | minEnclosingCircle((Mat)contour,center,radius); 548 | //以最小外接圆半径作为数学期望,计算轮廓上各点到圆心距离的标准差 549 | float fsum = 0; 550 | float fcompare = 0; 551 | for (int i=0;i &pts, Mat &img) 637 | { 638 | //构建pca数据。这里做的是将轮廓点的x和y作为两个维压到data_pts中去。 639 | Mat data_pts = Mat(pts.size(), 2, CV_64FC1);//使用mat来保存数据,也是为了后面pca处理需要 640 | for (int i = 0; i < data_pts.rows; ++i) 641 | { 642 | data_pts.at(i, 0) = pts[i].x; 643 | data_pts.at(i, 1) = pts[i].y; 644 | } 645 | //执行PCA分析 646 | PCA pca_analysis(data_pts, Mat(), CV_PCA_DATA_AS_ROW); 647 | //获得最主要分量,在本例中,对应的就是轮廓中点,也是图像中点 648 | Point pos = Point(pca_analysis.mean.at(0, 0),pca_analysis.mean.at(0, 1)); 649 | //存储特征向量和特征值 650 | vector eigen_vecs(2); 651 | vector eigen_val(2); 652 | for (int i = 0; i < 2; ++i) 653 | { 654 | eigen_vecs[i] = Point2d(pca_analysis.eigenvectors.at(i, 0),pca_analysis.eigenvectors.at(i, 1)); 655 | eigen_val[i] = pca_analysis.eigenvalues.at(i,0);//注意,这个地方原代码写错了 656 | } 657 | //在轮廓/图像中点绘制小圆 658 | circle(img, pos, 3, CV_RGB(255, 0, 255), 2); 659 | //计算出直线,在主要方向上绘制直线 660 | line(img, pos, pos + 0.02 * Point(eigen_vecs[0].x * eigen_val[0], eigen_vecs[0].y * eigen_val[0]) , CV_RGB(255, 255, 0),3); 661 | line(img, pos, pos + 0.02 * Point(eigen_vecs[1].x * eigen_val[1], eigen_vecs[1].y * eigen_val[1]) , CV_RGB(0, 255, 255),3); 662 | //返回角度结果 663 | return atan2(eigen_vecs[0].y, eigen_vecs[0].x); 664 | } 665 | 666 | //根据中线将轮廓分为2个部分 667 | //pts 轮廓 668 | //pa pb 中线线段端点 669 | //p1 p2 分为两边后最远2点 670 | //lenght1,length2 对应距离 671 | //img 用于绘图 672 | //返回 是否分割成功 673 | bool SplitContoursByMiddleLine(vector &pts,Mat &img,Point pa,Point pb,Point& p1,float& length1,Point& p2,float& length2) 674 | { 675 | //寻找轮廓到中线(实际上是线段)的交点 676 | int isum = 0; 677 | Point2f pointOut; 678 | //bool bIsCross =false; 679 | int iStart = -1; 680 | int iEnd = -1; 681 | vector vecBorderPoints; 682 | float fDistance = 0; 683 | //将轮廓划分为两个部分 684 | for (int i = 0;i< pts.size();i++) 685 | { 686 | float f = GetPointLineDistance(img,pts[i], pa,pb,pointOut);//获得轮廓上所有点最远距离点 687 | 688 | } 689 | 690 | //对所有 轮廓和边缘的交点 进行排序,得到距离最远的点对 691 | float fDistance = 0; 692 | for (int i = 0 ;i fDistance) 697 | { 698 | fDistance = getDistance(pts[vecBorderPoints[i]],pts[vecBorderPoints[j]]); 699 | iStart = vecBorderPoints[i]; 700 | iEnd = vecBorderPoints[j]; 701 | } 702 | } 703 | } 704 | if (-1 == iEnd ) //出现问题了,交给比较方便的方法吧 705 | return false; 706 | if (iStart > iEnd) 707 | swap(iStart,iEnd); 708 | if ((iEnd - iStart) vector1; 719 | vector vector2; 720 | for (int i = 0;i=iStart && i<=iEnd) 723 | { 724 | vector1.push_back(pts[i]); 725 | if(DEBUG) 726 | circle(img,pts[i],3,Scalar(0,0,255)); 727 | } 728 | else 729 | { 730 | vector2.push_back(pts[i]); 731 | if(DEBUG) 732 | circle(img,pts[i],3,Scalar(0,255,255)); 733 | } 734 | 735 | } 736 | //分别在这两个轮廓里面找到交点距离 737 | Point pstart = pts[iStart]; 738 | Point pend = pts[iEnd]; 739 | float fmax = -1;int imax = -1; 740 | for (int i =0;ifmax) //冒泡 744 | { 745 | fmax = f; 746 | imax = i; 747 | } 748 | } 749 | if (DEBUG) 750 | circle(img,vector1[imax],3,cv::Scalar(255,0,0),2); 751 | p1 = vector1[imax]; 752 | length1 = fmax; 753 | 754 | fmax = -1; imax = -1; 755 | for (int i =0;ifmax) //冒泡 759 | { 760 | fmax = f; 761 | imax = i; 762 | } 763 | } 764 | if (DEBUG) 765 | circle(img,vector2[imax],3,cv::Scalar(255,0,0),2); 766 | p2 = vector2[imax]; 767 | length2 = fmax; 768 | return true; 769 | } 770 | 771 | //获得真实的长宽,返回值为false的话代表识别不成功 772 | bool getRealWidthHeight(vector &pts,vector &resultPts, Mat &img,float& flong,float& fshort) 773 | { 774 | //构建pca数据。这里做的是将轮廓点的x和y作为两个维压到data_pts中去。 775 | Mat data_pts = Mat(pts.size(), 2, CV_64FC1);//使用mat来保存数据,也是为了后面pca处理需要 776 | for (int i = 0; i < data_pts.rows; ++i) 777 | { 778 | data_pts.at(i, 0) = pts[i].x; 779 | data_pts.at(i, 1) = pts[i].y; 780 | } 781 | //执行PCA分析 782 | PCA pca_analysis(data_pts, Mat(), CV_PCA_DATA_AS_ROW); 783 | //获得最主要分量,在本例中,对应的就是轮廓中点,也是图像中点 784 | Point pos = Point(pca_analysis.mean.at(0, 0),pca_analysis.mean.at(0, 1)); 785 | //获得特征向量和特征值 786 | vector eigen_vecs(2); 787 | vector eigen_val(2); 788 | for (int i = 0; i < 2; ++i) 789 | { 790 | eigen_vecs[i] = Point2d(pca_analysis.eigenvectors.at(i, 0),pca_analysis.eigenvectors.at(i, 1)); 791 | eigen_val[i] = pca_analysis.eigenvalues.at(i,0); 792 | } 793 | if (eigen_vecs[0].x == 0 || abs(eigen_vecs[0].y / eigen_vecs[0].x) >100)//一般出现在中线为垂直情况,这个时候可以直接采用简单方法 794 | return false; 795 | if (eigen_vecs[1].x == 0 || abs(eigen_vecs[1].y / eigen_vecs[1].x) >100) 796 | return false; 797 | //在轮廓/图像中点绘制小圆 798 | if (DEBUG) 799 | circle(img, pos, 3, CV_RGB(255, 0, 255), 2); 800 | 801 | //获得长短轴和轮廓的交接点,首先得到一条绝对可行的直线 802 | //长轴 803 | Point pa = pos-0.04 * Point(eigen_vecs[0].x * eigen_val[0], eigen_vecs[0].y * eigen_val[0]); 804 | Point pb = pos + 0.04 * Point(eigen_vecs[0].x * eigen_val[0], eigen_vecs[0].y * eigen_val[0]) ; 805 | //短轴 806 | Point pc = pos- 0.2 * Point(eigen_vecs[1].x * eigen_val[1], eigen_vecs[1].y * eigen_val[1]); 807 | Point pd = pos + 0.2 * Point(eigen_vecs[1].x * eigen_val[1], eigen_vecs[1].y * eigen_val[1]); 808 | //通过对边界进行限定,提高算法效率,获得的结果能够保证此时的pa,pb都是最远的点 809 | LineIterator it(img, pa, pb); 810 | LineIterator it2(img, pb, pa); 811 | LineIterator it3(img, pc, pd); 812 | LineIterator it4(img, pd, pc); 813 | for(int i = 0; i < it.count; i++, ++it) 814 | { 815 | if( 0 == pointPolygonTest(pts,it.pos(),true))//第一个在轮廓上的点 816 | pa = it.pos(); 817 | } 818 | 819 | for(int i = 0; i < it2.count; i++, ++it2) 820 | { 821 | if( 0 == pointPolygonTest(pts,it2.pos(),true))//第一个在轮廓上的点 822 | pb = it2.pos(); 823 | } 824 | 825 | for(int i = 0; i < it3.count; i++, ++it3) 826 | { 827 | if( 0 == pointPolygonTest(pts,it3.pos(),true))//第一个在轮廓上的点 828 | pc = it3.pos(); 829 | } 830 | 831 | for(int i = 0; i < it4.count; i++, ++it4) 832 | { 833 | if( 0 == pointPolygonTest(pts,it4.pos(),true))//第一个在轮廓上的点 834 | pd = it4.pos(); 835 | } 836 | //计算出直线,在长短轴上绘制直线 837 | if (DEBUG) 838 | { 839 | line(img, pa,pb , CV_RGB(255, 255, 0),10); 840 | line(img, pc,pd, CV_RGB(0, 255, 255),10); 841 | } 842 | 843 | //将 轮廓按照长短轴进行划分.这里_p[]得到的是4个边界最远点;而_length则是长度 844 | Point _p[4]; 845 | float _length[4] = {-1,-1,-1,-1}; 846 | if (!SplitContoursByMiddleLine(pts,img,pa,pb,_p[0],_length[0],_p[1],_length[1])) 847 | return false; 848 | if (!SplitContoursByMiddleLine(pts,img,pc,pd,_p[2],_length[2],_p[3],_length[3])) 849 | return false; 850 | 851 | //开始获得结论 852 | if (eigen_vecs[0].x == 0 || eigen_vecs[1].x == 0)//除数为0 853 | return false; 854 | float k_long = eigen_vecs[0].y /eigen_vecs[0].x; 855 | float k_short = eigen_vecs[1].y /eigen_vecs[1].x; 856 | if (k_long == k_short)//这种情况不应该出现 857 | return false; 858 | 859 | //返回长度 860 | if (_length[0]<0 || _length[1]<0 || _length[2]<0 || _length[3]<0) 861 | return false; 862 | fshort = _length[0]+_length[1]; 863 | flong = _length[2]+_length[3]; 864 | //通过解析方法,获得最后结果 865 | Point p[4]; 866 | p[0].x = (k_long * _p[0].x - k_short * _p[2].x + _p[2].y - _p[0].y) / (k_long - k_short); 867 | p[0].y = (p[0].x - _p[0].x)*k_long + _p[0].y; 868 | p[1].x = (k_long * _p[0].x - k_short * _p[3].x + _p[3].y - _p[0].y) / (k_long - k_short); 869 | p[1].y = (p[1].x - _p[0].x)*k_long + _p[0].y; 870 | p[2].x = (k_long * _p[1].x - k_short * _p[2].x + _p[2].y - _p[1].y) / (k_long - k_short); 871 | p[2].y = (p[2].x - _p[1].x)*k_long + _p[1].y; 872 | p[3].x = (k_long * _p[1].x - k_short * _p[3].x + _p[3].y - _p[1].y) / (k_long - k_short); 873 | p[3].y = (p[3].x - _p[1].x)*k_long + _p[1].y; 874 | 875 | //简单排序 876 | if (p[1].x < p[0].x) 877 | swap(p[1],p[0]); 878 | if (p[3].x < p[2].x) 879 | swap(p[3],p[2]); 880 | //绘图 881 | for (int i = 0;i<4;i++) 882 | resultPts.push_back(p[i]); 883 | 884 | //line(img,p[0],p[1],CV_RGB(0, 255, 255), 5); 885 | //line(img,p[0],p[2],CV_RGB(0, 255, 255), 5); 886 | //line(img,p[3],p[1],CV_RGB(0, 255, 255), 5); 887 | //line(img,p[3],p[2],CV_RGB(0, 255, 255), 5); 888 | 889 | return true; 890 | 891 | } 892 | 893 | //投影到x或Y轴上,上波形为vup,下波形为vdown,gap为误差间隔 894 | void projection2(Mat src,vector& vup,vector& vdown,int direction,int gap){ 895 | Mat tmp = src.clone(); 896 | vector vdate; 897 | if (DIRECTION_X == direction){ 898 | for (int i=0;i0 && vdate[i+gap]>0){ 915 | for (int j=i;j0) 924 | vup.push_back(i); 925 | if (vdate[i-1]>0 && vdate[i] == 0) 926 | vdown.push_back(i); 927 | } 928 | } 929 | //轮廓柔化 930 | bool SmoothEdgeSingleChannel( Mat mInput,Mat &mOutput, double amount, double radius, uchar Threshold) 931 | { 932 | if(mInput.empty()) 933 | { 934 | return 0; 935 | } 936 | if(radius<1) 937 | radius=1; 938 | 939 | Mat mGSmooth,mDiff,mAbsDiff; 940 | mOutput = Mat(mInput.size(),mInput.type()); 941 | 942 | GaussianBlur(mInput,mGSmooth,Size(0,0),radius); 943 | //imshow("mGSmooth",mGSmooth); 944 | 945 | subtract(mGSmooth,mInput,mDiff); 946 | //imshow("mDiff",mDiff); 947 | 948 | mDiff*=amount; 949 | threshold(abs(2* mDiff),mAbsDiff,Threshold,255,THRESH_BINARY_INV); 950 | 951 | mDiff.setTo(Scalar(0),mAbsDiff); 952 | //imshow("mDiff Multiplied",mDiff); 953 | 954 | add(mInput,mDiff,mOutput); 955 | 956 | return true; 957 | } 958 | #pragma endregion 图像处理 959 | 960 | #pragma region 文件操作 961 | //递归读取目录下全部文件 962 | void getFiles(string path, vector& files,string flag){ 963 | //文件句柄 964 | long hFile = 0; 965 | //文件信息 966 | struct _finddata_t fileinfo; 967 | string p; 968 | if((hFile = _findfirst(p.assign(path).append("\\*").c_str(),&fileinfo)) != -1){ 969 | do{ 970 | //如果是目录,迭代之,如果不是,加入列表 971 | if((fileinfo.attrib & _A_SUBDIR)){ 972 | if(strcmp(fileinfo.name,".") != 0 && strcmp(fileinfo.name,"..") != 0 && flag=="r") 973 | getFiles( p.assign(path).append("\\").append(fileinfo.name), files,flag ); 974 | } 975 | else{ 976 | files.push_back(p.assign(path).append("\\").append(fileinfo.name) ); 977 | } 978 | }while(_findnext(hFile, &fileinfo) == 0); 979 | _findclose(hFile); 980 | } 981 | } 982 | //递归读取目录下全部图片 983 | void getFiles(string path, vector& files,string flag){ 984 | vector fileNames; 985 | getFiles(path,fileNames,flag); 986 | for (int i=0;i0)//如果是图片 989 | files.push_back(tmp); 990 | } 991 | } 992 | //递归读取目录下全部图片和名称 993 | void getFiles(string path, vector>& files,string flag){ 994 | vector fileNames; 995 | getFiles(path,fileNames,flag); 996 | for (int i=0;i0){ 999 | pair apir; 1000 | apir.first = tmp; 1001 | apir.second = fileNames[i]; 1002 | files.push_back(apir); 1003 | } 1004 | } 1005 | } 1006 | ////删除目录下的全部文件 1007 | void deleteFiles(string path,string flag){ 1008 | //文件句柄 1009 | long hFile = 0; 1010 | //文件信息 1011 | struct _finddata_t fileinfo; 1012 | string p; 1013 | if((hFile = _findfirst(p.assign(path).append("\\*").c_str(),&fileinfo)) != -1){ 1014 | do{ 1015 | //如果是目录,迭代之,如果不是,加入列表 1016 | if((fileinfo.attrib & _A_SUBDIR)){ 1017 | if(strcmp(fileinfo.name,".") != 0 && strcmp(fileinfo.name,"..") != 0 && flag=="r") 1018 | deleteFiles(p.assign(path).append("\\").append(fileinfo.name).c_str(),flag ); 1019 | } 1020 | else{ 1021 | deleteFiles(p.assign(path).append("\\").append(fileinfo.name).c_str()); 1022 | } 1023 | }while(_findnext(hFile, &fileinfo) == 0); 1024 | _findclose(hFile); 1025 | } 1026 | } 1027 | //创建或续写目录下的csv文件,填写“文件位置-分类”对 1028 | int writeCsv(const string& filename,const vector>srcVect,char separator ){ 1029 | ofstream file(filename.c_str(),ofstream::app); 1030 | if (!file) 1031 | return 0; 1032 | for (int i=0;i> readCsv(const string& filename, char separator) { 1039 | pair apair; 1040 | string line, path, classlabel; 1041 | vector> retVect; 1042 | ifstream file(filename.c_str(), ifstream::in); 1043 | if (!file) 1044 | return retVect; 1045 | while (getline(file, line)) { 1046 | stringstream liness(line); 1047 | getline(liness, path, separator); 1048 | getline(liness, classlabel); 1049 | if(!path.empty() && !classlabel.empty()) { 1050 | apair.first = path; 1051 | apair.second = classlabel; 1052 | retVect.push_back(apair); 1053 | } 1054 | 1055 | } 1056 | return retVect; 1057 | } 1058 | ////获得ini文件中的值 1059 | CString GetInitString( CString Name1 ,CString Name2){ 1060 | char c[100] ; 1061 | memset( c ,0 ,100) ; 1062 | CString csCfgFilePath; 1063 | GetModuleFileName(NULL, csCfgFilePath.GetBufferSetLength(MAX_PATH+1), MAX_PATH); 1064 | csCfgFilePath.ReleaseBuffer(); 1065 | int nPos = csCfgFilePath.ReverseFind ('\\'); 1066 | csCfgFilePath = csCfgFilePath.Left (nPos); 1067 | csCfgFilePath += "\\Config" ; 1068 | BOOL br = GetPrivateProfileString(Name1,Name2 ,"0",c, 100 , csCfgFilePath) ; 1069 | CString rstr ; 1070 | rstr.Format("%s" , c) ; 1071 | return rstr ; 1072 | } 1073 | //写入ini文件中的值 1074 | void WriteInitString( CString Name1 ,CString Name2 ,CString strvalue){ 1075 | CString csCfgFilePath; 1076 | GetModuleFileName(NULL, csCfgFilePath.GetBufferSetLength(MAX_PATH+1), MAX_PATH); 1077 | csCfgFilePath.ReleaseBuffer(); 1078 | int nPos = csCfgFilePath.ReverseFind ('\\'); 1079 | csCfgFilePath = csCfgFilePath.Left (nPos); 1080 | csCfgFilePath += "\\Config" ; 1081 | BOOL br = WritePrivateProfileString(Name1 ,Name2 ,strvalue ,csCfgFilePath) ; 1082 | if ( !br) 1083 | TRACE("savewrong") ; 1084 | } 1085 | 1086 | ////获得当前目录路径 1087 | //static CString GetLocalPath(){ 1088 | // CString csCfgFilePath; 1089 | // GetModuleFileName(NULL, csCfgFilePath.GetBufferSetLength(MAX_PATH+1), MAX_PATH); 1090 | // csCfgFilePath.ReleaseBuffer(); 1091 | // int nPos = csCfgFilePath.ReverseFind ('\\'); 1092 | // csCfgFilePath = csCfgFilePath.Left (nPos); 1093 | // return csCfgFilePath; 1094 | //} 1095 | 1096 | //获得.exe路径 1097 | static CString GetExePath() 1098 | { 1099 | CString strPath; 1100 | GetModuleFileName(NULL,strPath.GetBufferSetLength(MAX_PATH+1),MAX_PATH); 1101 | strPath.ReleaseBuffer(); 1102 | return strPath; 1103 | } 1104 | 1105 | //开机自动运行 1106 | static BOOL SetAutoRun(CString strPath,bool flag) 1107 | { 1108 | CString str; 1109 | HKEY hRegKey; 1110 | BOOL bResult; 1111 | str=_T("Software\\Microsoft\\Windows\\CurrentVersion\\Run"); 1112 | if(RegOpenKey(HKEY_LOCAL_MACHINE, str, &hRegKey) != ERROR_SUCCESS) 1113 | bResult=FALSE; 1114 | else 1115 | { 1116 | _splitpath(strPath.GetBuffer(0),NULL,NULL,str.GetBufferSetLength(MAX_PATH+1),NULL); 1117 | strPath.ReleaseBuffer(); 1118 | str.ReleaseBuffer();//str是键的名字 1119 | if (flag){ 1120 | if(::RegSetValueEx( hRegKey,str,0,REG_SZ,(CONST BYTE *)strPath.GetBuffer(0),strPath.GetLength() ) != ERROR_SUCCESS) 1121 | bResult=FALSE; 1122 | else 1123 | bResult=TRUE; 1124 | }else{ 1125 | if( ::RegDeleteValue(hRegKey,str) != ERROR_SUCCESS) 1126 | bResult=FALSE; 1127 | else 1128 | bResult=TRUE; 1129 | } 1130 | strPath.ReleaseBuffer(); 1131 | } 1132 | return bResult; 1133 | } 1134 | 1135 | #pragma endregion 文件操作 1136 | 1137 | #pragma region 字符串操作 1138 | //string替换 1139 | void string_replace(string & strBig, const string & strsrc, const string &strdst) 1140 | { 1141 | string::size_type pos=0; 1142 | string::size_type srclen=strsrc.size(); 1143 | string::size_type dstlen=strdst.size(); 1144 | while( (pos=strBig.find(strsrc, pos)) != string::npos) 1145 | { 1146 | strBig.replace(pos, srclen, strdst); 1147 | pos += dstlen; 1148 | } 1149 | } 1150 | 1151 | //C++的spilt函数 1152 | void SplitString(const string& s, vector& v, const string& c){ 1153 | std::string::size_type pos1, pos2; 1154 | pos2 = s.find(c); 1155 | pos1 = 0; 1156 | while(std::string::npos != pos2){ 1157 | v.push_back(s.substr(pos1, pos2-pos1)); 1158 | pos1 = pos2 + c.size(); 1159 | pos2 = s.find(c, pos1); 1160 | } 1161 | if(pos1 != s.length()) 1162 | v.push_back(s.substr(pos1)); 1163 | } 1164 | //! 通过文件夹名称获取文件名,不包括后缀 1165 | void getFileName(const string& filepath, string& name,string& lastname){ 1166 | vector spilt_path; 1167 | SplitString(filepath, spilt_path, "\\"); 1168 | int spiltsize = spilt_path.size(); 1169 | string filename = ""; 1170 | if (spiltsize != 0){ 1171 | filename = spilt_path[spiltsize-1]; 1172 | vector spilt_name; 1173 | SplitString(filename, spilt_name, "."); 1174 | int name_size = spilt_name.size(); 1175 | if (name_size != 0) 1176 | name = spilt_name[0]; 1177 | lastname = spilt_name[name_size-1]; 1178 | } 1179 | } 1180 | void getFileName(const string& filepath, string& name){ 1181 | vector spilt_path; 1182 | SplitString(filepath, spilt_path, "\\"); 1183 | int spiltsize = spilt_path.size(); 1184 | string filename = ""; 1185 | if (spiltsize != 0){ 1186 | filename = spilt_path[spiltsize-1]; 1187 | vector spilt_name; 1188 | SplitString(filename, spilt_name, "."); 1189 | int name_size = spilt_name.size(); 1190 | if (name_size != 0) 1191 | name = spilt_name[0]; 1192 | } 1193 | } 1194 | 1195 | #pragma endregion 字符串操作 1196 | 1197 | #pragma region excel操作 1198 | ////////////////////////////////////////////////////////////////////////////// 1199 | //名称:GetExcelDriver 1200 | //功能:获取ODBC中Excel驱动 1201 | //作者:徐景周(jingzhou_xu@163.net) 1202 | //组织:未来工作室(Future Studio) 1203 | //日期:2002.9.1 1204 | ///////////////////////////////////////////////////////////////////////////// 1205 | CString GetExcelDriver() 1206 | { 1207 | char szBuf[2001]; 1208 | WORD cbBufMax = 2000; 1209 | WORD cbBufOut; 1210 | char *pszBuf = szBuf; 1211 | CString sDriver; 1212 | 1213 | // 获取已安装驱动的名称(涵数在odbcinst.h里) 1214 | if (!SQLGetInstalledDrivers(szBuf, cbBufMax, &cbBufOut)) 1215 | return ""; 1216 | 1217 | // 检索已安装的驱动是否有Excel... 1218 | do 1219 | { 1220 | if (strstr(pszBuf, "Excel") != 0) 1221 | { 1222 | //发现 ! 1223 | sDriver = CString(pszBuf); 1224 | break; 1225 | } 1226 | pszBuf = strchr(pszBuf, '\0') + 1; 1227 | } 1228 | while (pszBuf[1] != '\0'); 1229 | 1230 | return sDriver; 1231 | } 1232 | 1233 | /////////////////////////////////////////////////////////////////////////////// 1234 | // BOOL MakeSurePathExists( CString &Path,bool FilenameIncluded) 1235 | // 参数: 1236 | // Path 路径 1237 | // FilenameIncluded 路径是否包含文件名 1238 | // 返回值: 1239 | // 文件是否存在 1240 | // 说明: 1241 | // 判断Path文件(FilenameIncluded=true)是否存在,存在返回TURE,不存在返回FALSE 1242 | // 自动创建目录 1243 | // 1244 | /////////////////////////////////////////////////////////////////////////////// 1245 | BOOL MakeSurePathExists( CString &Path,bool FilenameIncluded) 1246 | { 1247 | int Pos=0; 1248 | while((Pos=Path.Find('\\',Pos+1))!=-1) 1249 | CreateDirectory(Path.Left(Pos),NULL); 1250 | if(!FilenameIncluded) 1251 | CreateDirectory(Path,NULL); 1252 | // return ((!FilenameIncluded)?!_access(Path,0): 1253 | // !_access(Path.Left(Path.ReverseFind('\\')),0)); 1254 | return !_access(Path,0); 1255 | } 1256 | 1257 | //获得默认的文件名 1258 | //2018年6月26日 将其升级为可以输入路径参数的 1259 | BOOL GetDefaultXlsFileName(CString& sExcelFile) 1260 | { 1261 | /////默认文件名:yyyymmddhhmmss.xls 1262 | CString timeStr; 1263 | CTime day; 1264 | day=CTime::GetCurrentTime(); 1265 | int filenameday,filenamemonth,filenameyear,filehour,filemin,filesec; 1266 | filenameday=day.GetDay();//dd 1267 | filenamemonth=day.GetMonth();//mm月份 1268 | filenameyear=day.GetYear();//yyyy 1269 | filehour=day.GetHour();//hh 1270 | filemin=day.GetMinute();//mm分钟 1271 | filesec=day.GetSecond();//ss 1272 | timeStr.Format("%04d%02d%02d%02d%02d%02d",filenameyear,filenamemonth,filenameday,filehour,filemin,filesec); 1273 | if (sExcelFile == "") 1274 | { 1275 | sExcelFile = timeStr + ".xls"; //获取随机时间的文件名称 1276 | }else{ 1277 | sExcelFile = sExcelFile+".xls"; 1278 | } 1279 | 1280 | //打开选择路径窗口 1281 | CString pathName; 1282 | CString defaultDir = _T("C:\\outtest"); 1283 | CString fileName=sExcelFile; 1284 | CString szFilters= _T("xls(*.xls)"); 1285 | CFileDialog dlg(FALSE,defaultDir,fileName,OFN_HIDEREADONLY|OFN_READONLY,szFilters,NULL); 1286 | if(dlg.DoModal()==IDOK){ 1287 | //获得保存位置 1288 | pathName = dlg.GetPathName(); 1289 | }else{ 1290 | return FALSE; 1291 | } 1292 | 1293 | sExcelFile = pathName; 1294 | return TRUE; 1295 | } 1296 | 1297 | /////////////////////////////////////////////////////////////////////////////// 1298 | // void GetExcelDriver(CListCtrl* pList, CString strTitle) 1299 | // 参数: 1300 | // pList 需要导出的List控件指针 1301 | // strTitle 导出的数据表标题 1302 | // 说明: 1303 | // 导出CListCtrl控件的全部数据到Excel文件。Excel文件名由用户通过“另存为” 1304 | // 对话框输入指定。创建名为strTitle的工作表,将List控件内的所有数据(包括 1305 | // 列名和数据项)以文本的形式保存到Excel工作表中。保持行列关系。 1306 | // 1307 | // edit by [r]@dotlive.cnblogs.com 1308 | // 2016年8月12日 修改为可以保存多个表的模式 1309 | /////////////////////////////////////////////////////////////////////////////// 1310 | CString ExportListToExcel(CString sExcelFile,CListCtrl* pList, CString strTitle) 1311 | { 1312 | CString warningStr; 1313 | if (pList->GetItemCount ()>0) { 1314 | CDatabase database; 1315 | 1316 | 1317 | CString sSql; 1318 | CString tableName = strTitle; 1319 | 1320 | // 检索是否安装有Excel驱动 "Microsoft Excel Driver (*.xls)" 1321 | CString sDriver; 1322 | sDriver = GetExcelDriver(); 1323 | if (sDriver.IsEmpty()) 1324 | { 1325 | // 没有发现Excel驱动 1326 | AfxMessageBox("没有安装Excel!\n请先安装Excel软件才能使用导出功能!"); 1327 | return NULL; 1328 | } 1329 | 1330 | ///默认文件名 1331 | /* CString sExcelFile; 1332 | if (!GetDefaultXlsFileName(sExcelFile)) 1333 | return NULL;*/ 1334 | 1335 | // 创建进行存取的字符串 1336 | sSql.Format("DRIVER={%s};DSN='';FIRSTROWHASNAMES=1;READONLY=FALSE;CREATE_DB=\"%s\";DBQ=%s",sDriver, sExcelFile, sExcelFile); 1337 | 1338 | // 创建数据库 (既Excel表格文件) 1339 | if( database.OpenEx(sSql,CDatabase::noOdbcDialog) ) 1340 | { 1341 | // 创建表结构 1342 | int i; 1343 | LVCOLUMN columnData; 1344 | CString columnName; 1345 | int columnNum = 0; 1346 | CString strH; 1347 | CString strV; 1348 | 1349 | sSql = ""; 1350 | strH = ""; 1351 | columnData.mask = LVCF_TEXT; 1352 | columnData.cchTextMax =100; 1353 | columnData.pszText = columnName.GetBuffer (100); 1354 | for(i=0;pList->GetColumn(i,&columnData);i++) 1355 | { 1356 | if (i!=0) 1357 | { 1358 | sSql = sSql + ", " ; 1359 | strH = strH + ", " ; 1360 | } 1361 | sSql = sSql + " " + columnData.pszText +" TEXT"; 1362 | strH = strH + " " + columnData.pszText +" "; 1363 | } 1364 | columnName.ReleaseBuffer (); 1365 | columnNum = i; 1366 | 1367 | sSql = "CREATE TABLE " + tableName + " ( " + sSql + " ) "; 1368 | database.ExecuteSQL(sSql); 1369 | 1370 | 1371 | // 插入数据项 1372 | int nItemIndex; 1373 | for (nItemIndex=0;nItemIndexGetItemCount ();nItemIndex++){ 1374 | strV = ""; 1375 | for(i=0;iGetItemText(nItemIndex,i) +"' "; 1382 | } 1383 | 1384 | sSql = "INSERT INTO "+ tableName 1385 | +" ("+ strH + ")" 1386 | +" VALUES("+ strV + ")"; 1387 | database.ExecuteSQL(sSql); 1388 | } 1389 | 1390 | } 1391 | 1392 | // 关闭数据库 1393 | database.Close(); 1394 | return sExcelFile; 1395 | } 1396 | } 1397 | //2个datesheet的模式 1398 | CString ExportListToExcel(CListCtrl* pList, CString strTitle,CListCtrl* pList2,CString strTitle2) 1399 | { 1400 | CString warningStr; 1401 | if (pList->GetItemCount ()>0) { 1402 | CDatabase database; 1403 | CString sDriver; 1404 | CString sExcelFile; 1405 | CString sSql; 1406 | CString tableName = strTitle; 1407 | CString tableName2 = strTitle2; 1408 | 1409 | // 检索是否安装有Excel驱动 "Microsoft Excel Driver (*.xls)" 1410 | sDriver = GetExcelDriver(); 1411 | if (sDriver.IsEmpty()) 1412 | { 1413 | // 没有发现Excel驱动 1414 | AfxMessageBox("没有安装Excel!\n请先安装Excel软件才能使用导出功能!"); 1415 | return NULL; 1416 | } 1417 | 1418 | ///默认文件名 1419 | if (!GetDefaultXlsFileName(sExcelFile)) 1420 | return NULL; 1421 | 1422 | // 创建进行存取的字符串 1423 | sSql.Format("DRIVER={%s};DSN='';FIRSTROWHASNAMES=1;READONLY=FALSE;CREATE_DB=\"%s\";DBQ=%s",sDriver, sExcelFile, sExcelFile); 1424 | 1425 | // 创建数据库 (既Excel表格文件) 1426 | if( database.OpenEx(sSql,CDatabase::noOdbcDialog) ) 1427 | { 1428 | // 创建表结构1 1429 | int i; 1430 | LVCOLUMN columnData; 1431 | LVCOLUMN columnData2; 1432 | CString columnName; 1433 | CString columnName2; 1434 | int columnNum = 0; 1435 | CString strH; 1436 | CString strV; 1437 | 1438 | sSql = ""; 1439 | strH = ""; 1440 | columnData.mask = LVCF_TEXT; 1441 | columnData.cchTextMax =100; 1442 | columnData.pszText = columnName.GetBuffer (100); 1443 | columnData2.mask = LVCF_TEXT; 1444 | columnData2.cchTextMax =100; 1445 | columnData2.pszText = columnName2.GetBuffer (100); 1446 | // 插入数据项1 1447 | for(i=0;pList->GetColumn(i,&columnData);i++) 1448 | { 1449 | if (i!=0) 1450 | { 1451 | sSql = sSql + ", " ; 1452 | strH = strH + ", " ; 1453 | } 1454 | sSql = sSql + " " + columnData.pszText +" TEXT"; 1455 | strH = strH + " " + columnData.pszText +" "; 1456 | } 1457 | columnName.ReleaseBuffer (); 1458 | columnNum = i; 1459 | 1460 | sSql = "CREATE TABLE " + tableName + " ( " + sSql + " ) "; 1461 | database.ExecuteSQL(sSql); 1462 | 1463 | 1464 | int nItemIndex; 1465 | for (nItemIndex=0;nItemIndexGetItemCount();nItemIndex++){ 1466 | strV = ""; 1467 | for(i=0;iGetItemText(nItemIndex,i) +"' "; 1474 | } 1475 | 1476 | sSql = "INSERT INTO "+ tableName 1477 | +" ("+ strH + ")" 1478 | +" VALUES("+ strV + ")"; 1479 | database.ExecuteSQL(sSql); 1480 | } 1481 | //插入数据项2 1482 | sSql = ""; 1483 | strH=""; 1484 | int columnNum2 = 0; 1485 | 1486 | for(int i=0;pList2->GetColumn(i,&columnData2);i++) 1487 | { 1488 | if (i!=0) 1489 | { 1490 | sSql = sSql + ", " ; 1491 | strH = strH + ", " ; 1492 | } 1493 | sSql = sSql + " " + columnData2.pszText +" TEXT"; 1494 | strH = strH + " " + columnData2.pszText +" "; 1495 | } 1496 | columnName2.ReleaseBuffer (); 1497 | columnNum2 = i; 1498 | 1499 | sSql = "CREATE TABLE " + tableName2 + " ( " + sSql + " ) "; 1500 | database.ExecuteSQL(sSql); 1501 | 1502 | for (nItemIndex=0;nItemIndexGetItemCount ();nItemIndex++){ 1503 | strV = ""; 1504 | for(i=0;iGetItemText(nItemIndex,i) +"' "; 1511 | } 1512 | 1513 | sSql = "INSERT INTO "+ tableName2 1514 | +" ("+ strH + ")" 1515 | +" VALUES("+ strV + ")"; 1516 | database.ExecuteSQL(sSql); 1517 | } 1518 | } 1519 | // 关闭数据库 1520 | database.Close(); 1521 | 1522 | warningStr.Format("导出文件保存于%s!",sExcelFile); 1523 | AfxMessageBox(warningStr); 1524 | return sExcelFile; 1525 | } 1526 | } 1527 | #pragma endregion excel操作 1528 | 1529 | } -------------------------------------------------------------------------------- /GOCVHelper_2020_04_20.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsxyhelu/GOCvHelper/7d8b19decb86c672fd8026bba8e86a8a0ca5b2f6/GOCVHelper_2020_04_20.cpp -------------------------------------------------------------------------------- /GoCvHelper0.7b.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsxyhelu/GOCvHelper/7d8b19decb86c672fd8026bba8e86a8a0ca5b2f6/GoCvHelper0.7b.cpp -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GOCvHelper 2 | GOCVHelper(GreenOpen Computer Version Helper )是我在这几年编写图像处理程序的过程中积累下来的函数库。主要是对Opencv的适当扩展和在实现Mfc程序时候的功能增强。 3 | 这里将算法库开放源代码,并且编写一系列blog对函数实现进行说明。目的是在于“取之于互联网,用之于互联网”。并且也希望该库能够继续发展下去。 4 | 由于算法库基于Opencv和Mfc进行编写,所以要求阅读使用者具备一定基础。 5 | 最终提交的是GOCVHelper.h 和GOCVHelper版本号.cpp两个文件。 6 | 具体介绍地址为:http://www.cnblogs.com/jsxyhelu/p/5904251.html 7 | --------------------------------------------------------------------------------