Commit 6c7e636f 张士柳

1 个父辈 c7efc275
......@@ -1227,34 +1227,6 @@ namespace eyemLib_Sharp
Stopwatch sw = new Stopwatch();
sw.Restart();
string file = fileName.Split(new string[] { "\\" }, StringSplitOptions.RemoveEmptyEntries)[2];
//string[] fileNames = Directory.GetFiles(@"C:\Users\nzslw\PycharmProjects\pythonProject\venv\data\AntBee\test\ants", "*.jpg", SearchOption.AllDirectories);
//return;
//foreach (var item in fileNames)
//{
// eyemBuildTrainFile(item, "D:\\标签识别\\" + item.Split(new string[] { "\\" }, StringSplitOptions.RemoveEmptyEntries)[4]);
//}
//return;
//for (int i = 0; i < fileNames.Length; i++)
//{
// string item = fileNames[i];
// string[] items = item.Split(new string[] { "\\" }, StringSplitOptions.RemoveEmptyEntries);
// FileInfo fi = new FileInfo(item);
// fi.MoveTo(@"D:\日志\" + (i+83).ToString() + ".png");
//}
//return;
//StreamWriter swr = new StreamWriter("D:\\val.txt", true);
//foreach (var item in random_shuffle)
//{
// string[] items = item.Split(new string[] { "\\" }, StringSplitOptions.RemoveEmptyEntries);
// swr.WriteLine("data/val_images/" + items[items.Length - 1]);
// swr.Flush();
//}
//return;
//eyemRenameFile(@"D:\训练数据集\20211021");
//return;
//flag = eyemInitNNDetector(".\\darknet\\cifar_resnet50.cfg", ".\\darknet\\cifar_resnet50.weights");
//if (flag == 0)
......@@ -1388,8 +1360,6 @@ namespace eyemLib_Sharp
//eyemImageFree(ref search);
//return;
//flag = eyemNormalize(ref image);
//EyemImage image1 = new EyemImage(); EyemImage image2 = new EyemImage(); EyemImage image3 = new EyemImage();
//eyemDecompose(image, out image1, out image2, out image3);
......@@ -1655,12 +1625,12 @@ namespace eyemLib_Sharp
int[] ipReelNum = new int[4];
//"IP_SMALL_PARTS","IP_LARGE_PARTS","IP_LONG_PARTS","IP_SQUARE_PARTS","IP_DYNAMIC_PARTS","","IP_DYNAMIC_SP1","IP_DYNAMIC_SP2"
//eyemCountObject(image, tpRoi, file.Replace(".png", ""), ipReelNum, out tpDstImg);
//eyemCountObjectIrregularParts(image, tpRoi, file.Replace(".png", ""), "IP_LARGE_PARTS", ipReelNum, out tpDstImg);
eyemCountObjectE(image, tpRoi, file.Replace(".png", ""), ipReelNum, out tpDstImg);
//eyemCountObjectIrregularParts(image, tpRoi, file.Replace(".png", ""), "IP_LONG_PARTS", ipReelNum, out tpDstImg);
//eyemCountObjectE(image, tpRoi, file.Replace(".png", ""), ipReelNum, out tpDstImg);
//eyemCountObjectIrregularPartsE(image, tpRoi, file.Replace(".png", ""), "D:\\模板文件\\" + "20210825095751-1.tpl", hModelID, ipReelNum, out tpDstImg);
//算法选项
//std::string sOptions[8] = { "IP_DEFAULT_PARTS","IP_SMALL_PARTS","IP_LARGE_PARTS","IP_LONG_PARTS","IP_SQUARE_PARTS","","IP_DYNAMIC_SP1","IP_DYNAMIC_SP2" };
//eyemCountObjectIrregularPartsMultiopt(image, tpRoi, new int[] { 2, 2, 2, 2 }, ipReelNum, out tpDstImg);
eyemCountObjectIrregularPartsMultiopt(image, tpRoi, new int[] { 0, 0, 0, 0 }, ipReelNum, out tpDstImg);
//移除模板
//flag = eyemRemoveModelByName(hModelID, "D:\\模板文件及图像\\df871193-6632-48f9-abfe-540c3fc49c3f.tpl");
......
......@@ -28,7 +28,7 @@ namespace eyemLib_Sharp
eyemImageRead(fileName, -1, out image);
}
/// <summary>
/// 从Bitmap初始化Unmanaged新实例(GDI不支持除8位以外深度的图像;若要加载不同深度图像请使用从文件名加载
/// 从Bitmap初始化Unmanaged新实例(GDI不支持除8位以外深度的图像;若要加载不同深度图像请从文件名初始化
/// </summary>
/// <param name="bitmap"></param>
public UnmanagedBitmap(Bitmap bitmap)
......
......@@ -8,7 +8,7 @@
#include <opencv.hpp>
#ifndef FILEVERSION
#define FILEVERSION "2.4.8.2"
#define FILEVERSION "2.4.8.4"
#endif
#ifndef COPYRIGHT
......
此文件类型无法预览
......@@ -1791,7 +1791,8 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
int backThresh = 15 * cvRound((double)maxIdx[0] - processLevel);
//去掉背景
cv::parallel_for_(cv::Range(0, Y), [&](const cv::Range range)->void {
for (int y = range.start; y < range.end; y++) {
for (int y = range.start; y < range.end; y++)
{
for (int x = 0; x < X; x++) {
if ((src8U.data)[(x)+(y)*X] >= backThresh) {
(src8U.data)[(x)+(y)*X] = backThresh;
......@@ -1801,6 +1802,7 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
});
//增强到目标亮度方便显示
cc += cv::Scalar((162 - backThresh), (162 - backThresh), (162 - backThresh));
//cv::imwrite("cc.png",cc);
//料盘中心
cv::Point reelPt;
//去掉干扰
......@@ -2182,7 +2184,7 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
image = cv::Scalar(0);
//当仅剩一圈的情况
cv::Mat srcPrevEx1;
cv::morphologyEx(binary, srcPrevEx1, cv::MORPH_DILATE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(15, 15)));
cv::morphologyEx(binary, srcPrevEx1, cv::MORPH_DILATE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(31, 31)));
cv::findContours(srcPrevEx1, contoursFilter, cv::RETR_TREE, cv::CHAIN_APPROX_NONE);
//闭合轮廓
......@@ -2198,10 +2200,10 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
cv::findContours(image, contoursFilter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
if (contoursFilter.empty())
{
{
//当不足一圈,中心定位是不对的无法应用追踪算法
return FUNC_CANNOT_CALC;
}
}
}
//过滤轮廓
std::vector<cv::Point> contourMax = contoursFilter[0];
......@@ -2215,6 +2217,9 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
//计算最大外接圆半径
float tFRadius = 0;
cv::minEnclosingCircle(contourMax, cv::Point2f(), tFRadius);
//
tFRadius = tFRadius < 300 ? 300 : tFRadius;
cv::Moments mu = cv::moments(contourMax);
cv::Point2f reelCenter(float(mu.m10 / mu.m00), float(mu.m01 / mu.m00));
//画中心
......@@ -2297,6 +2302,36 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
return dMatchDeg < te.dMatchDeg;
}
};
#ifdef _DEBUG
/** 链码节点(理想情况是一盘料首尾相接,对于追踪条件较好的料盘有用,但是对于存在乱七八糟的起点则难以追踪)
bJunction 是否是节点
iDirect 追踪方向(1 顺时针 -1 逆时针)
iStartOrEnd 起点或终点(0 起点 1 终点 2 中间节点)
iLinkID (每颗料都有的唯一编号)
iChainID 批次编号
iContPrev、iContCt、iContNxt 链接节点(指的是两个不同追踪流程连接处,用iLinkID表示,默认0)
vInfo 位置信息
ptCenter 元件中心坐标
*/
struct Link
{
//是否是接口处
bool bJunction = false;
//方向(1 顺时针 -1 逆时针),起点或终点(0 起点 1 终点 2 中间),编号(总编号),批次编号
int iDirect, iStartOrEnd, iLinkID, iChainID;
//连接节点
int iContPrev, iContCt, iContNxt;//iContPrev 上一个编号,iContCt 当前编号,iContNxt下一个编号
//位置信息
std::vector<cv::Point> vInfo;
//中心
cv::Point ptCenter;
Link() {};
Link(bool bJunction, int iDirect, int iStartOrEnd, int iLinkID, int iChainID, int iContPrev, int iContCt, int iContNxt, cv::Point ptCenter, std::vector<cv::Point> vInfo) :
bJunction(bJunction), iDirect(iDirect), iStartOrEnd(iStartOrEnd), iLinkID(iLinkID), iChainID(iChainID), iContPrev(iContPrev), iContCt(iContCt), iContNxt(iContNxt), ptCenter(ptCenter), vInfo(vInfo) {};
};
#endif
//缩放比例
float coeff = 1.0f;
//填充值
......@@ -2310,6 +2345,11 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
cv::Mat trackMat(Y, X, CV_8UC1, ucpTrackLabel);
//计数图像
cv::Mat lbMat(Y, X, CV_8UC1, cv::Scalar(0));
#ifdef _DEBUG //<编号图像(2022034:新增测试用)
int tracingID = 0;
std::vector<Link> Links;
cv::Mat tracingChainMap(Y, X, CV_16SC3, cv::Scalar(-32768, -32768, -32768));
#endif
//定位图像
cv::Mat srcPrevS, tplMat;//模板文件
srcPrev.convertTo(srcPrevS, CV_32F);
......@@ -2322,6 +2362,8 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
logger.t("eyemCountObjectIrregularParts 点料阶段被跳过执行...");
break;
}
//20220314:新增测试用,起始编号等信息
auto chainID = (int)std::distance(tracingAnchors.begin(), itvx) + 1;
//起始位置信息
TracingAnchor ta = (*itvx);
//起始位置坐标
......@@ -2428,9 +2470,15 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
//最大值
cv::minMaxLoc(tplMat, NULL, &taMaxGray);
}
//标记为已追踪过
std::vector<cv::Point> vT = { cv::Point(_pts[0]),cv::Point(_pts[1]) ,cv::Point(_pts[2]) ,cv::Point(_pts[3]) };
cv::fillConvexPoly(trackMat, vT, cv::Scalar(255));
#ifdef _DEBUG //<20220314:新增测试用
tracingID++;
Links.push_back(Link(false, 1, 0, 0, chainID, -1, 0, tracingID, startCenter, vT));
cv::fillConvexPoly(tracingChainMap, vT, cv::Scalar(0, chainID, Links.back().iContCt));
#endif
//标记计数
lbMat.ptr<uint8_t>(cvRound(startCenter.y))[cvRound(startCenter.x)] = 255;
//标记当前位置
......@@ -2489,8 +2537,6 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
if (dChordL <= 2.1) {
continue;
}
//20220307测试用:对每次追踪进行编号?然后呢。。。
//顺时针(是否并行取决于在windows下运行还是树莓派上)
{
//追踪中心
......@@ -2506,7 +2552,7 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
do
{
bool found = true; bool trayEnd = false;
std::vector<Track> vParts;
std::vector<Track> vParts; cv::Point2f predictCenter;//20220316新增测试用
for (double t = trackAngle + (trackOffset + partDist - trackOffset / 12.0); t < trackAngle + (trackOffset + partDist - trackOffset / 12.0) + trackOffset / 6.0; t += dMinorStep)
{
cv::Point2f predicPos;
......@@ -2517,6 +2563,8 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
trayEnd = true;
break;
}
//20220316新增测试用
predictCenter = cv::Point2f(predicPos.x, predicPos.y);
//感兴趣区域(向外扩展了一个元件,防止中心定位出现偏差或者料盘本身变形导致的偏差)
cv::Point2f predictBox[4];
calcRotateRect(predicPos, (float)(trackAngle + (trackOffset + partDist)), (float)trackLength + (float)trackLength, (float)trackWidth + (float)trackWidth, predictBox);
......@@ -2791,6 +2839,10 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
}
//追踪终止,选取下一个起点
if (trayEnd) {
#ifdef _DEBUG
Links.back().iStartOrEnd = 1;
cv::drawMarker(cc, predictCenter, cv::Scalar(0, 0, 255, 255), cv::MARKER_TILTED_CROSS, 10, 1);
#endif
break;
}
//更新位置
......@@ -2803,10 +2855,60 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
trackOffset = (2 * asin(2 * trackLength / (2 * trackRadius))) * 180.0 / PI;
//更新元件间角度
partDist = (2 * asin(dChordL / (2 * trackRadius))) * 180.0 / PI;
//追踪到了重复的元件
//追踪到了已经标记的元件
if ((trackCenter.x<0 || trackCenter.x>X - 1 ||
trackCenter.y<0 || trackCenter.y>Y - 1) || trackMat.ptr<uint8_t>(cvRound(trackCenter.y))[cvRound(trackCenter.x)] == 255) {
found = false;
#ifdef _DEBUG //<20220314:测试用,获取连接节点junction,大量偏离如何处理
auto linkInfo = tracingChainMap.ptr<cv::Vec3s>(cvRound(trackCenter.y))[cvRound(trackCenter.x)];
//如果是反方向判断是junction,如果是同方向并且批次一样只能是偏离
if (linkInfo[0] == -1) {
//修改编号
Links.back().iContNxt = linkInfo[2]; Links.back().bJunction = true;
}
else {
//同批次且方向相同,判断是偏离
if (chainID == linkInfo[1]) {
//修改结束标志
found = true;
//判断不是junction而是追踪偏离,后续可以在这里增加偏离处理
Links.back().iStartOrEnd = 1;
//判断为偏离,采用理论位置代替
trackCenter = cv::Point2f(predictCenter.x, predictCenter.y);
//更新扫描半径
trackRadius = cv::norm(trackCenter - reelCenter);
//更新扫描角度
trackAngle = atan2((double)trackCenter.y - (double)reelCenter.y, (double)trackCenter.x - (double)reelCenter.x) * 180.0 / PI;
//更新偏移量(元件角度大小)
trackOffset = (2.0 * asin(2.0 * trackLength / (2.0 * trackRadius))) * 180.0 / PI;
//更新元件间角度
partDist = (2.0 * asin(dChordL / (2.0 * trackRadius))) * 180.0 / PI;
//计算元件位置
cv::Point2f pts[4];
calcRotateRect(trackCenter, (float)trackAngle, (float)trackLength, (float)trackWidth, pts);
//标记计数
lbMat.ptr<uint8_t>(cvRound(trackCenter.y))[cvRound(trackCenter.x)] = 255;
//标记为已追踪过
std::vector<cv::Point> vT = { cv::Point(pts[0]),cv::Point(pts[1]) ,cv::Point(pts[2]) ,cv::Point(pts[3]) };
cv::fillConvexPoly(trackMat, vT, cv::Scalar(255));
#ifdef _DEBUG //<20220314:测试用
tracingID++;
Links.push_back(Link(false, -1, 2, Links.back().iContNxt, chainID, Links.back().iContCt, Links.back().iContNxt, tracingID, trackCenter, vT));
cv::fillConvexPoly(tracingChainMap, vT, cv::Scalar(-1, chainID, Links.back().iContCt));
#endif
//用于显示
cv::circle(cc, trackCenter, 2, cv::Scalar(0, 255, 0, 255), 1);
#ifdef _DEBUG
for (int j = 0; j < 4; j++)
{
cv::line(cc, pts[j], pts[(j + 1) % 4], cv::Scalar(102, 205, 0, 255), 1);
}
#endif
}
}
#endif
}
else {
//计算元件位置
......@@ -2817,6 +2919,12 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
//标记为已追踪过
std::vector<cv::Point> vT = { cv::Point(pts[0]),cv::Point(pts[1]) ,cv::Point(pts[2]) ,cv::Point(pts[3]) };
cv::fillConvexPoly(trackMat, vT, cv::Scalar(255));
#ifdef _DEBUG //<20220314:测试用 标记编号
tracingID++;
Links.push_back(Link(false, 1, 2, Links.back().iContNxt, chainID, Links.back().iContCt, Links.back().iContNxt, tracingID, trackCenter, vT));
cv::fillConvexPoly(tracingChainMap, vT, cv::Scalar(1, chainID, Links.back().iContCt));
#endif
//用于显示
cv::circle(cc, trackCenter, 2, cv::Scalar(0, 255, 0, 255), 1);
#ifdef _DEBUG
......@@ -2836,7 +2944,15 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
trackEnd = (!found);
} while (!trackEnd);
}
#ifdef _DEBUG //<调转方向
for (auto&lk : Links)
{
if (lk.iChainID == chainID) {
lk.iContPrev = tracingID;
break;
}
}
#endif
//逆时针
{
//追踪起点
......@@ -2853,7 +2969,7 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
do
{
bool found = true; bool trayEnd = false;
std::vector<Track> vParts;
std::vector<Track> vParts; cv::Point2f predictCenter;//20220316新增测试用
for (double t = trackAngle - (trackOffset + partDist - trackOffset / 12.0); t > trackAngle - (trackOffset + partDist - trackOffset / 12.0) - trackOffset / 6.0; t -= dMinorStep)
{
cv::Point2f predicPos;
......@@ -2864,6 +2980,8 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
trayEnd = true;
break;
}
//20220316新增测试用
predictCenter = cv::Point2f(predicPos.x, predicPos.y);
//感兴趣区域(向外扩展了一个元件,防止中心定位出现偏差或者料盘本身变形导致的偏差)
cv::Point2f predicBox[4];
calcRotateRect(predicPos, (float)(trackAngle - (trackOffset + partDist)), (float)trackLength*2.0f, (float)trackWidth*2.0f, predicBox);
......@@ -3128,6 +3246,10 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
}
//接着下一个起点
if (trayEnd) {
#ifdef _DEBUG //<修改结束标志
Links.back().iStartOrEnd = 1;
cv::drawMarker(cc, predictCenter, cv::Scalar(0, 0, 255, 255), cv::MARKER_TILTED_CROSS, 10, 1);
#endif
break;
}
//更新位置
......@@ -3144,6 +3266,59 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
if ((trackCenter.x<0 || trackCenter.x>X - 1 ||
trackCenter.y<0 || trackCenter.y>Y - 1) || trackMat.ptr<uint8_t>(cvRound(trackCenter.y))[cvRound(trackCenter.x)] == 255) {
found = false;
#ifdef _DEBUG //<20220315:增加测试用
auto linkInfo = tracingChainMap.ptr<cv::Vec3s>(cvRound(trackCenter.y))[cvRound(trackCenter.x)];
//如果是反方向判断是junction,如果是同方向并且是同一批次只能是偏离,如果不同批次表明中间漏了许多但不能做处理
if (linkInfo[0] == 1) {
//判断为junction
auto lk = Links[linkInfo[2]]; auto bk = Links.back();
Links.back().iContNxt = linkInfo[2]; Links.back().bJunction = true;
}
else {
//满足同方向并且是同批次的,说明一定是偏离
if (chainID == linkInfo[1])
{
//修改结束标志
found = true;
//判断不是junction而是追踪偏离,后续可以在这里增加偏离处理
Links.back().iStartOrEnd = 1;
//判断为偏离,采用理论位置代替
trackCenter = cv::Point2f(predictCenter.x, predictCenter.y);
//更新扫描半径
trackRadius = cv::norm(trackCenter - reelCenter);
//更新扫描角度
trackAngle = atan2((double)trackCenter.y - (double)reelCenter.y, (double)trackCenter.x - (double)reelCenter.x) * 180.0 / PI;
//更新偏移量(元件角度大小)
trackOffset = (2.0 * asin(2.0 * trackLength / (2.0 * trackRadius))) * 180.0 / PI;
//更新元件间角度
partDist = (2.0 * asin(dChordL / (2.0 * trackRadius))) * 180.0 / PI;
//计算元件位置
cv::Point2f pts[4];
calcRotateRect(trackCenter, (float)trackAngle, (float)trackLength, (float)trackWidth, pts);
//标记计数
lbMat.ptr<uint8_t>(cvRound(trackCenter.y))[cvRound(trackCenter.x)] = 255;
//标记为已追踪过
std::vector<cv::Point> vT = { cv::Point(pts[0]),cv::Point(pts[1]) ,cv::Point(pts[2]) ,cv::Point(pts[3]) };
cv::fillConvexPoly(trackMat, vT, cv::Scalar(255));
#ifdef _DEBUG //<20220314:测试用
tracingID++;
Links.push_back(Link(false, -1, 2, Links.back().iContNxt, chainID, Links.back().iContCt, Links.back().iContNxt, tracingID, trackCenter, vT));
cv::fillConvexPoly(tracingChainMap, vT, cv::Scalar(-1, chainID, Links.back().iContCt));
#endif
//用于显示
cv::circle(cc, trackCenter, 2, cv::Scalar(0, 255, 0, 255), 1);
#ifdef _DEBUG
for (int j = 0; j < 4; j++)
{
cv::line(cc, pts[j], pts[(j + 1) % 4], cv::Scalar(102, 205, 0, 255), 1);
}
#endif
}
}
#endif
}
else {
//计算元件位置
......@@ -3154,6 +3329,12 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
//标记为已追踪过
std::vector<cv::Point> vT = { cv::Point(pts[0]),cv::Point(pts[1]) ,cv::Point(pts[2]) ,cv::Point(pts[3]) };
cv::fillConvexPoly(trackMat, vT, cv::Scalar(255));
#ifdef _DEBUG //<20220314:测试用
tracingID++;
Links.push_back(Link(false, -1, 2, Links.back().iContNxt, chainID, Links.back().iContCt, Links.back().iContNxt, tracingID, trackCenter, vT));
cv::fillConvexPoly(tracingChainMap, vT, cv::Scalar(-1, chainID, Links.back().iContCt));
#endif
//用于显示
cv::circle(cc, trackCenter, 2, cv::Scalar(0, 255, 0, 255), 1);
#ifdef _DEBUG
......@@ -3174,6 +3355,70 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
} while (!trackEnd);
}
}
#ifdef _DEBUG //<按顺序整理Links
std::vector<Link> srtLink;
std::vector<std::vector<Link>> srtLinks;
bool found = false; int nxt = -1;
for (auto&lk : Links)
{
//第一步,确定终点(如果有多个终点如何处理)
if (lk.iStartOrEnd == 1 && found == false && lk.iDirect == 1) {
found = true; nxt = lk.iContPrev;
srtLink.push_back(lk);
//第二步,从终点开始向起点排序
bool trackEnd = false;
do
{
auto lks = Links[nxt];
if (lks.iStartOrEnd == 0) {
//到达起点位置
cv::Point st = srtLink.back().ptCenter;
srtLink.push_back(lks);
cv::Point ed = lks.ptCenter;
//获取下一个位置
nxt = lks.iContPrev;
//画图
cv::arrowedLine(cc, st, ed, cv::Scalar(30, 105, 210, 255), 1);
}
else if (lks.iStartOrEnd == 1) {
//画图
cv::Point st = srtLink.back().ptCenter;
srtLink.push_back(lks);
cv::Point ed = lks.ptCenter;
//获取下一个位置
nxt = lks.iContPrev;
//画图
cv::arrowedLine(cc, st, ed, cv::Scalar(30, 105, 210, 255), 1);
//追踪结束
found = false; trackEnd = true;
}
if (lks.iStartOrEnd == 2 && lks.iDirect == 1) {
//到另一个终点,理论情况下到这里已经完全结束
cv::Point st = srtLink.back().ptCenter;
srtLink.push_back(lks);
cv::Point ed = lks.ptCenter;
//获取下一个位置
nxt = lks.iContPrev;
//画图
cv::arrowedLine(cc, st, ed, cv::Scalar(30, 105, 210, 255), 1);
}
if (lks.iStartOrEnd == 2 && lks.iDirect == -1)
{
cv::Point st = srtLink.back().ptCenter;
srtLink.push_back(lks);
cv::Point ed = lks.ptCenter;
//获取下一个位置
nxt = lks.iContNxt;
//画图
cv::arrowedLine(cc, st, ed, cv::Scalar(30, 105, 210, 255), 1);
}
} while (!trackEnd);
break;
}
if (!srtLink.empty()) srtLinks.push_back(srtLink);
}
//如果仅有一条链,判断零星的料距离起点和终点的理论位置是否相近,相近的话判断为料;如果有多条链,判断相邻链之间的位置(多条链生成顺序是不一定的。)
#endif
//计数
binary = lbMat.clone();
//释放资源
......@@ -3301,14 +3546,14 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
//获取最大轮廓
cv::findContours(image, contoursFilter, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
//定位内圈失败一般是仅剩一圈或者不足一圈的情况
if (contoursFilter.size() <= 0)
if (contoursFilter.empty())
{
image = cv::Scalar(0);
//当仅剩一圈的情况
cv::Mat srcPrevEx1;
cv::morphologyEx(binary, srcPrevEx1, cv::MORPH_DILATE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(15, 15)));
cv::morphologyEx(binary, srcPrevEx1, cv::MORPH_DILATE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(31, 31)));
cv::findContours(srcPrevEx1, contoursFilter, cv::RETR_TREE, cv::CHAIN_APPROX_NONE);
//闭合轮廓
......@@ -3329,7 +3574,7 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
return FUNC_CANNOT_CALC;
}
}
std::vector<cv::Point> contourMax = contoursFilter[0];
for (int i = 1; i < contoursFilter.size(); i++)
{
......@@ -3341,6 +3586,9 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
//计算最大外接圆半径
float tFRadius = 0;
cv::minEnclosingCircle(contourMax, cv::Point2f(), tFRadius);
//
tFRadius = tFRadius < 300 ? 300 : tFRadius;
cv::Moments mu = cv::moments(contourMax);
cv::Point2f reelCenter(float(mu.m10 / mu.m00), float(mu.m01 / mu.m00));
//画中心
......@@ -5923,61 +6171,128 @@ int eyemCountObjectIrregularParts(EyemImage tpImage, EyemRect tpRoi, const char
}
//最大值
cv::minMaxLoc(tplMat, NULL, &taMaxGray);
//20220302修改测试用,使用模板匹配增加追踪起点
const float icvDirections[4] = { 0 , 90 , 180 , -90 };
//用以计算元件中心
const int icvDirectionDeltas[4][2] = { { tplMat.cols / 2,tplMat.rows / 2 },{ tplMat.rows / 2,tplMat.cols / 2 },{ tplMat.cols / 2,tplMat.rows / 2 },\
{ tplMat.rows / 2, tplMat.cols / 2 } };
//cv::parallel_for_(cv::Range(0, 4), [&](const cv::Range& range)->void {
for (int tpl = 0/*range.start*/; tpl < 4 /*range.end*/; tpl++)
//20220302修改测试用,使用模板匹配增加追踪起点,20220310,增加至八个方向
//正常四个方向
{
//模板匹配(仅处理料盘区域以提高速度)
cv::Mat tplResult0;
cv::matchTemplate(srcPrev(mainZone).clone(), getTplMat(tplMat, icvDirections[tpl], fillVal), tplResult0, cv::TM_CCOEFF_NORMED);
const float icvDirections[4] = { 0 , 90 , 180 , -90 };
//用以计算元件中心
const int icvDirectionDeltas[4][2] = { { tplMat.cols / 2,tplMat.rows / 2 },{ tplMat.rows / 2,tplMat.cols / 2 },{ tplMat.cols / 2,tplMat.rows / 2 },\
{ tplMat.rows / 2, tplMat.cols / 2 } };
cv::Mat ki = getTplMat(tplMat, icvDirections[tpl], fillVal);
for (int tpl = 0; tpl < 4; tpl++)
{
//模板匹配(仅处理料盘区域以提高速度)
cv::Mat tplResult0;
cv::matchTemplate(srcPrev(mainZone).clone(), getTplMat(tplMat, icvDirections[tpl], fillVal), tplResult0, cv::TM_CCOEFF_NORMED);
//分数大于一定分数才当作元件处理
tplResult0 = cv::Mat(tplResult0 > 0.56);
cv::Mat ki = getTplMat(tplMat, icvDirections[tpl], fillVal);
cv::Point quard[4]{ cv::Point(tplResult0.cols,0),cv::Point(0,0),cv::Point(0,tplResult0.rows),cv::Point(tplResult0.cols,tplResult0.rows) };
//分数大于一定分数才当作元件处理
tplResult0 = cv::Mat(tplResult0 > 0.6);
std::vector<cv::Point> pt = { cv::Point(tplResult0.cols / 2,tplResult0.rows / 2),quard[tpl], quard[(tpl + 1) % 4] };
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<std::vector<cv::Point>> pts;
pts.push_back(pt);
std::vector<cv::Point> pt = { cv::Point(tplResult0.cols / 2,tplResult0.rows / 2),quard[tpl], quard[(tpl + 1) % 4] };
cv::Mat dirMask(tplResult0.size(), CV_8UC1, cv::Scalar(0));
std::vector<std::vector<cv::Point>> pts;
pts.push_back(pt);
cv::drawContours(dirMask, pts, 0, cv::Scalar(255), -1);
cv::Mat dirMask(tplResult0.size(), CV_8UC1, cv::Scalar(0));
//除去非必要部分
cv::bitwise_and(tplResult0, dirMask, tplResult0);
cv::drawContours(dirMask, pts, 0, cv::Scalar(255), -1);
//连通域分析
cv::Mat labels, stats, centroids;
int nccomps = cv::connectedComponentsWithStats(tplResult0, labels, stats, centroids);
//除去非必要部分
cv::bitwise_and(tplResult0, dirMask, tplResult0);
for (int i = 1; i < nccomps; i++) {
cv::Point matchPt(cvRound(centroids.ptr<double>(i)[0]) + icvDirectionDeltas[tpl][0] + mainZone.x, \
cvRound(centroids.ptr<double>(i)[1]) + icvDirectionDeltas[tpl][1] + mainZone.y);
//判断是否位于有效区域
if (srcPrevEx0.ptr<uint8_t>(matchPt.y)[matchPt.x] == 255)
{
//计算初始角度
double _trackAngle_ = atan2((double)matchPt.y - (double)reelCenter.y, (double)matchPt.x - (double)reelCenter.x) * 180.0 / PI;
cv::Point2f pts_t[4];
calcRotateRect(cv::Point2f((float)matchPt.x, (float)matchPt.y), (float)_trackAngle_, (float)tplMat.cols / 2.0f, (float)tplMat.rows / 4.0f, pts_t);
//for (int j = 0; j < 4; j++)
//{
// cv::line(cc, pts_t[j], pts_t[(j + 1) % 4], cv::Scalar(0, 0, 255, 255), 1);
//}
cv::RotatedRect _rbox_(pts_t[0], pts_t[1], pts_t[2]);
tracingAnchors.push_back(TracingAnchor(cv::Point2f((float)matchPt.x, (float)matchPt.y), (float)tplMat.cols, (float)tplMat.rows, _rbox_.size.area(), _rbox_));
//连通域分析
cv::Mat labels, stats, centroids;
int nccomps = cv::connectedComponentsWithStats(tplResult0, labels, stats, centroids);
for (int i = 1; i < nccomps; i++) {
cv::Point matchPt(cvRound(centroids.ptr<double>(i)[0]) + icvDirectionDeltas[tpl][0] + mainZone.x, \
cvRound(centroids.ptr<double>(i)[1]) + icvDirectionDeltas[tpl][1] + mainZone.y);
//判断是否位于有效区域
if (srcPrevEx0.ptr<uint8_t>(matchPt.y)[matchPt.x] == 255)
{
//计算初始角度
double _trackAngle_ = atan2((double)matchPt.y - (double)reelCenter.y, (double)matchPt.x - (double)reelCenter.x) * 180.0 / PI;
cv::Point2f pts_t[4];
calcRotateRect(cv::Point2f((float)matchPt.x, (float)matchPt.y), (float)_trackAngle_, (float)tplMat.cols / 2.0f, (float)tplMat.rows / 4.0f, pts_t);
//for (int j = 0; j < 4; j++)
//{
// cv::line(cc, pts_t[j], pts_t[(j + 1) % 4], cv::Scalar(0, 0, 255, 255), 1);
//}
cv::RotatedRect _rbox_(pts_t[0], pts_t[1], pts_t[2]);
tracingAnchors.push_back(TracingAnchor(cv::Point2f((float)matchPt.x, (float)matchPt.y), (float)tplMat.cols, (float)tplMat.rows, _rbox_.size.area(), _rbox_));
}
}
}
}
//});
//图像旋转后的四个方向
{
const float icvDirections[4] = { 0 , 90 , 180 , -90 };
//用以计算元件中心
const int icvDirectionDeltas[4][2] = { { tplMat.cols / 2,tplMat.rows / 2 },{ tplMat.rows / 2,tplMat.cols / 2 },{ tplMat.cols / 2,tplMat.rows / 2 },\
{ tplMat.rows / 2, tplMat.cols / 2 } };
float matxv[4];
cv::Mat ttmp = getTrackMat(srcPrev(mainZone).clone(), 45.0, 0, matxv);
for (int tpl = 0; tpl < 4; tpl++)
{
//模板匹配(仅处理料盘区域以提高速度)
cv::Mat tplResult0;
cv::matchTemplate(ttmp, getTplMat(tplMat, icvDirections[tpl], fillVal), tplResult0, cv::TM_CCOEFF_NORMED);
cv::Mat ki = getTplMat(tplMat, icvDirections[tpl], fillVal);
//分数大于一定分数才当作元件处理
tplResult0 = cv::Mat(tplResult0 > 0.6);
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<std::vector<cv::Point>> pts;
pts.push_back(pt);
cv::Mat dirMask(tplResult0.size(), CV_8UC1, cv::Scalar(0));
cv::drawContours(dirMask, pts, 0, cv::Scalar(255), -1);
//除去非必要部分
cv::bitwise_and(tplResult0, dirMask, tplResult0);
//连通域分析
cv::Mat labels, stats, centroids;
int nccomps = cv::connectedComponentsWithStats(tplResult0, labels, stats, centroids);
for (int i = 1; i < nccomps; i++) {
cv::Point matchPt(cvRound(centroids.ptr<double>(i)[0]) + icvDirectionDeltas[tpl][0], \
cvRound(centroids.ptr<double>(i)[1]) + icvDirectionDeltas[tpl][1]);
//计算旋转前的位置
float realX1 = 0.0f, realY1 = 0.0f;
realX1 = (float)mainZone.tl().x + ((matchPt.x - matxv[2])*matxv[4] - (matchPt.y - matxv[5])*matxv[1]) / (matxv[0] * matxv[4] - matxv[3] * matxv[1]);
realY1 = (float)mainZone.tl().y + ((matchPt.x - matxv[2])*matxv[3] - (matchPt.y - matxv[5])*matxv[0]) / (matxv[1] * matxv[3] - matxv[4] * matxv[0]);
//判断是否位于有效区域
if (srcPrevEx0.ptr<uint8_t>(cvRound(realY1))[cvRound(realX1)] == 255)
{
//计算初始角度
double _trackAngle_ = atan2((double)realY1 - (double)reelCenter.y, (double)realX1 - (double)reelCenter.x) * 180.0 / PI;
cv::Point2f pts_t[4];
calcRotateRect(cv::Point2f((float)realX1, (float)realY1), (float)_trackAngle_, (float)tplMat.cols / 2.0f, (float)tplMat.rows / 4.0f, pts_t);
//for (int j = 0; j < 4; j++)
//{
// cv::line(cc, pts_t[j], pts_t[(j + 1) % 4], cv::Scalar(0, 0, 255, 255), 1);
//}
cv::RotatedRect _rbox_(pts_t[0], pts_t[1], pts_t[2]);
tracingAnchors.push_back(TracingAnchor(cv::Point2f((float)realX1, (float)realY1), (float)tplMat.cols, (float)tplMat.rows, _rbox_.size.area(), _rbox_));
}
}
}
}
}
//标记为已追踪过
std::vector<cv::Point> vT = { cv::Point(_pts[0]),cv::Point(_pts[1]) ,cv::Point(_pts[2]) ,cv::Point(_pts[3]) };
......@@ -7099,7 +7414,7 @@ int eyemCountObjectE(EyemImage tpImage, EyemRect tpRoi, const char *fileName, in
trays.push_back(TrayPos(reelCenter, tray, false, backThresh));
}
//判断可能无料,不能100%判断
if (trays.empty()) {
if (trays.empty()) {//在这里empty的话只能 是单盘,没事,除非四盘都不满一圈。。。
//在这里增加继续判断是否是因为不满一圈的料,可能无法避免空料盘与空点问题,佳世达特供(无法用于多料盘而且佳世达一般多为小料没有那么多种类)
{
cv::Mat image8U;
......@@ -7378,7 +7693,7 @@ int eyemCountObjectE(EyemImage tpImage, EyemRect tpRoi, const char *fileName, in
//判断适用哪种算法
if (!useTrackMethod)
{
const int filterSize = 12;
const int filterSize = 15;
//去掉料盘深色部分干扰
const int winSize = sinPartSize > 15 ? 5 : 3;//对于部分器件过小的窗口会漏料
cv::Mat srcPrevEx;
......@@ -7424,8 +7739,10 @@ int eyemCountObjectE(EyemImage tpImage, EyemRect tpRoi, const char *fileName, in
// continue;
//}
//计算轮廓矩
cv::Moments mu = cv::moments(contourMax);
cv::Point2f reelCenter(float(mu.m10 / mu.m00), float(mu.m01 / mu.m00));
//计算最大外接圆半径
float tFRadius = 0;
cv::minEnclosingCircle(contourMax, cv::Point2f(), tFRadius);
......@@ -7611,7 +7928,7 @@ int eyemCountObjectE(EyemImage tpImage, EyemRect tpRoi, const char *fileName, in
image = cv::Scalar(0);
//当仅剩一圈的情况
cv::Mat srcPrevEx1;
cv::morphologyEx(binary, srcPrevEx1, cv::MORPH_DILATE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(15, 15)));
cv::morphologyEx(binary, srcPrevEx1, cv::MORPH_DILATE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(31, 31)));
cv::findContours(srcPrevEx1, contoursFilter, cv::RETR_TREE, cv::CHAIN_APPROX_NONE);
//闭合轮廓
......@@ -9593,7 +9910,6 @@ int eyemCountObjectIrregularPartsMultiopt(EyemImage tpImage, EyemRect tpRoi, int
cv::minMaxIdx(hist, NULL, NULL, NULL, maxIdx);
//背景阈值
int backThresh = (maxIdx[0] - 1) * (65536 / histSize);
//制作掩膜
cv::Mat mask(Y, X, CV_8UC1, cv::Scalar(0));
cv::parallel_for_(cv::Range(0, Y), [&](const cv::Range range)->void {
......@@ -9605,20 +9921,40 @@ int eyemCountObjectIrregularPartsMultiopt(EyemImage tpImage, EyemRect tpRoi, int
}
}
});
//去掉干扰(可有可无)
cv::morphologyEx(mask, mask, cv::MORPH_OPEN, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(9, 9)));
//制作背景
cv::Mat mean, stddev;
cv::meanStdDev(src, mean, stddev, mask);
//像素重新排列
std::vector<uint16_t> data;
for (int y = 0; y < Y; y++)
{
for (int x = 0; x < X; x++) {
if (mask.ptr<uint8_t>(y)[x] == 255) {
data.push_back(src.ptr<uint16_t>(y)[x]);
}
}
}
//随机打乱(让背景尽量显得自然一点)
std::random_shuffle(data.begin(), data.end());
cv::Mat rnd(Y, X, CV_16UC1);
cv::randn(rnd, mean, stddev);
//拷贝指定部分
const int dataSize = cvFloor(sqrt((int)data.size()));
std::vector<uint16_t> pixVal(dataSize*dataSize);
memcpy(&pixVal[0], &data[0], dataSize*dataSize*sizeof(uint16_t));
//制作背景
cv::Mat rnd;
rnd = cv::Mat(dataSize, dataSize, CV_16UC1, &pixVal[0]);
//扩充到原尺寸
cv::resize(rnd, rnd, cv::Size(X, Y));
//算法选项
std::string sOptions[8] = { "IP_DEFAULT_PARTS","IP_SMALL_PARTS","IP_LARGE_PARTS","IP_LONG_PARTS","IP_SQUARE_PARTS","","IP_DYNAMIC_SP1","IP_DYNAMIC_SP2" };
//将各个料盘分别拷贝到背景里
std::vector<cv::Rect> q = { cv::Rect(0, 0, X / 2, Y / 2),cv::Rect(X / 2, 0, X / 2, Y / 2)
,cv::Rect(0, Y / 2, X / 2, Y / 2),cv::Rect(X / 2, Y / 2, X / 2, Y / 2) };
std::vector<cv::Rect> q = { cv::Rect(0, 0, X / 2, Y / 2),cv::Rect(0, Y / 2, X / 2, Y / 2)
,cv::Rect(X / 2, Y / 2, X / 2, Y / 2),cv::Rect(X / 2, 0, X / 2, Y / 2) };
std::vector<EyemImage> inputs;
for (auto&rect : q)
......@@ -9641,7 +9977,7 @@ int eyemCountObjectIrregularPartsMultiopt(EyemImage tpImage, EyemRect tpRoi, int
}
//用于显示
cv::Mat cc(Y + 1, X + 1, CV_8UC4, cv::Scalar(127, 116, 102, 255));
//点料(左上、右上、左下、右下)
//点料(左上、左下、右下、右上)
#define OPTIMIZE true
#if OPTIMIZE
cv::parallel_for_(cv::Range(0, 4), [&](const cv::Range range)->void {
......@@ -9664,6 +10000,7 @@ int eyemCountObjectIrregularPartsMultiopt(EyemImage tpImage, EyemRect tpRoi, int
if (ipReelNum_[j] != 0)
{
ipReelNum[i] = ipReelNum_[j];
break;
}
}
//拼接图像
......@@ -9756,10 +10093,10 @@ int eyemCountObjectIrregularPartsMultiopt(EyemImage tpImage, EyemRect tpRoi, int
int baseLine;
cv::Size labelSize = cv::getTextSize("No Reel", cv::FONT_HERSHEY_SIMPLEX, 2.5, 2, &baseLine);
cv::putText(cc, "No Reel", cv::Point(reelPt.x - labelSize.width / 2, reelPt.y - labelSize.height), cv::FONT_HERSHEY_SIMPLEX, 2.5, cv::Scalar(0, 0, 238, 255), 2);
}
}
//画网格
cv::rectangle(cc, cv::Rect(q[i].x, q[i].y, q[i].width, q[i].height), cv::Scalar(0, 0, 255, 255), 4);
}
}
#endif
//释放图像
for (auto&input : inputs)
......@@ -9778,7 +10115,7 @@ int eyemCountObjectIrregularPartsMultiopt(EyemImage tpImage, EyemRect tpRoi, int
//拷贝数据
memcpy(tpDstImg->vpImage, cc.data, _Size);
return FUNC_OK;
}
}
int eyemAchvMatchMat(EyemImage tpImage, EyemRect tpRoi, EyemImage *tpDstImg)
{
......@@ -12932,7 +13269,7 @@ int eyemLibImpl(EyemImage tpImage, EyemImage *tpDstImg)
#pragma endregion
sw.Stop();
std::cout << "TimeSpan:" << sw.ElapsedMilliseconds() << std::endl;
std::cout << "时间花费:" << sw.ElapsedMilliseconds() << std::endl;
////<输出结果图像
//tpDstImg->iWidth = cc.cols; tpDstImg->iHeight = cc.rows; tpDstImg->iDepth = cc.depth(); tpDstImg->iChannels = cc.channels();
////内存尺寸
......
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!