Commit 42d41a23 张士柳

1 个父辈 4a279e0b
...@@ -719,7 +719,7 @@ namespace eyemLib_Sharp ...@@ -719,7 +719,7 @@ namespace eyemLib_Sharp
private static extern int eyemCountObjectE(EyemImage tpImage, string fileName, ref string pNumObj, out EyemImage tpDstImg); private static extern int eyemCountObjectE(EyemImage tpImage, string fileName, ref string pNumObj, out EyemImage tpDstImg);
//异型器件(新版本) //异型器件(新版本)
[DllImport("eyemLib.dll", CharSet = CharSet.None, CallingConvention = CallingConvention.Cdecl)] [DllImport("eyemLib.dll", CharSet = CharSet.None, CallingConvention = CallingConvention.Cdecl)]
private static extern int eyemCountObjectIrregularPartsE(EyemImage tpImage, string fileName, string ccSubType, string ccTplName, double dMinScore, ref string pNumObj, out EyemImage tpDstImg); private static extern int eyemCountObjectIrregularPartsE(EyemImage tpImage, string fileName, string ccTplName, double dMinScore, ref string pNumObj, out EyemImage tpDstImg);
//创建模板匹配模型 //创建模板匹配模型
[DllImport("eyemLib.dll", CharSet = CharSet.None, CallingConvention = CallingConvention.Cdecl)] [DllImport("eyemLib.dll", CharSet = CharSet.None, CallingConvention = CallingConvention.Cdecl)]
private static extern int eyemCreateTemplateModel(EyemImage tpImage, EyemRect tpRoi, string ccTplName); private static extern int eyemCreateTemplateModel(EyemImage tpImage, EyemRect tpRoi, string ccTplName);
...@@ -924,7 +924,7 @@ namespace eyemLib_Sharp ...@@ -924,7 +924,7 @@ namespace eyemLib_Sharp
//eyemCountObject(image, file.Replace(".png", ""), 35, 0, 100, 5, ref pNumObj, out tpDstImg); //eyemCountObject(image, file.Replace(".png", ""), 35, 0, 100, 5, ref pNumObj, out tpDstImg);
//eyemCountObjectIrregularParts(image, file.Replace(".png", ""), 0.1, "IP_LARGE_PARTS", 100, 7, ref pNumObj, out tpDstImg); //eyemCountObjectIrregularParts(image, file.Replace(".png", ""), 0.1, "IP_LARGE_PARTS", 100, 7, ref pNumObj, out tpDstImg);
//eyemCountObjectE(image, fileName, ref pNumObj, out tpDstImg); //eyemCountObjectE(image, fileName, ref pNumObj, out tpDstImg);
eyemCountObjectIrregularPartsE(image, file.Replace(".png", ""), "IP_LARGE_PARTS", "D://批量测试图像模板文件//" + file.Replace(".png", "") + "_tpl.png", 0.7, ref pNumObj, out tpDstImg); eyemCountObjectIrregularPartsE(image, file.Replace(".png", ""), "D://批量测试图像模板文件//" + file.Replace(".png", "") + "_tpl.png", 0.7, ref pNumObj, out tpDstImg);
Bitmap bmp = eyemCvtToBitmap(tpDstImg); Bitmap bmp = eyemCvtToBitmap(tpDstImg);
......
...@@ -835,7 +835,7 @@ extern "C" { ...@@ -835,7 +835,7 @@ extern "C" {
EXPORTS int eyemCountObject(EyemImage tpImage, const char *fileName, double dOffset, int iMinArea, int iMaxArea, int iWinSize, LPSTR *lpszNumObj, EyemImage *tpDstImg); EXPORTS int eyemCountObject(EyemImage tpImage, const char *fileName, double dOffset, int iMinArea, int iMaxArea, int iWinSize, LPSTR *lpszNumObj, EyemImage *tpDstImg);
EXPORTS int eyemCountObjectE(EyemImage tpImage, const char *fileName, LPSTR *lpszNumObj, EyemImage *tpDstImg); EXPORTS int eyemCountObjectE(EyemImage tpImage, const char *fileName, LPSTR *lpszNumObj, EyemImage *tpDstImg);
EXPORTS int eyemCountObjectIrregularParts(EyemImage tpImage, const char *fileName, double dOffset, const char * ccSubType, int iMaxArea, int iWinSize, LPSTR *lpszNumObj, EyemImage *tpDstImg); EXPORTS int eyemCountObjectIrregularParts(EyemImage tpImage, const char *fileName, double dOffset, const char * ccSubType, int iMaxArea, int iWinSize, LPSTR *lpszNumObj, EyemImage *tpDstImg);
EXPORTS int eyemCountObjectIrregularPartsE(EyemImage tpImage, const char *fileName, const char * ccSubType, const char *ccTplName, double dMinScore, LPSTR *lpszNumObj, EyemImage *tpDstImg); EXPORTS int eyemCountObjectIrregularPartsE(EyemImage tpImage, const char *fileName, const char *ccTplName, double dMinScore, LPSTR *lpszNumObj, EyemImage *tpDstImg);
EXPORTS int eyemCreateTemplateModel(EyemImage tpImage, EyemRect tpRoi, const char *ccTplName); EXPORTS int eyemCreateTemplateModel(EyemImage tpImage, EyemRect tpRoi, const char *ccTplName);
EXPORTS int eyemTrackFeature(EyemImage tpPrevImg, EyemImage tpNextImg, EyemRect3 *tpRois, int iRoiNum, int *ipResults); EXPORTS int eyemTrackFeature(EyemImage tpPrevImg, EyemImage tpNextImg, EyemRect3 *tpRois, int iRoiNum, int *ipResults);
EXPORTS int eyemAOIForTSAV(EyemImage tpRefImg, EyemImage tpNextImg, EyemRect3 *tpRois, int iRoiNum); EXPORTS int eyemAOIForTSAV(EyemImage tpRefImg, EyemImage tpNextImg, EyemRect3 *tpRois, int iRoiNum);
......
...@@ -151,6 +151,7 @@ ...@@ -151,6 +151,7 @@
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<OpenMPSupport>true</OpenMPSupport> <OpenMPSupport>true</OpenMPSupport>
<EnableParallelCodeGeneration>true</EnableParallelCodeGeneration> <EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
<MultiProcessorCompilation>false</MultiProcessorCompilation>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
......
...@@ -88,6 +88,39 @@ static cv::Mat getTplMat(cv::Mat &tplMat, double t) ...@@ -88,6 +88,39 @@ static cv::Mat getTplMat(cv::Mat &tplMat, double t)
return tplMatD; return tplMatD;
} }
static void findTrackModel(cv::Mat& image, cv::Mat &tplMat, double t, double trackWidth, cv::Point2f *pts, double &maxVal, cv::Point2f &maxLoc)
{
//图像尺寸
int X = image.cols, Y = image.rows;
//旋转矩形
cv::RotatedRect r(pts[0], pts[1], pts[2]);
//待匹配图像
cv::Rect rr(cv::Point2i(cv::max(r.boundingRect().x - cvRound(trackWidth), 0), \
cv::max(r.boundingRect().y - cvRound(trackWidth), 0)), \
cv::Point2i(cv::min(r.boundingRect().x + r.boundingRect().width + cvRound(trackWidth), X), \
cv::min(r.boundingRect().y + r.boundingRect().height + cvRound(trackWidth), Y)));
cv::Mat yyu = getTplMat(tplMat, t);
//判断待匹配图像是否小于模板图像
if (rr.width < yyu.cols || rr.height < yyu.rows)
return;
//计算中心位置
cv::Mat tplResult0;
cv::matchTemplate(image(rr&cv::Rect(0, 0, X, Y)), yyu, tplResult0, cv::TM_CCOEFF_NORMED);
//计算极值
cv::Point maxyyuloc;
cv::minMaxLoc(tplResult0, NULL, &maxVal, NULL, &maxyyuloc);
//计算匹配坐标
maxLoc = cv::Point2f(float(maxyyuloc.x + rr.x + cvRound((float)yyu.cols / 2.0f)), \
float(maxyyuloc.y + rr.y + cvRound((float)yyu.rows / 2.0f)));
}
static int Otsu(int hist[]) static int Otsu(int hist[])
{ {
// Otsu's threshold algorithm // Otsu's threshold algorithm
...@@ -3803,7 +3836,7 @@ int eyemCountObjectE(EyemImage tpImage, const char *fileName, LPSTR *lpszNumObj, ...@@ -3803,7 +3836,7 @@ int eyemCountObjectE(EyemImage tpImage, const char *fileName, LPSTR *lpszNumObj,
return FUNC_OK; return FUNC_OK;
} }
int eyemCountObjectIrregularPartsE(EyemImage tpImage, const char *fileName, const char * ccSubType, const char *tplName, double dMinScore, LPSTR *lpszNumObj, EyemImage *tpDstImg) int eyemCountObjectIrregularPartsE(EyemImage tpImage, const char *fileName, const char *tplName, double dMinScore, LPSTR *lpszNumObj, EyemImage *tpDstImg)
{ {
cv::Mat src = cv::Mat(tpImage.iHeight, tpImage.iWidth, MAKETYPE(tpImage.iDepth, tpImage.iChannels), tpImage.vpImage); cv::Mat src = cv::Mat(tpImage.iHeight, tpImage.iWidth, MAKETYPE(tpImage.iDepth, tpImage.iChannels), tpImage.vpImage);
if (src.empty()) { if (src.empty()) {
...@@ -3825,6 +3858,10 @@ int eyemCountObjectIrregularPartsE(EyemImage tpImage, const char *fileName, cons ...@@ -3825,6 +3858,10 @@ int eyemCountObjectIrregularPartsE(EyemImage tpImage, const char *fileName, cons
//图像尺寸 //图像尺寸
int X = src.cols, Y = src.rows; int X = src.cols, Y = src.rows;
//模板尺寸
int tplWidth, tplHeight;
tplWidth = tplMat.cols, tplHeight = tplMat.rows;
//去除局部量斑影响(默认亮斑尺寸不会大于15个像素) //去除局部量斑影响(默认亮斑尺寸不会大于15个像素)
cv::Mat srcTmp; cv::Mat srcTmp;
cv::morphologyEx(src, srcTmp, cv::MORPH_ERODE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(15, 15))); cv::morphologyEx(src, srcTmp, cv::MORPH_ERODE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(15, 15)));
...@@ -3881,1392 +3918,503 @@ int eyemCountObjectIrregularPartsE(EyemImage tpImage, const char *fileName, cons ...@@ -3881,1392 +3918,503 @@ int eyemCountObjectIrregularPartsE(EyemImage tpImage, const char *fileName, cons
cv::Mat binary, srcPrev; cv::Mat binary, srcPrev;
cv::bitwise_not(src8U, srcPrev); cv::bitwise_not(src8U, srcPrev);
//计数图像
cv::Mat lbMat(Y, X, CV_8UC1, cv::Scalar(0)); cv::Mat lbMat(Y, X, CV_8UC1, cv::Scalar(0));
//使用小料算法
if (strcmp(ccSubType, "IP_SMALL_PARTS") == 0)
{
//二值化
cv::threshold(srcPrev, binary, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
cv::morphologyEx(binary, binary, cv::MORPH_DILATE, cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5, 5)));
double backPix = cv::mean(srcPrev, binary)[0];//修改背景掩膜
//去掉背景
cv::parallel_for_(cv::Range(0, Y), [&](const cv::Range range)->void {
for (int y = range.start; y < range.end; y++)
{
for (int x = 0; x < X; x++)
{
if ((srcPrev.data)[(x)+(y)*X] <= cvRound(backPix))
{
(srcPrev.data)[(x)+(y)*X] = cvRound(backPix);
}
}
}
});
cv::threshold(srcPrev, binary, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
//待处理区域
uchar *upMask = binary.data;
//标签图像
unsigned char *pLabelImg = (unsigned char *)malloc(Y*X * sizeof(unsigned char));
memset(pLabelImg, 0, X*Y * sizeof(unsigned char));
cv::Mat lbImage(Y, X, CV_8UC1, pLabelImg);
//区分不同大小器件用不同的图处理
const char icvCodeDeltas[3][3][2] = { { { 0, -1 },{ 1, -1 },{ 1, 0 } },{ { 1, 1 },{ 0, 1 },{ -1, 1 } },{ { -1, 0 },{ -1, -1 },{ 0, -1 } } };
#define upSrc(x, y) (srcPrev.data)[(x) + (y)*X]
//连通域非极大值处理
for (int y = 1; y < Y - 1; y++)
{
for (int x = 1; x < X - 1; x++)
{
//属于连通域内,并且尚未被标记
if (upMask[(x)+(y)*X] != 0 && pLabelImg[(x)+(y)*X] != 255)
{
//生长种子点
auto pixval = upSrc(x, y);
if (pixval >= upSrc((x - 1), (y - 1)) && pixval >= upSrc((x), (y - 1)) && pixval >= upSrc((x + 1), (y - 1))\
&& pixval >= upSrc((x + 1), (y)) && pixval >= upSrc((x + 1), (y + 1)) && pixval >= upSrc((x), (y + 1))\
&& pixval >= upSrc((x - 1), (y + 1)) && pixval >= upSrc((x - 1), (y)))
{
//标记已处理
pLabelImg[(x)+(y)*X] = 255;
unsigned char direction = 0;
unsigned int xx = x;
unsigned int yy = y;
bool growEnd = false;
do
{
for (unsigned int n = 0; n < 3; n++)
{
bool found = false;
for (unsigned char i = 0; i < 3; i++)
{
int nx = xx + icvCodeDeltas[direction][i][0];
int ny = yy + icvCodeDeltas[direction][i][1];
//越界处理
if (nx < 2 || ny < 2 || nx>srcPrev.cols - 2 || ny>srcPrev.rows - 2)
continue;
//考虑多加个条件限制峰值
auto val = upSrc((nx), (ny));
if (val >= pixval&&pLabelImg[(nx)+(ny)*X] != 255)
{
found = true;
xx = nx;
yy = ny;
//next
direction = icvCodeDeltas[direction][i][2];
//标记已处理
pLabelImg[(xx)+(yy)*X] = 255;
break;
}
}
if (!found)
{
direction = (direction + 1) % 4;
}
if (growEnd = (direction == 3))
break;
}
} while (!growEnd);
}
}
}
}
//粗略计数
cv::Mat labels, stats, centroids;
int numObj = cv::connectedComponentsWithStats(lbImage, labels, stats, centroids);
//坐标图
binary = cv::Scalar(0);
//画图
double *dpCent = (double *)centroids.data;
for (int j = 1; j < numObj; j++)
{
//cv::Point2f ms((float)dpCent[(0) + (j) * 2], (float)dpCent[(1) + (j) * 2]);
binary.at<uchar>(cv::Point(cvRound((float)dpCent[(0) + (j) * 2]), cvRound((float)dpCent[(1) + (j) * 2]))) = 255;
}
//释放资源
free((void *)pLabelImg);
}
//使用大料算法(模板匹配方式) //使用大料算法(模板匹配方式)
else if (strcmp(ccSubType, "IP_LARGE_PARTS") == 0) cv::threshold(srcPrev, binary, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
{
double begin0 = (double)cv::getTickCount();
//二值化
cv::threshold(srcPrev, binary, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
cv::Mat srcPrevEx0; cv::Mat srcPrevEx0;
cv::morphologyEx(binary, srcPrevEx0, cv::MORPH_CLOSE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(95, 95))); cv::morphologyEx(binary, srcPrevEx0, cv::MORPH_CLOSE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(95, 95)));
//定位料盘中心 //定位料盘中心
std::vector<std::vector<cv::Point>> contours; std::vector<std::vector<cv::Point>> contours;
cv::findContours(srcPrevEx0, contours, cv::RETR_TREE, cv::CHAIN_APPROX_NONE); cv::findContours(srcPrevEx0, contours, cv::RETR_TREE, cv::CHAIN_APPROX_NONE);
cv::Mat srcPrevEx1 = cv::Mat::zeros(src8U.size(), CV_8UC1); cv::Mat srcPrevEx1 = cv::Mat::zeros(src8U.size(), CV_8UC1);
//填充料盘 //填充料盘
for (int i = 0; i < contours.size(); i++) for (int i = 0; i < contours.size(); i++)
{ {
cv::drawContours(srcPrevEx1, contours, i, cv::Scalar(255), -1); cv::drawContours(srcPrevEx1, contours, i, cv::Scalar(255), -1);
} }
cv::Mat image = srcPrevEx1.clone(); cv::Mat image = srcPrevEx1.clone();
//取中间部分,避免因料盘散开而导致中心定位错误(如果没有就选取外面) //取中间部分,避免因料盘散开而导致中心定位错误(如果没有就选取外面)
srcPrevEx1 -= srcPrevEx0; srcPrevEx1 -= srcPrevEx0;
// //
cv::morphologyEx(srcPrevEx1, srcPrevEx1, cv::MORPH_CLOSE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(95, 95))); cv::morphologyEx(srcPrevEx1, srcPrevEx1, cv::MORPH_CLOSE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(95, 95)));
//获取最大轮廓 //获取最大轮廓
cv::findContours(srcPrevEx1, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); cv::findContours(srcPrevEx1, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
if (contours.size() <= 0) if (contours.size() <= 0)
return FUNC_CANNOT_CALC; {
cv::findContours(image, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
}
std::vector<cv::Point> contourMax = contours[0]; double contourMaxArea = cv::contourArea(contourMax); std::vector<cv::Point> contourMax = contours[0]; double contourMaxArea = cv::contourArea(contourMax);
for (int i = 1; i < contours.size(); i++) for (int i = 1; i < contours.size(); i++)
{
double contourArea = cv::contourArea(contours[i]);
if (contourArea > contourMaxArea)
{ {
double contourArea = cv::contourArea(contours[i]); contourMax = contours[i];
if (contourArea > contourMaxArea) contourMaxArea = contourArea;
{
contourMax = contours[i];
contourMaxArea = contourArea;
}
} }
}
//质心 //质心
cv::Moments mu = cv::moments(contourMax); cv::Moments mu = cv::moments(contourMax);
cv::Point2f reelCenter(float(mu.m10 / mu.m00), float(mu.m01 / mu.m00)); cv::Point2f reelCenter(float(mu.m10 / mu.m00), float(mu.m01 / mu.m00));
//画中心 //画中心
reelCenter.x = reelCenter.x > 0 && reelCenter.x < X ? reelCenter.x : 0; reelCenter.x = reelCenter.x > 0 && reelCenter.x < X ? reelCenter.x : 0;
reelCenter.y = reelCenter.y > 0 && reelCenter.y < Y ? reelCenter.y : 0; reelCenter.y = reelCenter.y > 0 && reelCenter.y < Y ? reelCenter.y : 0;
cv::drawMarker(cc, reelCenter, cv::Scalar(0, 0, 238, 255), 1, 35, 2); cv::drawMarker(cc, reelCenter, cv::Scalar(0, 0, 238, 255), 1, 35, 2);
//计算料盘范围 //计算料盘范围
cv::findContours(image, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); cv::findContours(image, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
contourMax = contours[0]; contourMaxArea = cv::contourArea(contourMax); contourMax = contours[0]; contourMaxArea = cv::contourArea(contourMax);
for (int i = 1; i < contours.size(); i++) for (int i = 1; i < contours.size(); i++)
{
double contourArea = cv::contourArea(contours[i]);
if (contourArea > contourMaxArea)
{ {
double contourArea = cv::contourArea(contours[i]); contourMax = contours[i];
if (contourArea > contourMaxArea) contourMaxArea = contourArea;
{
contourMax = contours[i];
contourMaxArea = contourArea;
}
} }
}
cv::Rect rec = cv::boundingRect(contourMax); cv::Rect rec = cv::boundingRect(contourMax);
cv::Point tl, br, tr, bl;
tl = cv::Point(cv::max(rec.tl().x - 35, 0), cv::max(rec.tl().y - 35, 0)); br = cv::Point(cv::min(rec.br().x + 35, Y), cv::min(rec.br().y + 35, Y));
tr = cv::Point(br.x, tl.y); bl = cv::Point(tl.x, br.y);
//模板信息 //顶点坐标
int tplWidth, tplHeight; cv::Point tl, br, tr, bl;
tplWidth = tplMat.cols, tplHeight = tplMat.rows; tl = cv::Point(cv::max(rec.tl().x - 35, 0), cv::max(rec.tl().y - 35, 0)); br = cv::Point(cv::min(rec.br().x + 35, Y), cv::min(rec.br().y + 35, Y));
tr = cv::Point(br.x, tl.y); bl = cv::Point(tl.x, br.y);
//用八个方向的模板进行模板匹配(模板匹配的方式对于发生形变的器件效果不好) //用八个方向的模板进行模板匹配(模板匹配的方式对于发生形变的器件效果不好)
const float icvDirections[4] = { 0 , 90 , 180 , -90 }; const float icvDirections[4] = { 0 , 90 , 180 , -90 };
//用以计算元件中心 //用以计算元件中心
const int icvDirectionDeltas[4][2] = { { tplWidth / 2,tplHeight / 2 },{ tplHeight / 2,tplWidth / 2 },{ tplWidth / 2,tplHeight / 2 },\ const int icvDirectionDeltas[4][2] = { { tplWidth / 2,tplHeight / 2 },{ tplHeight / 2,tplWidth / 2 },{ tplWidth / 2,tplHeight / 2 },\
{ tplHeight / 2,tplWidth / 2 } }; { tplHeight / 2,tplWidth / 2 } };
//用于计数 //用于计数
cv::Mat matchParts(Y, X, CV_8UC1, cv::Scalar(0)); cv::Mat matchParts(Y, X, CV_8UC1, cv::Scalar(0));
cv::parallel_for_(cv::Range(0, 4), [&](const cv::Range& range)->void { cv::parallel_for_(cv::Range(0, 4), [&](const cv::Range& range)->void {
for (int tpl = range.start; tpl < range.end; tpl++) for (int tpl = range.start; tpl < range.end; tpl++)
{ {
float t = icvDirections[tpl]; float t = icvDirections[tpl];
//模板匹配(仅处理料盘区域以提高速度) //模板匹配(仅处理料盘区域以提高速度)
cv::Mat tplResult0; cv::Mat tplResult0;
cv::matchTemplate(srcPrev(cv::Rect(tl, br)), getTplMat(tplMat, t), tplResult0, cv::TM_CCOEFF_NORMED); cv::matchTemplate(srcPrev(cv::Rect(tl, br)), getTplMat(tplMat, t), tplResult0, cv::TM_CCOEFF_NORMED);
//分数大于0.7才当作元件处理 //分数大于0.7才当作元件处理
tplResult0 = cv::Mat(tplResult0 > dMinScore); tplResult0 = cv::Mat(tplResult0 > dMinScore);
cv::Point quard[4]{ cv::Point(tplResult0.cols,0),cv::Point(0,0),cv::Point(0,tplResult0.rows),cv::Point(tplResult0.cols,tplResult0.rows) }; cv::Point quard[4]{ cv::Point(tplResult0.cols,0),cv::Point(0,0),cv::Point(0,tplResult0.rows),cv::Point(tplResult0.cols,tplResult0.rows) };
std::vector<cv::Point> pt = { cv::Point(tplResult0.cols / 2,tplResult0.rows / 2),quard[tpl], quard[(tpl + 1) % 4] }; std::vector<cv::Point> pt = { cv::Point(tplResult0.cols / 2,tplResult0.rows / 2),quard[tpl], quard[(tpl + 1) % 4] };
std::vector<std::vector<cv::Point>> pts; std::vector<std::vector<cv::Point>> pts;
pts.push_back(pt); pts.push_back(pt);
cv::Mat dirMask(tplResult0.size(), CV_8UC1, cv::Scalar(0)); cv::Mat dirMask(tplResult0.size(), CV_8UC1, cv::Scalar(0));
cv::drawContours(dirMask, pts, 0, cv::Scalar(255), -1); cv::drawContours(dirMask, pts, 0, cv::Scalar(255), -1);
//除去非必要部分 //除去非必要部分
cv::bitwise_and(tplResult0, dirMask, tplResult0); cv::bitwise_and(tplResult0, dirMask, tplResult0);
//连通域分析 //连通域分析
cv::Mat labels, stats, centroids; cv::Mat labels, stats, centroids;
int nccomps = cv::connectedComponentsWithStats(tplResult0, labels, stats, centroids); int nccomps = cv::connectedComponentsWithStats(tplResult0, labels, stats, centroids);
for (int i = 1; i < nccomps; i++) { for (int i = 1; i < nccomps; i++) {
cv::Point matchPt(cvRound(centroids.ptr<double>(i)[0]) + icvDirectionDeltas[tpl][0] + tl.x, \ cv::Point matchPt(cvRound(centroids.ptr<double>(i)[0]) + icvDirectionDeltas[tpl][0] + tl.x, \
cvRound(centroids.ptr<double>(i)[1]) + icvDirectionDeltas[tpl][1] + tl.y); cvRound(centroids.ptr<double>(i)[1]) + icvDirectionDeltas[tpl][1] + tl.y);
matchParts.ptr<uint8_t>(matchPt.y)[matchPt.x] = 255; matchParts.ptr<uint8_t>(matchPt.y)[matchPt.x] = 255;
}
} }
}); }
});
///<追踪元件算法 ///<追踪元件算法
//标记初始区域 //标记初始区域
std::vector<cv::Point> matchPts; std::vector<cv::Point> matchPts;
cv::findNonZero(matchParts, matchPts); cv::findNonZero(matchParts, matchPts);
#ifdef _DEBUG #ifdef _DEBUG
//计算元件间距(弦长) //计算元件间距(弦长)
/* /*
已知弦长L和半径R求角度2A: 已知弦长L和半径R求角度2A:
sinA=(1/2)*L/R=L/(2R),2A=2asin(L/(2R)). sinA=(1/2)*L/R=L/(2R),2A=2asin(L/(2R)).
*/ */
for (std::vector<cv::Point>::iterator itv = matchPts.begin(); itv != matchPts.end(); ++itv) for (std::vector<cv::Point>::iterator itv = matchPts.begin(); itv != matchPts.end(); ++itv)
{ {
//cv::drawMarker(cc, cv::Point((*itv).x, (*itv).y), cv::Scalar(0, 255, 0, 255)); cv::drawMarker(cc, cv::Point((*itv).x, (*itv).y), cv::Scalar(0, 255, 0, 255));
} }
#endif #endif
std::cout << "追踪前计算耗时:" << 1000 * (static_cast<double>(cv::getTickCount()) - begin0) / cv::getTickFrequency() << std::endl; //标签图
//标签图 cv::Mat trackMat(Y, X, CV_8UC1, cv::Scalar(0));
cv::Mat trackMat(Y, X, CV_8UC1, cv::Scalar(0));
//追踪信息
struct Track {
int iLimit, iPartSize;
double dMatchDeg;
cv::Point Pos;
std::vector<cv::Point2f> Rect;
Track() {};
Track(int iLimit, int iPartSize, double dMatchDeg, cv::Point Pos, std::vector<cv::Point2f> Rect) :iLimit(iLimit), \
iPartSize(iPartSize), dMatchDeg(dMatchDeg), Pos(Pos), Rect(Rect) {};
//追踪信息 bool operator >(const Track &te)const
struct Track { {
int iLimit, iPartSize; return dMatchDeg > te.dMatchDeg;
double dMatchDeg; }
cv::Point Pos; };
std::vector<cv::Point2f> Rect;
//计算元件间距
double dChordL = .0;
for (std::vector<cv::Point>::iterator itv = matchPts.begin(); itv != matchPts.end(); ++itv)
{
///<初始化追踪参数
//追踪起点
cv::Point2f startCenter((float)(*itv).x, (float)(*itv).y);
//扫描步长
const double dMinorStep = 0.1;
//追踪长宽
const double trackLength = (double)tplWidth / 2., trackWidth = (double)tplHeight / 4.;
//起始扫描角度
const double startAngle = atan2((double)startCenter.y - (double)reelCenter.y, (double)startCenter.x - (double)reelCenter.x) * 180 / PI;
//起始扫描半径
const double startRadius = cv::norm(startCenter - reelCenter);
//偏移角度(元件尺寸)
const double dOffset = (2 * asin(2 * trackLength / (2 * startRadius))) * 180. / PI;
//初始搜索角度,用以确定元件间距(默认15度范围内存在元件)
const double dScanRange = 15;
//追踪元件间距(弦长,可以尽量避免因个别器件偏离导致的追踪中断)
std::vector<Track> track4ChordL; bool expect = false;
for (double t = startAngle + dOffset; t < startAngle + dOffset + dScanRange; t += dMinorStep)
{
float x = float(reelCenter.x + startRadius*cos(t*c));
float y = float(reelCenter.y + startRadius*sin(t*c));
Track() {}; cv::Point2f pts[4];
calcRotateRect(cv::Point2f(x, y), (float)t, (float)trackLength, (float)trackWidth, pts);
Track(int iLimit, int iPartSize, double dMatchDeg, cv::Point Pos, std::vector<cv::Point2f> Rect) :iLimit(iLimit), \ //计算极值
iPartSize(iPartSize), dMatchDeg(dMatchDeg), Pos(Pos), Rect(Rect) {}; double maxyyu; cv::Point2f trackCentert;
findTrackModel(srcPrev, tplMat, 90 - (t + 180), trackWidth, pts, maxyyu, trackCentert);
bool operator >(const Track &te)const //匹配阈值
if (maxyyu > dMinScore)
{ {
return dMatchDeg > te.dMatchDeg; track4ChordL.push_back(Track(0, 0, maxyyu, (cv::Point)trackCentert, std::vector<cv::Point2f>()));
if (!expect)
expect = true;
} }
}; if ((maxyyu < dMinScore) && expect)
break;
}
//计算元件间距
if (track4ChordL.size() != 0) {
std::sort(track4ChordL.begin(), track4ChordL.end(), std::greater<Track>());
cv::Point ecpectPos = track4ChordL[0].Pos;
dChordL = 2.0 * startRadius*sin(((2.0 * asin((cv::norm(startCenter - cv::Point2f((float)ecpectPos.x, \
(float)ecpectPos.y))) / (2.0 * startRadius))) * 180.0 / PI - dOffset)*PI / 180.0 / 2.0);
cv::drawMarker(cc, cv::Point(ecpectPos.x, ecpectPos.y), cv::Scalar(0, 0, 255, 255), 0, 10);
}
else {
//用先前的方式重新计算间距,因为耗时操作不会在这里,加上可以减少耗时但可能不一定准
}
//计算元件间距 //计算元件间距
double dChordL = .0; if (dChordL > 0) {
for (std::vector<cv::Point>::iterator itv = matchPts.begin(); itv != matchPts.end(); ++itv) break;
}
}
///<开始两个方向追踪流程
for (std::vector<cv::Point>::iterator itv = matchPts.begin(); itv != matchPts.end(); ++itv)
{
//追踪起点
cv::Point2f startCenter((float)(*itv).x, (float)(*itv).y);
//已作标记
if (trackMat.ptr<uint8_t>((int)startCenter.y)[(int)startCenter.x] == 255)
continue;
//计算元件区域
cv::Point2f points[4];
{ {
///<初始化追踪参数 double t = atan2((double)(*itv).y - reelCenter.y, (double)(*itv).x - reelCenter.x) * 180 / PI;
//追踪起点 const float trackLength = (float)tplWidth / 2.0f, trackWidth = (float)tplHeight / 4.0f;
cv::Point2f startCenter((float)(*itv).x, (float)(*itv).y);
//扫描步长 calcRotateRect(startCenter, (float)t, trackLength, trackWidth, points);
const double dMinorStep = 0.1; }
//追踪长宽
const double trackLength = (double)tplWidth / 2., trackWidth = (double)tplHeight / 4.;
//起始扫描角度
const double startAngle = atan2((double)startCenter.y - (double)reelCenter.y, (double)startCenter.x - (double)reelCenter.x) * 180 / PI;
//起始扫描半径
const double startRadius = cv::norm(startCenter - reelCenter);
//偏移角度(元件尺寸)
const double dOffset = (2 * asin(2 * trackLength / (2 * startRadius))) * 180. / PI;
//初始搜索角度,用以确定元件间距(默认15度范围内存在元件) //增加判断,在模板匹配失效时候判断,可能匹配的不是元件
const double dScanRange = 15; cv::RotatedRect rt(points[0], points[1], points[2]);
//追踪元件间距(弦长,可以尽量避免因个别器件偏离导致的追踪中断) cv::Rect brt = rt.boundingRect()&cv::Rect(0, 0, X, Y);
std::vector<Track> trackScore; bool expect = false;
for (double t = startAngle + dOffset; t < startAngle + dOffset + dScanRange; t += dMinorStep) //判断为背景
{ if (((float)cv::countNonZero(binary(brt)) / rt.size.area()) < 0.25) {
float x = float(reelCenter.x + startRadius*cos(t*c)); continue;
float y = float(reelCenter.y + startRadius*sin(t*c)); }
//标记为已追踪过
std::vector<cv::Point> vT = { cv::Point(points[0]),cv::Point(points[1]) ,cv::Point(points[2]) ,cv::Point(points[3]) };
cv::fillConvexPoly(trackMat, vT, cv::Scalar(255));
//标记计数
lbMat.ptr<uint8_t>(cvRound(startCenter.y))[cvRound(startCenter.x)] = 255;
//标记当前位置
cv::drawMarker(cc, cv::Point((*itv).x, (*itv).y), cv::Scalar(0, 165, 255, 255), cv::MARKER_DIAMOND, 5);
///<初始化追踪参数
//扫描步长
const double dMinorStep = 0.1;
//追踪长宽
const double trackLength = (double)tplWidth / 2., trackWidth = (double)tplHeight / 4.;
//起始扫描角度
const double startAngle = atan2((double)startCenter.y - (double)reelCenter.y, (double)startCenter.x - (double)reelCenter.x) * 180 / PI;
//起始扫描半径
const double startRadius = cv::norm(startCenter - reelCenter);
//偏移角度(元件尺寸)
const double dOffset = (2 * asin(2 * trackLength / (2 * startRadius))) * 180. / PI;
//并行处理
//#pragma omp parallel sections
{
//(顺时针)
//#pragma omp section
{
//追踪中心
cv::Point2f trackCenter = cv::Point2f(startCenter.x, startCenter.y);
//追踪角度、半径
double trackAngle = startAngle, trackRadius = startRadius;
//元件本身角度
double trackOffset = dOffset;
//元件间间距
double partDist = (2 * asin(dChordL / (2 * trackRadius))) * 180 / PI;
//外接矩形顶点
cv::Point2f pts[4]; cv::Point2f pts[4];
calcRotateRect(cv::Point2f(x, y), (float)t, (float)trackLength, (float)trackWidth, pts); //开始追踪
bool trackEnd = true;
do
{
double begin0 = (double)cv::getTickCount();
//旋转矩形 bool found = true;
cv::RotatedRect r(pts[0], pts[1], pts[2]); std::vector<Track> vParts;
for (double t = trackAngle + (trackOffset / 2.0 + partDist + trackOffset / 3.0); t < trackAngle + \
(trackOffset / 2.0 + partDist + trackOffset / 3.0) + trackOffset / 3.0; t += dMinorStep)
{
trackCenter.x = reelCenter.x + (float)trackRadius*(float)cos(t*c);
trackCenter.y = reelCenter.y + (float)trackRadius*(float)sin(t*c);
//待匹配图像 //计算旋转矩形
cv::Rect rr(cv::Point2i(cv::max(r.boundingRect().x - cvRound(trackWidth), 0), \ calcRotateRect(trackCenter, (float)t, (float)trackLength, (float)trackWidth, pts);
cv::max(r.boundingRect().y - cvRound(trackWidth), 0)), \
cv::Point2i(cv::min(r.boundingRect().x + r.boundingRect().width + cvRound(trackWidth), X), \
cv::min(r.boundingRect().y + r.boundingRect().height + cvRound(trackWidth), Y)));
//不同方向模板图像(考虑实时更新模板图像或多角度模板,这样可能速度会下降) //模板匹配
cv::Mat yyu = getTplMat(tplMat, 90 - (t + 180)); double maxyyu; cv::Point2f maxyyuloc;
findTrackModel(srcPrev, tplMat, 90 - (t + 180), trackWidth, pts, maxyyu, maxyyuloc);
//待匹配图像小于模板图像的话不进行匹配 //存放结果
if (rr.width < yyu.cols || rr.height < yyu.rows) vParts.push_back(Track(0, 0, maxyyu, cv::Point(cvRound(trackCenter.x), cvRound(trackCenter.y)), std::vector<cv::Point2f>()));
continue;
//计算中心位置 //测试标记
cv::Mat tplResult0; //cv::drawMarker(cc, cv::Point(cvRound(trackCenter.x), cvRound(trackCenter.y)), cv::Scalar(0, 0, 255, 255), cv::MARKER_CROSS, 5);
cv::matchTemplate(srcPrev(rr), yyu, tplResult0, cv::TM_CCOEFF_NORMED); }
// if (vParts.size() <= 0)
double maxyyu; cv::Point maxyyuloc; break;
cv::minMaxLoc(tplResult0, NULL, &maxyyu, NULL, &maxyyuloc);
//匹配阈值 //极值处认为是元件
if (maxyyu > dMinScore) std::sort(vParts.begin(), vParts.end(), std::greater<Track>());
{
//初次满足分数阈值
cv::Point2f trackCentert = cv::Point2f(float(maxyyuloc.x + rr.x + cvRound((float)yyu.cols / 2.0f)), \
float(maxyyuloc.y + rr.y + cvRound((float)yyu.rows / 2.0f)));
trackScore.push_back(Track(0, 0, maxyyu, (cv::Point)trackCentert, std::vector<cv::Point2f>())); //更新切线方向位置
trackCenter = cv::Point2f((float)vParts[0].Pos.x, (float)vParts[0].Pos.y);
if (!expect) //更新扫描角度
expect = true; trackAngle = atan2((double)trackCenter.y - reelCenter.y, (double)trackCenter.x - reelCenter.x) * 180 / PI;
}
if ((maxyyu < dMinScore) && expect)
break;
}
//计算元件间距 ///<开始离心/向心扫描(横向由于存在间隔所以一般不会出现偏离的情况,除非料盘本身严重变形或者中心定位出问题)
if (trackScore.size() != 0) {
cv::Point ecpectPos = trackScore[0].Pos; calcRotateRect(trackCenter, (float)trackAngle, (float)trackLength, (float)trackWidth, pts);
std::sort(trackScore.begin(), trackScore.end(), std::greater<Track>()); //模板匹配/更新元件精确位置
double maxyyu;
findTrackModel(srcPrev, tplMat, 90 - (trackAngle + 180), trackWidth, pts, maxyyu, trackCenter);
dChordL = 2.0 * startRadius*sin(((2.0 * asin((cv::norm(startCenter - cv::Point2f((float)ecpectPos.x, \ //更新扫描半径
(float)ecpectPos.y))) / (2.0 * startRadius))) * 180.0 / PI - dOffset)*PI / 180.0 / 2.0); trackRadius = cv::norm(trackCenter - reelCenter);
//更新扫描角度
trackAngle = atan2((double)trackCenter.y - reelCenter.y, (double)trackCenter.x - reelCenter.x) * 180 / PI;
//更新偏移量(元件大小)
trackOffset = (2 * asin(2 * trackLength / (2 * trackRadius))) * 180 / PI;
//更新元件间角度
partDist = (2 * asin(dChordL / (2 * trackRadius))) * 180 / PI;
cv::drawMarker(cc, cv::Point(ecpectPos.x, ecpectPos.y), cv::Scalar(0, 0, 255, 255), 0, 10); //更新元件区域
} calcRotateRect(trackCenter, (float)trackAngle, (float)trackLength, (float)trackWidth, pts);
else {
//用先前的方式重新计算间距,因为耗时操作不会在这里,加上可以减少耗时但可能不一定准
}
//计算元件间距 //需要进行越界处理
if (dChordL > 0) { cv::RotatedRect rtt(pts[0], pts[1], pts[2]);
break;
}
}
//开始两个方向追踪 //判断是否结束
for (std::vector<cv::Point>::iterator itv = matchPts.begin(); itv != matchPts.end(); ++itv) if (((trackMat.ptr<uint8_t>(cvRound(trackCenter.y))[cvRound(trackCenter.x)] == 255)) || \
{ ((float)cv::countNonZero(binary((rtt.boundingRect()&cv::Rect(0, 0, X, Y)))) / rtt.size.area()) < 0.25)
//追踪起点 {
cv::Point2f startCenter((float)(*itv).x, (float)(*itv).y); found = false;
//cv::drawMarker(cc, cv::Point(cvRound(trackCenter.x), cvRound(trackCenter.y)), cv::Scalar(0, 0, 255, 255), cv::MARKER_DIAMOND, 5);
}
else
{
//标记为已追踪过
std::vector<cv::Point> ptPoly = { cv::Point(pts[0]),cv::Point(pts[1]) ,cv::Point(pts[2]) ,cv::Point(pts[3]) };
cv::fillConvexPoly(trackMat, ptPoly, cv::Scalar(255));
//已作标记 //标记计数
if (trackMat.ptr<uint8_t>((int)startCenter.y)[(int)startCenter.x] == 255) lbMat.ptr<uint8_t>(cvRound(trackCenter.y))[cvRound(trackCenter.x)] = 255;
continue;
//计算元件区域 //标记当前位置
cv::Point2f points[4]; cv::drawMarker(cc, trackCenter, cv::Scalar(0, 255, 0, 255), cv::MARKER_DIAMOND, 5);
{ }
double t = atan2((double)(*itv).y - reelCenter.y, (double)(*itv).x - reelCenter.x) * 180 / PI;
const float trackLength = (float)tplWidth / 2.0f, trackWidth = (float)tplHeight / 4.0f; //std::cout << "总体耗时:" << 1000 * (static_cast<double>(cv::getTickCount()) - begin0) / cv::getTickFrequency() << std::endl;
calcRotateRect(startCenter, (float)t, trackLength, trackWidth, points); trackEnd = (!found);
} while (!trackEnd);
} }
//增加判断,在模板匹配失效时候判断,可能匹配的不是元件 //#pragma omp section
cv::RotatedRect rt(points[0], points[1], points[2]); //逆时针追踪
{
//追踪起点
cv::Point2f trackCenter(startCenter.x, startCenter.y);
//起始扫描角度、半径
double trackAngle = startAngle, trackRadius = startRadius;
//元件本身角度
double trackOffset = dOffset;
//元件间间距
double partDist = (2 * asin(dChordL / (2 * trackRadius))) * 180 / PI;
//外接矩形
cv::Point2f pts[4];
//开始追踪
bool trackEnd = true;
//
do
{
bool found = true;
std::vector<Track> vParts;
for (double t = trackAngle - (partDist + trackOffset / 2.0); t > trackAngle - (partDist + trackOffset / 2.0) - trackOffset; \
t -= dMinorStep)
{
trackCenter.x = float(reelCenter.x + trackRadius*cos(t*c));
trackCenter.y = float(reelCenter.y + trackRadius*sin(t*c));
cv::Rect brt = rt.boundingRect()&cv::Rect(0, 0, X, Y); //计算旋转矩形
calcRotateRect(trackCenter, (float)t, (float)trackLength, (float)trackWidth, pts);
if (((float)cv::countNonZero(binary(brt)) / rt.size.area()) < 0.25) { //模板匹配
continue; double maxyyu; cv::Point2f maxyyuloc;
} findTrackModel(srcPrev, tplMat, 90 - (t + 180), trackWidth, pts, maxyyu, maxyyuloc);
//标记为已追踪过 //存放结果
std::vector<cv::Point> vT = { cv::Point(points[0]),cv::Point(points[1]) ,cv::Point(points[2]) ,cv::Point(points[3]) }; vParts.push_back(Track(0, 0, maxyyu, cv::Point(cvRound(trackCenter.x), cvRound(trackCenter.y)), std::vector<cv::Point2f>()));
cv::fillConvexPoly(trackMat, vT, cv::Scalar(255)); }
if (vParts.size() == 0) break;
//标记计数 //极值认为是元件
lbMat.ptr<uint8_t>(cvRound(startCenter.y))[cvRound(startCenter.x)] = 255; std::sort(vParts.begin(), vParts.end(), std::greater<Track>());
//标记当前位置 //更新位置
cv::drawMarker(cc, cv::Point((*itv).x, (*itv).y), cv::Scalar(0, 165, 255, 255), cv::MARKER_DIAMOND, 5); trackCenter = cv::Point2f((float)vParts[0].Pos.x, (float)vParts[0].Pos.y);
///<初始化追踪参数 //更新扫描角度
trackAngle = atan2((double)trackCenter.y - reelCenter.y, (double)trackCenter.x - reelCenter.x) * 180 / PI;
//扫描步长 ///<开始纵向扫描(横向由于存在间隔所以一般不会出现偏离的情况,除非料盘本身严重变形或者中心定位出问题)
const double dMinorStep = 0.1;
//追踪长宽
const double trackLength = (double)tplWidth / 2., trackWidth = (double)tplHeight / 4.;
//起始扫描角度
const double startAngle = atan2((double)startCenter.y - (double)reelCenter.y, (double)startCenter.x - (double)reelCenter.x) * 180 / PI;
//起始扫描半径
const double startRadius = cv::norm(startCenter - reelCenter);
//偏移角度(元件尺寸)
const double dOffset = (2 * asin(2 * trackLength / (2 * startRadius))) * 180. / PI;
//并行处理 calcRotateRect(trackCenter, (float)trackAngle, (float)trackLength, (float)trackWidth, pts);
//#pragma omp parallel sections
{
//(顺时针)
//#pragma omp section
{
//追踪中心
cv::Point2f trackCenter = cv::Point2f(startCenter.x, startCenter.y);
//追踪角度、半径
double trackAngle = startAngle, trackRadius = startRadius;
//元件本身角度
double trackOffset = dOffset;
//元件间间距
double partDist = (2 * asin(dChordL / (2 * trackRadius))) * 180 / PI;
//外接矩形顶点
cv::Point2f pts[4];
//开始追踪
bool trackEnd = true;
do
{
bool found = true;
std::vector<Track> vParts;
for (double t = trackAngle + (trackOffset / 2.0 + partDist); t < trackAngle + (trackOffset / 2.0 + partDist) + trackOffset; \
t += dMinorStep)
{
trackCenter.x = reelCenter.x + (float)trackRadius*(float)cos(t*c);
trackCenter.y = reelCenter.y + (float)trackRadius*(float)sin(t*c);
calcRotateRect(trackCenter, (float)t, (float)trackLength, (float)trackWidth, pts);
std::vector<cv::Point> vPoints;
std::vector<cv::Point2f> vRect(pts, pts + sizeof(pts) / sizeof(cv::Point2f));
//获取内部坐标
calcRotateRect(vRect, vPoints);
//计算灰度值
double dMatch = 0;
for (int v = 0; v < vPoints.size(); v++)
{
if (vPoints[v].x >= 0 && vPoints[v].x <= X&&vPoints[v].y >= 0 && vPoints[v].y <= Y)
{
dMatch += (srcPrev.data)[(vPoints[v].x) + (vPoints[v].y)*X];
}
}
dMatch /= (double)vPoints.size();
//仅扫描一个元件的角度
vParts.push_back(Track(0, 0, dMatch, cv::Point(cvRound(trackCenter.x), cvRound(trackCenter.y)), vRect));
}
if (vParts.size() == 0) continue;
//灰度极值认为是元件
std::sort(vParts.begin(), vParts.end(), std::greater<Track>());
//更新位置
trackCenter = cv::Point(vParts[0].Pos.x, vParts[0].Pos.y);
//更新扫描角度
trackAngle = atan2((double)trackCenter.y - reelCenter.y, (double)trackCenter.x - reelCenter.x) * 180 / PI;
///<开始纵向扫描(横向由于存在间隔所以一般不会出现偏离的情况,除非料盘本身严重变形或者中心定位出问题)
calcRotateRect(trackCenter, (float)trackAngle, (float)trackLength, (float)trackWidth, pts);
//旋转矩形
cv::RotatedRect r(pts[0], pts[1], pts[2]);
//待匹配图像
cv::Rect rr(cv::Point2i(cv::max(r.boundingRect().x - cvRound(trackWidth), 0), \
cv::max(r.boundingRect().y - cvRound(trackWidth), 0)), \
cv::Point2i(cv::min(r.boundingRect().x + r.boundingRect().width + cvRound(trackWidth), X), \
cv::min(r.boundingRect().y + r.boundingRect().height + cvRound(trackWidth), Y)));
//不同方向模板图像(考虑实时更新模板图像或多角度模板,活动模板,用定位到的区域选取当前位置的模板,这样可能速度会下降)
cv::Mat yyu = getTplMat(tplMat, 90 - (trackAngle + 180));
//判断待匹配图像是否小于模板图像
if (rr.width < yyu.cols || rr.height < yyu.rows)
continue;
//计算中心位置
cv::Mat tplResult0;
cv::matchTemplate(srcPrev(rr), yyu, tplResult0, cv::TM_CCOEFF_NORMED);
//
double maxyyu; cv::Point maxyyuloc;
cv::minMaxLoc(tplResult0, NULL, &maxyyu, NULL, &maxyyuloc);
//旧标记点
cv::Point2f trackOldCenter = cv::Point2f(trackCenter.x, trackCenter.y);
//更新元件精确位置
trackCenter = cv::Point2f(float(maxyyuloc.x + rr.x + cvRound((float)yyu.cols / 2.0f)), \
float(maxyyuloc.y + rr.y + cvRound((float)yyu.rows / 2.0f)));
//cv::drawMarker(cc, trackCenter, cv::Scalar(0, 0, 255, 255));
//更新扫描半径
trackRadius = cv::norm(trackCenter - reelCenter);
//更新扫描角度
trackAngle = atan2((double)trackCenter.y - reelCenter.y, (double)trackCenter.x - reelCenter.x) * 180 / PI;
//更新偏移量(元件大小)
trackOffset = (2 * asin(2 * trackLength / (2 * trackRadius))) * 180 / PI;
//更新元件间角度
partDist = (2 * asin(dChordL / (2 * trackRadius))) * 180 / PI;
//更新元件区域
calcRotateRect(trackCenter, (float)trackAngle, (float)trackLength, (float)trackWidth, pts);
//需要进行越界处理
cv::RotatedRect rtt(pts[0], pts[1], pts[2]);
//判断是否结束
if (((trackMat.ptr<uint8_t>(cvRound(trackCenter.y))[cvRound(trackCenter.x)] == 255)) || \
((float)cv::countNonZero(binary((rtt.boundingRect()&cv::Rect(0, 0, X, Y)))) / rtt.size.area()) < 0.25)
{
found = false;
}
else
{
//标记为已追踪过
std::vector<cv::Point> ptPoly = { cv::Point(pts[0]),cv::Point(pts[1]) ,cv::Point(pts[2]) ,cv::Point(pts[3]) };
cv::fillConvexPoly(trackMat, ptPoly, cv::Scalar(255));
//标记计数
lbMat.ptr<uint8_t>(cvRound(trackCenter.y))[cvRound(trackCenter.x)] = 255;
//画出元件区域
//for (int j = 0; j < 4; j++)
//{
// cv::line(cc, pts[j], pts[(j + 1) % 4], cv::Scalar(0, 165, 255, 255), 1);
//}
//标记当前位置
cv::drawMarker(cc, trackCenter, cv::Scalar(0, 255, 0, 255), cv::MARKER_DIAMOND, 5);
}
trackEnd = (!found);
} while (!trackEnd);
}
//#pragma omp section
//逆时针追踪
{
//追踪起点
cv::Point2f trackCenter(startCenter.x, startCenter.y);
//起始扫描角度、半径
double trackAngle = startAngle, trackRadius = startRadius;
//元件本身角度
double trackOffset = dOffset;
//元件间间距
double partDist = (2 * asin(dChordL / (2 * trackRadius))) * 180 / PI;
//外接矩形
cv::Point2f pts[4];
//开始追踪
bool trackEnd = true;
//
do
{
bool found = true;
std::vector<Track> vParts;
for (double t = trackAngle - (partDist + trackOffset / 2.0); t > trackAngle - (partDist + trackOffset / 2.0) - trackOffset; \
t -= dMinorStep)
{
trackCenter.x = float(reelCenter.x + trackRadius*cos(t*c));
trackCenter.y = float(reelCenter.y + trackRadius*sin(t*c));
calcRotateRect(trackCenter, (float)t, (float)trackLength, (float)trackWidth, pts);
std::vector<cv::Point> vPoints;
std::vector<cv::Point2f> vRect(pts, pts + sizeof(pts) / sizeof(cv::Point2f));
//获取内部坐标
calcRotateRect(vRect, vPoints);
//计算灰度值
double dMatch = 0;
for (int v = 0; v < vPoints.size(); v++)
{
if (vPoints[v].x >= 0 && vPoints[v].x <= X&&vPoints[v].y >= 0 && vPoints[v].y <= Y)
{
dMatch += (srcPrev.data)[(vPoints[v].x) + (vPoints[v].y)*X];
}
}
dMatch /= (double)vPoints.size();
//仅扫描一个元件的角度
vParts.push_back(Track(0, 0, dMatch, cv::Point(cvRound(trackCenter.x), cvRound(trackCenter.y)), vRect));
}
if (vParts.size() == 0) continue;
//灰度极值认为是元件
std::sort(vParts.begin(), vParts.end(), std::greater<Track>());
//更新位置
trackCenter = cv::Point(vParts[0].Pos.x, vParts[0].Pos.y);
//更新扫描角度
trackAngle = atan2((double)trackCenter.y - reelCenter.y, (double)trackCenter.x - reelCenter.x) * 180 / PI;
///<开始纵向扫描(横向由于存在间隔所以一般不会出现偏离的情况,除非料盘本身严重变形或者中心定位出问题)
calcRotateRect(trackCenter, (float)trackAngle, (float)trackLength, (float)trackWidth, pts);
//旋转矩形
cv::RotatedRect r(pts[0], pts[1], pts[2]);
//待匹配图像
cv::Rect rr(cv::Point2i(cv::max(r.boundingRect().x - cvRound(trackWidth), 0), \
cv::max(r.boundingRect().y - cvRound(trackWidth), 0)), \
cv::Point2i(cv::min(r.boundingRect().x + r.boundingRect().width + cvRound(trackWidth), X), \
cv::min(r.boundingRect().y + r.boundingRect().height + cvRound(trackWidth), Y)));
//不同方向模板图像(考虑实时更新模板图像或多角度模板,这样可能速度会下降)
cv::Mat yyu = getTplMat(tplMat, 90 - (trackAngle + 180));
//判断待匹配图像是否小于模板图像
if (rr.width < yyu.cols || rr.height < yyu.rows)
continue;
//计算中心位置
cv::Mat tplResult0;
cv::matchTemplate(srcPrev(rr), yyu, tplResult0, cv::TM_CCOEFF_NORMED);
//
double maxyyu; cv::Point maxyyuloc;
cv::minMaxLoc(tplResult0, NULL, &maxyyu, NULL, &maxyyuloc);
//旧标记点
cv::Point2f trackOldCenter = cv::Point2f(trackCenter.x, trackCenter.y);
//更新元件精确位置
trackCenter = cv::Point2f(float(maxyyuloc.x + rr.x + cvRound((float)yyu.cols / 2.0f)), \
float(maxyyuloc.y + rr.y + cvRound((float)yyu.rows / 2.0f)));
//cv::drawMarker(cc, trackCenter, cv::Scalar(0, 0, 255, 255));
//更新扫描半径
trackRadius = cv::norm(trackCenter - reelCenter);
//更新扫描角度
trackAngle = atan2((double)trackCenter.y - reelCenter.y, (double)trackCenter.x - reelCenter.x) * 180 / PI;
//更新偏移量
trackOffset = (2 * asin(2 * trackLength / (2 * trackRadius))) * 180 / PI;
//更新元件间角度
partDist = (2 * asin(dChordL / (2 * trackRadius))) * 180 / PI;
//更新元件区域
calcRotateRect(trackCenter, (float)trackAngle, (float)trackLength, (float)trackWidth, pts);
//需要进行越界处理
cv::RotatedRect rtt(pts[0], pts[1], pts[2]);
//判断是否结束
if (((trackMat.ptr<uint8_t>(cvRound(trackCenter.y))[cvRound(trackCenter.x)] == 255)) || \
(((float)cv::countNonZero(binary((rtt.boundingRect()&cv::Rect(0, 0, X, Y)))) / rtt.size.area()) < 0.25))
{
//如果匹配点被标记,或者偏离既定路径太大不排除标记点出错可能性
found = false;
}
else
{
//标记为已追踪过
std::vector<cv::Point> ptPoly = { cv::Point(pts[0]),cv::Point(pts[1]) ,cv::Point(pts[2]) ,cv::Point(pts[3]) };
cv::fillConvexPoly(trackMat, ptPoly, cv::Scalar(255));
//标记计数
lbMat.ptr<uint8_t>(cvRound(trackCenter.y))[cvRound(trackCenter.x)] = 255;
//画出元件区域
//for (int j = 0; j < 4; j++)
//{
// cv::line(cc, pts[j], pts[(j + 1) % 4], cv::Scalar(0, 165, 255, 255), 1);
//}
//标记当前位置
cv::drawMarker(cc, trackCenter, cv::Scalar(0, 255, 0, 255), cv::MARKER_DIAMOND, 5);
}
trackEnd = (!found);
} while (!trackEnd);
}
}
}
}
//对单个器件间存在断裂使用,及料盘内圈颜色过深
else if (strcmp(ccSubType, "IP_LONG_PARTS") == 0)
{
//二值化
cv::threshold(srcPrev, binary, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
cv::morphologyEx(binary, binary, cv::MORPH_CLOSE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(75, 75)));
//计算直方图
int hist[256];
for (int y = 0; y < 256; y++) hist[y] = 0;
for (int y = 0; y < Y; y++)
{
uchar *uPtr = srcPrev.data + y * X;
for (int x = 0; x < srcPrev.cols; x++, uPtr++)
{
if ((binary.data)[(x)+(y)*X] == 255)
{
hist[*uPtr]++;
}
}
}
cv::threshold(srcPrev, binary, Otsu(hist), 255, cv::THRESH_BINARY);
//去掉料盘深色部分
cv::Mat srcPrevEx;
cv::morphologyEx(srcPrev, srcPrevEx, cv::MORPH_TOPHAT, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)));
cv::threshold(srcPrevEx, srcPrevEx, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
//获得元件区域
cv::morphologyEx(srcPrevEx, srcPrevEx, cv::MORPH_CLOSE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(75, 75)));
//去掉干扰
binary &= srcPrevEx;
//将断裂处连接在一起?
cv::morphologyEx(binary, binary, cv::MORPH_DILATE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)));
//计算元件大小
cv::Mat m1, m2, m3;
int nccomps = cv::connectedComponentsWithStats(binary, m1, m2, m3);
std::vector<uchar> colors0(nccomps + 1, 0);
for (int i = 1; i < nccomps; i++) {
colors0[i] = 255;
if ((((int *)m2.data)[(cv::CC_STAT_AREA) + (i)*m2.cols] <= 2))//经验值
{
colors0[i] = 0;
}
}
//认为是干扰
cv::parallel_for_(cv::Range(0, Y), [&](const cv::Range& range)->void {
for (int y = range.start; y < range.end; y++)
{
for (int x = 0; x < X; x++)
{
int label = ((int *)m1.data)[(x)+(y)*m1.cols];
CV_Assert(0 <= label && label <= nccomps);
(binary.data)[(x)+(y)*X] = colors0[label];
}
}
});
nccomps = cv::connectedComponentsWithStats(binary, m1, m2, m3);
//
if (nccomps <= 1) return false;
//统计元件面积
std::vector<int> vHist(nccomps);
for (int y = 0; y < Y; y++)
{
int *uPtr = (int *)m1.data + y * X;
for (int x = 0; x < X; x++, uPtr++)
{
vHist[*uPtr]++;
}
}
//统计面积个数
std::map<int, int> cAreaMap;
for (const auto& v : vHist)
{
std::map<int, int>::iterator it = cAreaMap.find(v);
if (it != cAreaMap.end())
{
it->second++;
continue;
}
else { cAreaMap.insert(std::make_pair(v, 1)); };
}
struct tMap
{
int Key;
int Value;
tMap(int Key, int Value) :Key(Key), Value(Value) {}
bool operator >(const tMap &te)const
{
return Value > te.Value;
}
};
//获得单个元器件面积(准确性待测试,假定不粘连占大多数!)
std::vector<tMap> tVector;
std::map<int, int>::iterator it;
for (it = cAreaMap.begin(); it != cAreaMap.end(); it++)
{
tVector.push_back(tMap(it->first, it->second));
}
std::sort(tVector.begin(), tVector.end(), std::greater<tMap>());
if (tVector.size() < 2)
{
return FUNC_CANNOT_CALC;
}
//单个元件面积
int sinPartSize = cvRound((tVector[0].Key + tVector[1].Key) / 2.);
//采用追踪算法
nccomps = cv::connectedComponentsWithStats(binary, m1, m2, m3);
//连在一起
cv::Mat srcPrevEx0;
cv::morphologyEx(binary, srcPrevEx0, cv::MORPH_CLOSE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(45, 45)));
//定位料盘中心
std::vector<std::vector<cv::Point>> contoursFilter;
cv::findContours(srcPrevEx0, contoursFilter, cv::RETR_TREE, cv::CHAIN_APPROX_NONE);
cv::Mat image = cv::Mat::zeros(src8U.size(), CV_8UC1);
//
for (int i = 0; i < contoursFilter.size(); i++)
{
cv::drawContours(image, contoursFilter, i, cv::Scalar(255), -1);
}
image -= srcPrevEx0;
//获取最大轮廓
cv::findContours(image, contoursFilter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
if (contoursFilter.size() <= 0)
return FUNC_CANNOT_CALC;
std::vector<cv::Point> contourMax = contoursFilter[0];
for (int i = 1; i < contoursFilter.size(); i++)
{
if (cv::contourArea(contoursFilter[i]) > cv::contourArea(contourMax))
{
contourMax = contoursFilter[i];
}
}
//计算最大外接圆半径
float tFRadius = 0;
cv::minEnclosingCircle(contourMax, cv::Point2f(), tFRadius);
cv::Moments mu = cv::moments(contourMax);
cv::Point2f reelCenter(float(mu.m10 / mu.m00), float(mu.m01 / mu.m00));
//画中心
reelCenter.x = reelCenter.x > 0 && reelCenter.x < X ? reelCenter.x : 0;
reelCenter.y = reelCenter.y > 0 && reelCenter.y < Y ? reelCenter.y : 0;
cv::drawMarker(cc, reelCenter, cv::Scalar(0, 0, 238, 255), 1, 35, 2);
//包含未粘连器件
image = cv::Scalar(0);
std::vector<uchar> colors(nccomps + 1, 0);
for (int i = 1; i < nccomps; i++) {
colors[i] = 255;
if ((((int *)m2.data)[(cv::CC_STAT_AREA) + (i)*m2.cols] >= 1.5*sinPartSize) || (((int *)m2.data)[(cv::CC_STAT_AREA) + (i)*m2.cols] < 0.4*sinPartSize))//经验值
{
colors[i] = 0;
}
}
//认为是粘连
cv::parallel_for_(cv::Range(0, Y), [&](const cv::Range& range)->void {
for (int y = range.start; y < range.end; y++)
{
for (int x = 0; x < X; x++)
{
int label = ((int *)m1.data)[(x)+(y)*m1.cols];
CV_Assert(0 <= label && label <= nccomps);
(image.data)[(x)+(y)*X] = colors[label];
}
}
});
//去掉中心1/3区域
cv::circle(image, reelCenter, cvRound(tFRadius / 3), cv::Scalar(0), -1);
//追踪直至没有单个元件存在
bool bExistSingle = true;
//用于计数
cv::Mat lb4Count(Y, X, CV_8UC1, cv::Scalar(0));
//标签图
unsigned char *ucpTrackLabel = new unsigned char[Y*X]();
cv::Mat trackMat(Y, X, CV_8UC1, ucpTrackLabel);
do
{
//不随机挑选起点(考虑换成面积最小的那个)
std::vector<cv::Point> contourMin;
cv::findContours(image, contoursFilter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
//终止追踪
if (contoursFilter.size() <= 0) break;
//大于等于1个随机挑选
if (contoursFilter.size() > 1)
{
//随机数生成
srand((unsigned)time(NULL));
contourMin = contoursFilter[rand() % (contoursFilter.size() - 1)];
for (int fc = 0; fc < contoursFilter.size(); fc++)
{
if (cv::contourArea(contoursFilter[fc]) > 0.4*sinPartSize)
{
if (cv::contourArea(contoursFilter[fc]) < cv::contourArea(contourMin))
{
contourMin = contoursFilter[fc];
}
}
}
}
else if (contoursFilter.size() == 1)
{
contourMin = contoursFilter[0];
}
//去掉起始位置
std::vector<std::vector<cv::Point>> vTempRect;
vTempRect.push_back(contourMin);
cv::drawContours(image, vTempRect, 0, cv::Scalar(0), -1);
//最小外包矩形
cv::RotatedRect rect = cv::minAreaRect(contourMin);
cv::Point2f points[4];
rect.points(points);
//for (int j = 0; j < 4; j++)
//{
// cv::line(cc, points[j], points[(j + 1) % 4], cv::Scalar(0, 165, 255, 255), 1);
//}
//追踪起点
cv::Point2f startCenter((points[0].x + points[1].x + points[2].x + points[3].x) / 4.f, (points[0].y + points[1].y + points[2].y + points[3].y) / 4.f);
//打标签
cv::Mat labels;
nccomps = cv::connectedComponents(image, labels);
//去掉已处理的分离器件
std::vector<uchar> labeled(nccomps + 1, 0);
//标记为已追踪过
std::vector<cv::Point> vT = { cv::Point(points[0]),cv::Point(points[1]) ,cv::Point(points[2]) ,cv::Point(points[3]) };
cv::fillConvexPoly(trackMat, vT, cv::Scalar(255));
//起点加入计数
cv::circle(lb4Count, cv::Point(startCenter), 0, cv::Scalar(255), 1);
cv::circle(cc, cv::Point(startCenter), 2, cv::Scalar(0, 255, 0, 255), 1);
///<追踪元件算法
struct Track {
int iLimit, iPartSize;
double dMatchDeg;
cv::Point Pos;
std::vector<cv::Point2f> Rect;
Track() {};
Track(int iLimit, int iPartSize, double dMatchDeg, cv::Point Pos, std::vector<cv::Point2f> Rect) :iLimit(iLimit), iPartSize(iPartSize), dMatchDeg(dMatchDeg), Pos(Pos), Rect(Rect) {};
bool operator >(const Track &te)const
{
return dMatchDeg > te.dMatchDeg;
}
};
//扫描步长
const double dMinorStep = 0.1;
//追踪长宽
const double trackLength = std::max(rect.size.width / 2, rect.size.height / 2), trackWidth = std::min(rect.size.width / 4, rect.size.height / 4);
//起始扫描角度
const double startAngle = atan2((double)startCenter.y - reelCenter.y, (double)startCenter.x - reelCenter.x) * 180 / PI;
//起始扫描半径
const double startRadius = cv::norm(startCenter - reelCenter);
//偏移角度(元件尺寸)
const double dOffset = (2 * asin(2 * trackLength / (2 * startRadius))) * 180 / PI;
//偏移角度(元件间距)
const double dScanRange = 15;
//追踪元件间距(弦长,可以尽量避免因个别器件偏离导致的追踪中断)
double dChordL = .0;
for (double t = startAngle + dOffset / 1.5; t < startAngle + dOffset / 1.5 + dScanRange; t += dMinorStep)
{
float x = float(reelCenter.x + startRadius*cos(t*c));
float y = float(reelCenter.y + startRadius*sin(t*c));
//初次确定元件间距
const double angle = atan2((double)reelCenter.y - y, (double)reelCenter.x - x);
cv::Point p1 = cv::Point(cvRound(x + trackWidth * cos(angle)),
cvRound(y + trackWidth * sin(angle)));
cv::Point p2 = cv::Point(cvRound(x + trackWidth * cos(angle + CV_PI)),
cvRound(y + trackWidth * sin(angle + CV_PI)));
cv::LineIterator it(binary, p1, p2, 4);
for (int n = 0; n < it.count; n++, ++it)
{
if ((binary.data)[(it.pos().x) + (it.pos().y)*X] == 255)
{
//计算元件间距(弦长)
dChordL = 2.0 * startRadius*sin(((2.0 * asin((cv::norm(startCenter - cv::Point2f(x, y))) / (2.0 * startRadius))) * 180.0 / PI - dOffset / 2.0)*PI / 180.0 / 2.0);
break;
}
}
if (dChordL > 0)
break;
}
//并行处理
//#pragma omp parallel sections
{
//(顺时针)
//#pragma omp section
{
//追踪中心
cv::Point2f trackCenter = cv::Point2f(startCenter.x, startCenter.y);
//追踪角度、半径
double trackAngle = startAngle, trackRadius = startRadius;
//元件本身角度
double trackOffset = dOffset;
//元件间间距
double partDist = (2 * asin(dChordL / (2 * trackRadius))) * 180 / PI;
//外包矩形顶点
cv::Point2f pts[4];
//结束位置
Track trackEndPos;
//开始追踪
bool trackEnd = true;
do
{
bool found = true;
std::vector<Track> vParts;
for (double t = trackAngle + (trackOffset / 2.0 + partDist); t < trackAngle + (trackOffset / 2.0 + partDist) + trackOffset; t += dMinorStep)
{
trackCenter.x = reelCenter.x + (float)trackRadius*(float)cos(t*c);
trackCenter.y = reelCenter.y + (float)trackRadius*(float)sin(t*c);
float b = (float)cos(t*c)*0.5f;
float a = (float)sin(t*c)*0.5f;
pts[0].x = float(trackCenter.x - a*trackLength * 2 - b*trackWidth * 4);
pts[0].y = float(trackCenter.y + b*trackLength * 2 - a*trackWidth * 4);
pts[1].x = float(trackCenter.x + a*trackLength * 2 - b*trackWidth * 4);
pts[1].y = float(trackCenter.y - b*trackLength * 2 - a*trackWidth * 4);
pts[2].x = float(2 * trackCenter.x - pts[0].x);
pts[2].y = float(2 * trackCenter.y - pts[0].y);
pts[3].x = float(2 * trackCenter.x - pts[1].x);
pts[3].y = float(2 * trackCenter.y - pts[1].y);
std::vector<cv::Point> vPoints;
std::vector<cv::Point2f> vRect(pts, pts + sizeof(pts) / sizeof(cv::Point2f));
//获取内部坐标
calcRotateRect(vRect, vPoints);
//计算灰度值
double dMatch = 0;
for (int v = 0; v < vPoints.size(); v++)
{
if (vPoints[v].x >= 0 && vPoints[v].x <= X&&vPoints[v].y >= 0 && vPoints[v].y <= Y)
{
dMatch += (srcPrev.data)[(vPoints[v].x) + (vPoints[v].y)*X];
}
}
dMatch /= (double)vPoints.size();
//仅扫描一个元件的角度
vParts.push_back(Track(0, 0, dMatch, cv::Point(cvRound(trackCenter.x), cvRound(trackCenter.y)), vRect));
//cv::circle(cc, cv::Point(cvRound(trackCenter.x), cvRound(trackCenter.y)), 0, cv::Scalar(0, 255, 255, 255), 1);
}
if (vParts.size() == 0) continue;
//
trackEndPos = vParts[vParts.size() / 2];
//灰度极值认为是元件
std::sort(vParts.begin(), vParts.end(), std::greater<Track>());
//更新位置
trackCenter = cv::Point(vParts[0].Pos.x, vParts[0].Pos.y);
//更新扫描角度
trackAngle = atan2((double)trackCenter.y - reelCenter.y, (double)trackCenter.x - reelCenter.x) * 180 / PI;
//纵向扫描
vParts.clear();
std::vector<cv::Point> trackLine;
drawLine(cc, reelCenter, trackCenter, cv::Scalar(0, 255, 255, 255), 1, trackLength, trackWidth * 2, trackLine);
//更改纵向扫描方向,分两个方向?
cv::LineIterator it(binary, trackLine[0], trackLine[1], 4);
for (int n = 0; n < it.count; n++, ++it)
{
float b = (float)cos(trackAngle*PI / 180.)*0.5f;
float a = (float)sin(trackAngle*PI / 180.)*0.5f;
pts[0].x = (float)(it.pos().x - a*trackLength * 2 - b*trackWidth * 4);
pts[0].y = (float)(it.pos().y + b*trackLength * 2 - a*trackWidth * 4);
pts[1].x = (float)(it.pos().x + a*trackLength * 2 - b*trackWidth * 4);
pts[1].y = (float)(it.pos().y - b*trackLength * 2 - a*trackWidth * 4);
pts[2].x = (float)(2 * it.pos().x - pts[0].x);
pts[2].y = (float)(2 * it.pos().y - pts[0].y);
pts[3].x = (float)(2 * it.pos().x - pts[1].x);
pts[3].y = (float)(2 * it.pos().y - pts[1].y);
std::vector<cv::Point> vPoints;
std::vector<cv::Point2f> vRect(pts, pts + sizeof(pts) / sizeof(cv::Point2f));
//获取内部坐标
calcRotateRect(vRect, vPoints);
//计算灰度值
int iLimit = 0, iPartSize = 0;
double dMatch = 0;
for (int v = 0; v < vPoints.size(); v++)
{
if (vPoints[v].x >= 0 && vPoints[v].x <= X&&vPoints[v].y >= 0 && vPoints[v].y <= Y)
{
iLimit += ucpTrackLabel[(vPoints[v].x) + (vPoints[v].y)*X];
dMatch += (srcPrev.data)[(vPoints[v].x) + (vPoints[v].y)*X];
if ((binary.data)[(vPoints[v].x) + (vPoints[v].y)*X] == 255)
iPartSize++;
}
}
vParts.push_back(Track(iLimit, iPartSize, dMatch, it.pos(), vRect));
//cv::circle(cc, it.pos(), 0, cv::Scalar(255, 0, 0, 255), 1);
}
if (vParts.size() == 0) continue;
//方案二每个点以当前半径画圆,看下个点偏离圆多少
//灰度极值认为是元件(最多问题出现在这里,加个条件判断矩形内是否存在已标记像素)
std::sort(vParts.begin(), vParts.end(), std::greater<Track>());
//更新当前元件位置(必须不与已有元件重合)
Track mac = vParts[0];
if (mac.iLimit != 0)
{
for (int cc = 1; cc < vParts.size(); cc++)
{
if (vParts[cc].iLimit < mac.iLimit)
mac = vParts[cc];
if (mac.iLimit == 0)
break;
}
}
trackCenter = mac.Pos;
//更新扫描半径
trackRadius = cv::norm(trackCenter - reelCenter);
//更新扫描角度
trackAngle = atan2((double)trackCenter.y - reelCenter.y, (double)trackCenter.x - reelCenter.x) * 180 / PI;
//更新偏移量(元件大小)
trackOffset = (2 * asin(2 * trackLength / (2 * trackRadius))) * 180 / PI;
//更新元件间角度
partDist = (2 * asin(dChordL / (2 * trackRadius))) * 180 / PI;
//判断是否结束
if ((mac.iPartSize < sinPartSize / 4) || (trackMat.at<uchar>((cvRound(trackCenter.y)), (cvRound(trackCenter.x))) == 255) || (mac.iLimit / 255) > (rect.size.area() / 4) || (binary.at<uchar>((cvRound(trackCenter.y)), (cvRound(trackCenter.x))) == 0))
{
found = false;
//for (int j = 0; j < 4; j++)
//{
// cv::line(cc, trackEndPos.Rect[j], trackEndPos.Rect[(j + 1) % 4], cv::Scalar(0, 0, 255, 255), 1);
//}
//cv::circle(cc, trackCenter, 1, cv::Scalar(0, 255, 0, 255), 1);
}
else
{
//画出最终位置
std::vector<cv::Point> ptPoly;
for (int j = 0; j < 4; j++)
{
ptPoly.push_back(cv::Point(cvRound(mac.Rect[j].x), cvRound(mac.Rect[j].y)));
//cv::line(cc, mac.Rect[j], mac.Rect[(j + 1) % 4], cv::Scalar(0, 255, 0, 255), 1);
}
cv::circle(cc, trackCenter, 2, cv::Scalar(0, 255, 0, 255), 1);
cv::circle(lb4Count, trackCenter, 0, cv::Scalar(255), 1);
//标记Label
cv::fillConvexPoly(trackMat, ptPoly, cv::Scalar(255));
//获得已处理标签
std::vector<cv::Point> vTemp;
calcRotateRect(mac.Rect, vTemp);
for (int p = 0; p < vTemp.size(); p++)
{
if (vTemp[p].x >= 0 && vTemp[p].x <= X&&vTemp[p].y >= 0 && vTemp[p].y <= Y)
{
int label = labels.at<int>(vTemp[p]);
if (label != 0)
{
labeled[label] = 255;
break;
}
}
}
}
trackEnd = (!found);
} while (!trackEnd);
}
//#pragma omp section //模板匹配/更新元件精确位置
//逆时针追踪 double maxyyu;
{ findTrackModel(srcPrev, tplMat, 90 - (trackAngle + 180), trackWidth, pts, maxyyu, trackCenter);
//追踪起点
cv::Point2f trackCenter(startCenter.x, startCenter.y);
//起始扫描角度、半径
double trackAngle = startAngle, trackRadius = startRadius;
//元件本身角度
double trackOffset = dOffset;
//元件间间距
double partDist = (2 * asin(dChordL / (2 * trackRadius))) * 180 / PI;
//当扫描一圈后修正中心位置(待测试)
cv::Point2f pts[4];
//结束位置
Track trackEndPos;
//开始追踪
bool trackEnd = true;
//
do
{
bool found = true;
std::vector<Track> vParts;
for (double t = trackAngle - (partDist + trackOffset / 2.0); t > trackAngle - (partDist + trackOffset / 2.0) - trackOffset; t -= dMinorStep)
{
trackCenter.x = float(reelCenter.x + trackRadius*cos(t*c));
trackCenter.y = float(reelCenter.y + trackRadius*sin(t*c));
float b = (float)cos(t*c)*0.5f; //更新扫描半径
float a = (float)sin(t*c)*0.5f; trackRadius = cv::norm(trackCenter - reelCenter);
pts[0].x = (float)(trackCenter.x - a*trackLength * 2 - b*trackWidth * 4); //更新扫描角度
pts[0].y = (float)(trackCenter.y + b*trackLength * 2 - a*trackWidth * 4); trackAngle = atan2((double)trackCenter.y - reelCenter.y, (double)trackCenter.x - reelCenter.x) * 180 / PI;
pts[1].x = (float)(trackCenter.x + a*trackLength * 2 - b*trackWidth * 4); //更新偏移量
pts[1].y = (float)(trackCenter.y - b*trackLength * 2 - a*trackWidth * 4); trackOffset = (2 * asin(2 * trackLength / (2 * trackRadius))) * 180 / PI;
pts[2].x = (float)(2 * trackCenter.x - pts[0].x); //更新元件间角度
pts[2].y = (float)(2 * trackCenter.y - pts[0].y); partDist = (2 * asin(dChordL / (2 * trackRadius))) * 180 / PI;
pts[3].x = (float)(2 * trackCenter.x - pts[1].x);
pts[3].y = (float)(2 * trackCenter.y - pts[1].y);
std::vector<cv::Point> vPoints; //更新元件区域
std::vector<cv::Point2f> vRect(pts, pts + sizeof(pts) / sizeof(cv::Point2f)); calcRotateRect(trackCenter, (float)trackAngle, (float)trackLength, (float)trackWidth, pts);
//获取内部坐标
calcRotateRect(vRect, vPoints);
//计算灰度值
double dMatch = 0;
for (int v = 0; v < vPoints.size(); v++)
{
if (vPoints[v].x >= 0 && vPoints[v].x <= X&&vPoints[v].y >= 0 && vPoints[v].y <= Y)
{
dMatch += (srcPrev.data)[(vPoints[v].x) + (vPoints[v].y)*X];
}
}
dMatch /= (double)vPoints.size();
//仅扫描一个元件的角度
vParts.push_back(Track(0, 0, dMatch, cv::Point(cvRound(trackCenter.x), cvRound(trackCenter.y)), vRect));
//cv::circle(cc, trackCenter, 0, cv::Scalar(0, 255, 255, 255), 1);
}
if (vParts.size() == 0) continue;
//
trackEndPos = vParts[vParts.size() / 2];
//灰度极值认为是元件
std::sort(vParts.begin(), vParts.end(), std::greater<Track>());
//更新位置
trackCenter = cv::Point(vParts[0].Pos.x, vParts[0].Pos.y);
//更新扫描角度
trackAngle = atan2((double)trackCenter.y - reelCenter.y, (double)trackCenter.x - reelCenter.x) * 180 / PI;
//纵向扫描
vParts.clear();
std::vector<cv::Point> trackLine;
drawLine(cc, reelCenter, trackCenter, cv::Scalar(0, 255, 255, 255), 1, trackLength, trackWidth * 2, trackLine);
//更改纵向扫描方向,分两个方向
cv::LineIterator it(binary, trackLine[0], trackLine[1], 4);
for (int n = 0; n < it.count; n++, ++it)
{
float b = (float)cos(trackAngle*PI / 180.)*0.5f;
float a = (float)sin(trackAngle*PI / 180.)*0.5f;
pts[0].x = (float)(it.pos().x - a*trackLength * 2 - b*trackWidth * 4);
pts[0].y = (float)(it.pos().y + b*trackLength * 2 - a*trackWidth * 4);
pts[1].x = (float)(it.pos().x + a*trackLength * 2 - b*trackWidth * 4);
pts[1].y = (float)(it.pos().y - b*trackLength * 2 - a*trackWidth * 4);
pts[2].x = (float)(2 * it.pos().x - pts[0].x);
pts[2].y = (float)(2 * it.pos().y - pts[0].y);
pts[3].x = (float)(2 * it.pos().x - pts[1].x);
pts[3].y = (float)(2 * it.pos().y - pts[1].y);
std::vector<cv::Point> vPoints; //需要进行越界处理
std::vector<cv::Point2f> vRect(pts, pts + sizeof(pts) / sizeof(cv::Point2f)); cv::RotatedRect rtt(pts[0], pts[1], pts[2]);
//获取内部坐标 //判断是否结束
calcRotateRect(vRect, vPoints); if (((trackMat.ptr<uint8_t>(cvRound(trackCenter.y))[cvRound(trackCenter.x)] == 255)) || \
//计算灰度值 (((float)cv::countNonZero(binary((rtt.boundingRect()&cv::Rect(0, 0, X, Y)))) / rtt.size.area()) < 0.25))
int iLimit = 0, iPartSize = 0;
double dMatch = 0;
for (int v = 0; v < vPoints.size(); v++)
{
if (vPoints[v].x >= 0 && vPoints[v].x <= X&&vPoints[v].y >= 0 && vPoints[v].y <= Y)
{
iLimit += ucpTrackLabel[(vPoints[v].x) + (vPoints[v].y)*X];
dMatch += (srcPrev.data)[(vPoints[v].x) + (vPoints[v].y)*X];
if ((binary.data)[(vPoints[v].x) + (vPoints[v].y)*X] == 255)
iPartSize++;
}
}
vParts.push_back(Track(iLimit, iPartSize, dMatch, it.pos(), vRect));
//cv::circle(cc, it.pos(), 0, cv::Scalar(255, 0, 0, 255), 1);
}
if (vParts.size() == 0) continue;
//灰度极值认为是元件
std::sort(vParts.begin(), vParts.end(), std::greater<Track>());
//更新当前元件位置
Track mac = vParts[0];
if (mac.iLimit != 0)
{
for (int cc = 1; cc < vParts.size(); cc++)
{
if (vParts[cc].iLimit < mac.iLimit)
mac = vParts[cc];
if (mac.iLimit == 0)
break;
}
}
trackCenter = mac.Pos;
//更新扫描半径
trackRadius = cv::norm(trackCenter - reelCenter);
//更新扫描角度
trackAngle = atan2((double)trackCenter.y - reelCenter.y, (double)trackCenter.x - reelCenter.x) * 180 / PI;
//更新偏移量
trackOffset = (2 * asin(2 * trackLength / (2 * trackRadius))) * 180 / PI;
//更新追踪角度
partDist = (2 * asin(dChordL / (2 * trackRadius))) * 180 / PI;
//绕完一周后更新料盘中心试试?
//判断是否结束
if (mac.iPartSize < sinPartSize / 4 || (trackMat.at<uchar>((cvRound(trackCenter.y)), (cvRound(trackCenter.x))) == 255) || (mac.iLimit / 255) >(rect.size.area() / 4) || (binary.at<uchar>((cvRound(trackCenter.y)), (cvRound(trackCenter.x))) == 0))
{
found = false;
//for (int j = 0; j < 4; j++)
//{
// cv::line(cc, trackEndPos.Rect[j], trackEndPos.Rect[(j + 1) % 4], cv::Scalar(0, 0, 255, 255), 1);
//}
//cv::circle(cc, trackCenter, 1, cv::Scalar(0, 255, 0, 255), 1);
}
else
{
//画出最终位置
std::vector<cv::Point> ptPoly;
for (int j = 0; j < 4; j++)
{
ptPoly.push_back(cv::Point(cvRound(mac.Rect[j].x), cvRound(mac.Rect[j].y)));
//cv::line(cc, mac.Rect[j], mac.Rect[(j + 1) % 4], cv::Scalar(0, 255, 0, 255), 1);
}
//
cv::circle(cc, trackCenter, 2, cv::Scalar(0, 255, 0, 255), 1);
cv::circle(lb4Count, trackCenter, 0, cv::Scalar(255), 1);
//标记Label
cv::fillConvexPoly(trackMat, ptPoly, cv::Scalar(255));
//获得已处理标签
std::vector<cv::Point> vTemp;
calcRotateRect(mac.Rect, vTemp);
for (int p = 0; p < vTemp.size(); p++)
{
if (vTemp[p].x >= 0 && vTemp[p].x <= X&&vTemp[p].y >= 0 && vTemp[p].y <= Y)
{
int label = labels.at<int>(vTemp[p]);
if (label != 0)
{
labeled[label] = 255;
break;
}
}
}
}
trackEnd = (!found);
} while (!trackEnd);
}
}
//去掉已标记处理的
cv::parallel_for_(cv::Range(0, Y), [&](const cv::Range& range)->void {
for (int y = range.start; y < range.end; y++)
{
for (int x = 0; x < X; x++)
{ {
int label = ((int *)labels.data)[(x)+(y)*labels.cols]; //如果匹配点被标记,或者偏离既定路径太大不排除标记点出错可能性
CV_Assert(0 <= label && label <= nccomps); found = false;
if (labeled[label]) //cv::drawMarker(cc, cv::Point(cvRound(trackCenter.x), cvRound(trackCenter.y)), cv::Scalar(0, 0, 255, 255), cv::MARKER_DIAMOND, 5);
{
((int *)(labels.data))[(x)+(y)*X] = 0;
}
} }
} else
}); {
image = labels > 0; //标记为已追踪过
//判断是否存在未追踪单个料 std::vector<cv::Point> ptPoly = { cv::Point(pts[0]),cv::Point(pts[1]) ,cv::Point(pts[2]) ,cv::Point(pts[3]) };
bExistSingle = (cv::countNonZero(image) == 0); cv::fillConvexPoly(trackMat, ptPoly, cv::Scalar(255));
} while (!bExistSingle);
//拷贝计数
binary = lb4Count.clone();
//释放资源
delete[] ucpTrackLabel;
ucpTrackLabel = NULL;
}
//方形托盘
else if (strcmp(ccSubType, "IP_SQUARE_PARTS") == 0)
{
} //标记计数
//普通算法不做其他处理 lbMat.ptr<uint8_t>(cvRound(trackCenter.y))[cvRound(trackCenter.x)] = 255;
else
{ //画出元件区域
//二值化 //for (int j = 0; j < 4; j++)
cv::threshold(srcPrev, binary, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU); //{
cv::morphologyEx(binary, binary, cv::MORPH_CLOSE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(75, 75))); // cv::line(cc, pts[j], pts[(j + 1) % 4], cv::Scalar(0, 165, 255, 255), 1);
//计算直方图 //}
int hist[256];
for (int y = 0; y < 256; y++) hist[y] = 0; //标记当前位置
for (int y = 0; y < Y; y++) cv::drawMarker(cc, trackCenter, cv::Scalar(0, 255, 0, 255), cv::MARKER_DIAMOND, 5);
{ }
uchar *uPtr = srcPrev.data + y * X;
for (int x = 0; x < srcPrev.cols; x++, uPtr++) trackEnd = (!found);
{ } while (!trackEnd);
if ((binary.data)[(x)+(y)*X] == 255)
{
hist[*uPtr]++;
}
} }
} }
cv::threshold(srcPrev, binary, Otsu(hist), 255, cv::THRESH_BINARY);
//粗略计数
cv::Mat labels, stats, centroids;
int numObj = cv::connectedComponentsWithStats(binary, labels, stats, centroids);
//坐标图
binary = cv::Scalar(0);
//画图
double *dpCent = (double *)centroids.data;
for (int j = 1; j < numObj; j++)
{
binary.at<uchar>(cv::Point(cvRound((float)dpCent[(0) + (j) * 2]), cvRound((float)dpCent[(1) + (j) * 2]))) = 255;
}
} }
//计数 //计数
int numObj = cv::countNonZero(lbMat); int numObj = cv::countNonZero(lbMat);
//画图显示
std::string text = "Reel Number = " + std::to_string(numObj); std::string text = "Reel Number = " + std::to_string(numObj);
cv::putText(cc, text, cv::Point(35, 35), 0, 1.0, cv::Scalar(0, 140, 255, 255), 2); cv::putText(cc, text, cv::Point(35, 35), 0, 1.0, cv::Scalar(0, 140, 255, 255), 2);
......
...@@ -6,11 +6,12 @@ ...@@ -6,11 +6,12 @@
#define __EYEM_MISC_H #define __EYEM_MISC_H
#include <io.h> #include <io.h>
#include <omp.h>
#include <fstream> #include <fstream>
#include <direct.h> #include <direct.h>
#include "eyemLib.h" #include "eyemLib.h"
#include <omp.h>
constexpr double c = PI / 180.; constexpr double c = PI / 180.;
......
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!