You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1034 lines
23 KiB
C++

1 month ago
#include "coutlinedetector.h"
#include "GSurface.h"
#include <QDebug>
#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<cv::Point2f> >& inputContour, std::vector< std::vector<cv::Point2f> > & 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<cv::Point2f>& inputContour, std::vector<cv::Point2f>& 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<std::vector<cv::Point2f>>& 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<Point2f> cbd;
// CombineContours(cbd);
//
// vector<vector<Point2f>> vv; vv.push_back(cbd);
// WriteContours(vv, "d:/combined_contours.dfd");
//
// //转换为实际坐标
// vector< vector<Point2f>> realCoords;
// realCoords.resize(1);
// contourToRealCoords(cbd,realCoords[0],0);
//
//
// return WriteContours(realCoords,path);
//
// /*
//
// vector<vector<cv::Point2f> > ReservedContours;
//
// FilterContours(m_minArea,ReservedContours);
// vector<vector<cv::Point2f>> 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<Point2f>& src, CPolyline& dstLine )
{
dstLine.Clear();
for(auto p:src)
{
dstLine.AddPoint(p.x,p.y,0);
}
}
void fromPline(CPolyline& src, vector<Point2f>& 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<CPolyline*> 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<vector<int>> hierarchy;
hierarchy.resize(m_hierarchy.size(), vector<int>(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<Point2f> &dstContour)
// {
//
// vector<CPolyline*> 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<vector<int>> hierarchy;
// hierarchy.resize(m_hierarchy.size(),vector<int>(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<std::vector<Point> > &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<uchar>(j,i))
{
m_matMorp.at<uchar>(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<std::vector<cv::Point2f>>& COutlineDetector::GetOriginalContours(void)
{
return m_originalContours;
}
//获取沉积相轮廓线
std::vector<std::vector<cv::Point2f>>& COutlineDetector::GetFaciesContours(void)
{
return m_faciesContours;
}
void COutlineDetector::SmoothRootContour(std::vector<cv::Point2f>& 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<Point2f> 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<cv::Point2f>& 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<vector<Point>> contours;
vector<Vec4i> 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<vector<Point>> contours;
vector<Vec4i> 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<vector<Point>> 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<std::vector<cv::Point2f>>& 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<uchar>(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<uchar>(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<float>(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存储0xAARRGGBBpc一般小端存储低位在前所以字节顺序就成了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;
}