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++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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;
}