#include "coutlinedetector.h" #include "GSurface.h" #include #include "PolygonTreeInterface.h" #include "InterfaceElements.h" using namespace std; COutlineDetector::COutlineDetector() :m_pSurface(nullptr) , m_nIterations(3) , m_zlower(0) , m_zupper(0) , m_smoothTimes(0) , m_minArea(0) { } void COutlineDetector::clear() { // m_pSurface = nullptr; m_originalContours.clear(); m_matMorp.release(); } //设置最小轮廓线面积 void COutlineDetector::setContourMinArea(float area) { if(area < 0) area = 0; m_minArea = area; } //设置输出轮廓线平滑次数 void COutlineDetector::setContourSmoothTimes(unsigned int times) { if(times < 1) times = 0; m_smoothTimes = times; } void COutlineDetector::setSurface(GSurface *pSurf, float zlower, float zupper, unsigned int nIters) { m_pSurface = pSurf; setIterations(nIters); setTargetZRange(zlower,zupper); } void COutlineDetector::setTargetZRange(float zlower, float zupper) { m_zlower = zlower; m_zupper = zupper; } bool COutlineDetector::ProcessImage() { if(nullptr == m_pSurface) return false; morphologySolve(); createContours(); SmoothContours(); return true; } void COutlineDetector::multiContourToRealCoords(const std::vector< std::vector >& inputContour, std::vector< std::vector > & dstContours, int smoothTimes) { dstContours.resize(inputContour.size() ); for(int i = 0; i < inputContour.size(); i ++ ) contourToRealCoords(inputContour[i],dstContours[i],smoothTimes); } void COutlineDetector::contourToRealCoords(const std::vector& inputContour, std::vector& dstContour, int smoothTimes) { int N = inputContour.size(); double* x = new double[N]; double* y = new double[N]; float x0 = m_pSurface->X(0); float y0 = m_pSurface->Y(0); float dx = m_pSurface->DeltX(); float dy = m_pSurface->DeltY(); double tx,ty; for(int i = 0; i < N; i ++ ) { tx = inputContour[i].x; ty = inputContour[i].y; x[i] = x0 + dx* tx; y[i] = y0 + dy*ty; } if(N > 4 && smoothTimes > 0) { Smooth53(x,N,smoothTimes); Smooth53(y,N,smoothTimes); } dstContour.resize(N); for(int i = 0; i < N; i ++ ) { dstContour[i].x = x[i]; dstContour[i].y = y[i]; } delete []x; delete []y; } bool COutlineDetector::WriteContours( std::vector>& contours, const char* path) { FILE* fw = fopen(path,"w"); if(nullptr == fw) return false; float x, y; for(int j = 0; j < contours.size(); j ++ ) { /* if(contours[j].size() < 3) continue;*/ fprintf(fw,"Pline.%d\n",j); for(auto& p :contours[j] ) { x = p.x; y = p.y; fprintf(fw,"%.12g,%.12g\n",x,y); } auto p = contours[j][0]; x = p.x; y = p.y; fprintf(fw,"%.12g,%.12g\n",x,y); fprintf(fw,"\n"); } fclose(fw); return true; } // bool COutlineDetector::WriteContours(const char *path) // { // // // SmoothContours(); //平滑轮廓线 // // vector cbd; // CombineContours(cbd); // // vector> vv; vv.push_back(cbd); // WriteContours(vv, "d:/combined_contours.dfd"); // // //转换为实际坐标 // vector< vector> realCoords; // realCoords.resize(1); // contourToRealCoords(cbd,realCoords[0],0); // // // return WriteContours(realCoords,path); // // /* // // vector > ReservedContours; // // FilterContours(m_minArea,ReservedContours); // vector> contours; // multiContourToRealCoords(ReservedContours,contours,m_smoothTimes); // // // return WriteContours(contours,path); // // */ // // //// float x, y; //// for(int j = 0; j < contours.size(); j ++ ) //// { //// if(contours[j].size() < 3) //// continue; // //// fprintf(fw,"Pline\n"); // //// for(auto& p :contours[j] ) //// { //// x = p.x; //// y = p.y; //// fprintf(fw,"%.12g,%.12g\n",x,y); //// } // //// auto p = contours[j][0]; //// x = p.x; //// y = p.y; //// fprintf(fw,"%.12g,%.12g\n",x,y); // //// fprintf(fw,"\n"); //// } // // // // //// float x0 = m_pSurface->X(0); //// float y0 = m_pSurface->Y(0); // //// float dx = m_pSurface->DeltX(); //// float dy = m_pSurface->DeltY(); // //// float x,y; //// for(int j = 0; j < m_contours.size(); j ++ ) //// { //// if(m_contours[j].size() < 3) //// continue; // //// fprintf(fw,"Pline\n"); // //// for(auto& p :m_contours[j] ) //// { //// x = x0 + dx* p.x; //// y = y0 + dy*p.y; //// fprintf(fw,"%.12g,%.12g\n",x,y); //// } // //// auto p = m_contours[j][0]; //// x = x0 + dx* p.x; //// y = y0 + dy*p.y; //// fprintf(fw,"%.12g,%.12g\n",x,y); // //// fprintf(fw,"\n"); //// } // // // // //// fclose(fw); // // return true; // } // void toPline(vector& src, CPolyline& dstLine ) { dstLine.Clear(); for(auto p:src) { dstLine.AddPoint(p.x,p.y,0); } } void fromPline(CPolyline& src, vector& dst) { CPointXYZ pt; dst.resize(src.GetSize()); for(int i = 0; i < src.GetSize(); i ++ ) { pt = src.GetPoint(i); dst[i].x = pt.x0; dst[i].y = pt.y0; } } //生成沉积相轮廓线,局部合并内部包含孔洞的轮廓线 int COutlineDetector::CreateFaciesContours(void) { m_faciesContours.clear(); vector polygons; int i = 0; char chs[20]; polygons.reserve(m_originalContours.size()); for (auto& p : m_originalContours) { CPolyline* pl = new CPolyline; toPline(p, *pl); sprintf(chs, "%d", i); pl->SetName(chs); pl->setClosed(); i++; polygons.push_back(pl); } vector> hierarchy; hierarchy.resize(m_hierarchy.size(), vector(4, -1)); for (i = 0; i < m_hierarchy.size(); i++) { for (int j = 0; j < 4; j++) hierarchy[i][j] = m_hierarchy[i][j]; } CPolygonTreeInterface pgnTree; pgnTree.SetPolygons(polygons); pgnTree.SetHierarchy(hierarchy); double dx = m_pSurface->DeltX(); double dy = m_pSurface->DeltY(); double minArea = m_minArea / (dx*dy); if(minArea < 1e-4) minArea = 1e-4; pgnTree.Create(minArea); int N = pgnTree.GetResultPolygons().size(); m_faciesContours.resize(N); for (int i = 0; i < N; i++) { fromPline(*pgnTree.GetResultPolygons().at(i), m_faciesContours[i]); } //清空临时变量 for (auto p : polygons) delete p; return N; } // void COutlineDetector::CombineContours(std::vector &dstContour) // { // // vector polygons; // // int i = 0; // char chs[20]; // polygons.reserve(m_originalContours.size()); // for(auto& p: m_originalContours) // { // CPolyline* pl = new CPolyline; // toPline(p,*pl); // sprintf(chs,"%d",i); // pl->SetName(chs); // pl->setClosed(); // i++; // polygons.push_back(pl); // } // // vector> hierarchy; // hierarchy.resize(m_hierarchy.size(),vector(4,-1)); // // for(i = 0; i < m_hierarchy.size(); i ++ ) // { // for(int j = 0; j < 4; j ++ ) // hierarchy[i][j] = m_hierarchy[i][j]; // } // // // CPolygonTreeInterface pgnTree; // pgnTree.SetPolygons(polygons); // pgnTree.SetHierarchy(hierarchy); // // double dx = m_pSurface->DeltX(); // double dy = m_pSurface->DeltY(); // // double minArea = m_minArea/(dx*dy); // // // pgnTree.Create(minArea); // // CPolyline& pc = pgnTree.GetCombinedPolygon(); // // fromPline(pc,dstContour); // // // // // //清空临时变量 // for(auto p:polygons) // delete p; // // // // // // // // //// pgnTree.GetHierarchy().resize(m_hierarchy.size()); // //// for(int i = 0; i < m_hierarchy.size(); i ++ ) //// { //// pgnTree.GetHierarchy()[i].resize(4); //// for(int j = 0; j <4 ; j ++ ) //// pgnTree.GetHierarchy()[i][j] = m_hierarchy[i][j]; //// } // // //// for(int i = 0; i < m_contours.size(); i ++ ) //// { //// CPolyline* pc = new CPolyline; //// for(auto& p: m_contours[i] ) //// pc->AddPoint(p.x,p.y,0); // //// pgnTree.GetPolygons().push_back(pc); // //// } // // // // } // bool COutlineDetector::WriteContours(std::vector > &contours, const char* path) { FILE* fw = fopen(path,"w"); if(nullptr == fw) return false; float x, y; for(int j = 0; j < contours.size(); j ++ ) { if(contours[j].size() < 3) continue; fprintf(fw,"Pline.%d\n",j); for(auto& p :contours[j] ) { x = p.x; y = p.y; fprintf(fw,"%.12g,%.12g\n",x,y); } auto p = contours[j][0]; x = p.x; y = p.y; fprintf(fw,"%.12g,%.12g\n",x,y); fprintf(fw,"\n"); } fclose(fw); } void COutlineDetector::setIterations(unsigned int nIterations) { m_nIterations = nIterations; } void COutlineDetector::morphologySolve() { TraceBorder(); Mat srcMat ; convertSurfaceToMat(m_pSurface,m_zlower,m_zupper, srcMat); //surface to bin mat // imshow("bordermat",m_matBorder); // waitKey(0); m_matSrc = srcMat; Mat bin = srcMat; Mat kernel; kernel = getStructuringElement(MORPH_CROSS, Size(3, 3));//矩形结构元素 Mat materode, matdilate; //膨胀 dilate(bin, matdilate, kernel, Point(-1, -1), m_nIterations); //腐蚀 erode(matdilate, materode, kernel,Point(-1,-1),m_nIterations+1); //imshow("腐蚀", materode); // erode(bin, materode, kernel,Point(-1,-1),m_nIterations); // //imshow("腐蚀", materode); // //膨胀 // dilate(materode, matdilate, kernel, Point(-1, -1), m_nIterations+1); // //imshow("膨胀", matdilate); // // imwrite("d:/腐蚀膨胀后.bmp", matdilate); m_matMorp = matdilate; //由matBorder对越界部分进行修改 for(int j = 0; j < m_matMorp.rows; j ++ ) { for(int i = 0; i < m_matMorp.cols; i ++ ) { if(0 == m_matBorder.at(j,i)) { m_matMorp.at(j,i) = 0; } } } } //五点三次平滑 n 必须大于4 bool COutlineDetector::Smooth53(double* val, int n, int stimes) { if (n < 5 || stimes < 1) return false; int i, k; //k为平滑次数 double *xx = new double[n]; for (k = 0; k < stimes; k ++) { xx[0] = 69 * val[0] + 4 * val[1] - 6 * val[2] + 4 * val[3] - val[4]; xx[0] = xx[0] / 70; xx[1] = 2 * val[0] + 27 * val[1] + 12 * val[2] - 8 * val[3]; xx[1] = (xx[1] + 2 * val[4]) / 35; for (i = 2; i <= n - 3; i++) { xx[i] = -3 * val[i - 2] + 12 * val[i - 1] + 17 * val[i]; xx[i] = (xx[i] + 12 * val[i + 1] - 3 * val[i + 2]) / 35; } xx[n - 2] = 2 * val[n - 5] - 8 * val[n - 4] + 12 * val[n - 3]; xx[n - 2] = (xx[n - 2] + 27 * val[n - 2] + 2 * val[n - 1]) / 35; xx[n - 1] = -val[n - 5] + 4 * val[n - 4] - 6 * val[n - 3]; xx[n - 1] = (xx[n - 1] + 4 * val[n - 2] + 69 * val[n - 1]) / 70; for (int i = 0; i < n; i++) val[i] = xx[i]; } delete []xx; return true; } void COutlineDetector::SmoothContours() { int N = m_originalContours.size(); for(int i = 0; i < N; i ++ ) { //一级轮廓线 if(-1 == m_hierarchy[i][3] ) SmoothRootContour(m_originalContours[i],m_smoothTimes); else SmoothContour(m_originalContours[i],m_smoothTimes); } } std::vector>& COutlineDetector::GetOriginalContours(void) { return m_originalContours; } //获取沉积相轮廓线 std::vector>& COutlineDetector::GetFaciesContours(void) { return m_faciesContours; } void COutlineDetector::SmoothRootContour(std::vector& inputContour, int nTimes) { if(nTimes < 1 || inputContour.size() < 4) return; int N = inputContour.size(); //查找inputContour在边界上的部分 int istart = 0; int iend = 0; int i = 0; vector subline; while(i < N) { if( !isPtOnBorder(inputContour[i])) //找到不在边界上的首点 { istart = i; for( i = istart +1; i < N; i ++ ) { if( N-1 == i ) { iend = N-1; break; } else if(isPtOnBorder(inputContour[i])) { iend = i-1; break; } } if(istart > 0) istart --; if(iend < N-1) iend ++; if(iend - istart > 3) //至少5个点 { //平滑不在边界线上的局部曲线[istart,iend] subline.clear(); subline.reserve(iend-istart +1); for(int k = istart; k <= iend; k ++ ) subline.push_back(inputContour[k]); SmoothContour(subline,nTimes); //将平滑后的局部曲线替换原曲线 int t = 0; for(int k = istart; k <= iend; k ++ ) inputContour[k] = subline[t++]; } } i++; } } bool COutlineDetector::isPtOnBorder(const Point &pt) { for(auto& p:m_border) { if(fabs(pt.x - p.x) < 0.5 && fabs(pt.y-p.y) < 0.5) return true; } return false; } void COutlineDetector::SmoothContour( std::vector& inputContour, int nTimes) { if(nTimes < 1 || inputContour.size() < 4) return; int N = inputContour.size(); double* x = new double[N]; double* y = new double[N]; double tx,ty; for(int i = 0; i < N; i ++ ) { tx = inputContour[i].x; ty = inputContour[i].y; x[i] = tx; y[i] = ty; } if(N > 4 && nTimes > 0) { Smooth53(x,N,nTimes); Smooth53(y,N,nTimes); } for(int i = 0; i < N; i ++ ) { inputContour[i].x = x[i]; inputContour[i].y = y[i]; } delete []x; delete []y; } void COutlineDetector::TraceBorder() { m_border.clear(); convertSurfaceToMat(m_pSurface,m_pSurface->GetRange()[0],m_pSurface->GetRange()[1], m_matBorder); vector> contours; vector hierarchy; // hierarchy 四个参数:下一个,前一个,子轮廓,父轮廓 (不存在则为-1) findContours(m_matBorder, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, Point()); if(contours.size() > 0) { m_border.resize(contours[0].size()); for(int i = 0; i < contours[0].size(); i ++ ) { m_border[i] = contours[0][i]; } } } void COutlineDetector::createContours() { m_originalContours.clear(); m_faciesContours.clear(); vector> contours; vector hierarchy; // hierarchy 四个参数:下一个,前一个,子轮廓,父轮廓 (不存在则为-1) findContours(m_matMorp, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point()); // //查看轮廓线 // Mat tmpMat; // cvtColor(m_matMorp,tmpMat,COLOR_GRAY2RGB); // drawContours(tmpMat,contours,-1,Scalar(0,0,255)); // namedWindow("tmpMat",WINDOW_NORMAL); // imshow("tmpMat",tmpMat); // waitKey(0); ////begin test // WriteContours(contours,"d:/outcontours.dfd"); // FILE * fw = fopen("d:/hierarchy.txt","w"); // int i = 0; // for(auto p: hierarchy) // { // fprintf(fw,"%d:%d,%d,%d,%d\n",i++,p[0],p[1],p[2],p[3]); // } // fclose(fw); ////end test // vector> dstcontous; // dstcontous.resize(contours.size()); m_originalContours.resize(contours.size() ); for (int i = 0; i < contours.size(); i ++ ) { approxPolyDP(contours[i], m_originalContours[i], 1, true); } m_hierarchy.assign(hierarchy.begin(),hierarchy.end() ); // m_hierarchy.clear(); // //去除首个曲线(总轮廓线) // m_hierarchy.reserve(hierarchy.size()-1); // for(int i = 1; i < hierarchy.size(); i ++ ) // { // cv::Vec4i tmp = hierarchy[i]; // for(int j = 0; j < 4; j ++ ) // { // tmp[j] -= 1; // if(tmp[j] < -1) // tmp[j] = -1; // } // m_hierarchy.push_back(tmp); // } // drawContours(m_matSrc, m_contours, -1, Scalar(255, 0, 0)); // namedWindow("轮廓线",WINDOW_NORMAL); // imshow("轮廓线",m_matSrc); // //imshow("轮廓", big); // imwrite("d:/轮廓线.bmp", img); // //WriteContours("d:/等值线.dfd", contours, 10, 10); // waitKey(0); // TraceBorder();//追踪边界轮廓线 } void COutlineDetector::FilterContours(float minArea, std::vector>& ReservedContours) { double dx = m_pSurface->DeltX(); double dy = m_pSurface->DeltY(); double gridArea = dx*dy; //对应图片上的面积 double imArea = minArea/gridArea; if(m_originalContours.empty() || imArea < 1e-4 ) { ReservedContours.assign(m_originalContours.begin(),m_originalContours.end() ); return; } ReservedContours.reserve(m_originalContours.size() ); for(auto& p:m_originalContours) { if(p.size() < 3) continue; double area = contourArea(p); if(area < imArea) continue; ReservedContours.push_back(p); } } Mat &COutlineDetector::getResultMat() { return m_matMorp; } void COutlineDetector::convertSurfaceToMat(GSurface *pSrcSurf, float zlower, float zupper, Mat &dstMat) { int nx = pSrcSurf->XNum()-1; int ny = pSrcSurf->YNum() - 1; // Mat构造:行数,列数,存储结构,数据,step每行多少字节 dstMat = cv::Mat::zeros(ny,nx, CV_8UC1) ; //缺省黑色 float v; zlower -= 1e-4; zupper += 1e-4; for(int j = 0; j < ny; j ++) { for(int i = 0; i < nx; i ++) { v = pSrcSurf->Z(i,j); if(v > zlower && v < zupper) { dstMat.at(j,i) = 255; // 对指定的像素赋值为白色 } } } } bool COutlineDetector::convertMatToSurface(Mat& mat, float x0, float y0, float deltX, float deltY, GSurface* pDstSurf) { int nx = mat.cols; int ny = mat.rows; if(nx < 2 || ny < 2) return false; pDstSurf->Create(nx,ny,x0,y0,deltX,deltY); switch (mat.type()) { case CV_8UC1: for(int j = 0; j < ny; j ++) { uchar* p = mat.ptr(j); for(int i = 0; i < nx; i ++ ) { pDstSurf->setZ(i,j,(unsigned int)p[i]); } } break; case CV_32FC1: //float for(int j = 0; j < ny; j ++) { float* p = mat.ptr(j); for(int i = 0; i < nx; i ++ ) { pDstSurf->setZ(i,j,(float)p[i]); } } break; default: return false; break; } pDstSurf->CalcRange(); return true; } bool COutlineDetector::ConvertMatToQImage(cv::Mat & mat, QImage & image, QImage::Format fmt ) { if (mat.empty()) { qDebug() << "load image fail!"; return false; } switch (mat.type()) { case CV_8UC1: { image = QImage(mat.cols, mat.rows, QImage::Format_Indexed8); //set the color table image.setColorCount(256); for (int i = 0; i < 256; i++) image.setColor(i, qRgb(i, i, i)); //copy input mat uchar* pSrc = mat.data; for (int row = 0; row < mat.rows; row++) { uchar* pDest = image.scanLine(row); memcpy(pDest, pSrc, mat.cols); pSrc += mat.step; } break; } case CV_8UC3: image = QImage((const unsigned char*)mat.data, mat.cols, mat.rows, mat.step, QImage::Format_RGB888); image = image.rgbSwapped(); // BRG转为RGB // Qt5.14增加了Format_BGR888 // image = QImage((const unsigned char*)mat.data, mat.cols, mat.rows, mat.cols * 3, QImage::Format_BGR888); break; case CV_8UC4: image = QImage((const unsigned char*)mat.data, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32); break; case CV_16UC4: image = QImage((const unsigned char*)mat.data, mat.cols, mat.rows, mat.step, QImage::Format_RGBA8888);// Format_RGBA64); image = image.rgbSwapped(); // BRG转为RGB break; } if (!image.isNull()) image = image.convertToFormat(fmt); return true; } bool COutlineDetector::ConvertQImageToMat(const QImage &image, cv::Mat& mat) { switch (image.format()) { case QImage::Format_Grayscale8: // 灰度图,每个像素点1个字节(8位) // Mat构造:行数,列数,存储结构,数据,step每行多少字节 mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); break; case QImage::Format_ARGB32: // uint32存储0xAARRGGBB,pc一般小端存储低位在前,所以字节顺序就成了BGRA case QImage::Format_RGB32: // Alpha为FF case QImage::Format_ARGB32_Premultiplied: mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); break; case QImage::Format_RGB888: // RR,GG,BB字节顺序存储 mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); // opencv需要转为BGR的字节顺序 cv::cvtColor(mat, mat, cv::COLOR_RGB2BGR); break; case QImage::Format_Indexed8: mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.bits(), image.bytesPerLine()); break; //case QImage::Format_RGBA64: // uint64存储,顺序和Format_ARGB32相反,RGBA // mat = cv::Mat(image.height(), image.width(), CV_16UC4, (void*)image.constBits(), image.bytesPerLine()); // // opencv需要转为BGRA的字节顺序 // cv::cvtColor(mat, mat, cv::COLOR_RGBA2BGRA); // break; } return true; }