Commit 88e2a6de 张士柳

1 个父辈 1352ebcf
......@@ -596,7 +596,7 @@ namespace eyemLib_Sharp
//eyemCountObjectIrregularParts(ucpImage, file.Replace(".png", ""), 0d, "IP_LONG_PARTS", 100, 5, ref pNumObj, out tpDstImg);
int ipNum; EyemBarCode* tpResults;
DataCodeHandle hObject;
int iRes = eyemDetectAndDecode(ucpImage, tpRoi, file.Replace(".png", ""), "QR_CODE|DATA_MATRIX|CODE_128|CODE_39", out hObject, out tpResults, out ipNum, false, 11, 5, 128, 215, 1d);
int iRes = eyemDetectAndDecode(ucpImage, tpRoi, file.Replace(".png", ""), "QR_CODE|DATA_MATRIX|CODE_128|CODE_39", out hObject, out tpResults, out ipNum, false, 21, 5, 128, 215, 1d);
for (int i = 0; i < ipNum; i++)
{
Console.WriteLine("类型:" + Marshal.PtrToStringAnsi(tpResults[i].hType) + ";坐标" + "[" + tpResults[i].iCenterX.ToString() + "," + tpResults[i].iCenterY.ToString() + "]" + ";角度:" + tpResults[i].dAngle.ToString("F4") + "," + ";内容:" + Marshal.PtrToStringAnsi(tpResults[i].hText) + "");
......@@ -658,7 +658,7 @@ namespace eyemLib_Sharp
if (tpPoints == null)
throw new ArgumentNullException("cvPoint2D32f");
//分配结构体需要的内存
IntPtr memory = (IntPtr)Marshal.AllocHGlobal(checked(Marshal.SizeOf(typeof(EyemOcsDXY)) * iLength));
IntPtr memory = Marshal.AllocHGlobal(checked(Marshal.SizeOf(typeof(EyemOcsDXY)) * iLength));
for (int index = 0; index < iLength; index++)
{
Marshal.StructureToPtr(tpPoints[index], (IntPtr)(checked((long)memory + index * Marshal.SizeOf(typeof(EyemOcsDXY)))), false);
......
......@@ -560,6 +560,7 @@ int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileNam
//用于显示
cv::Mat showMat;
cv::cvtColor(src, showMat, cv::COLOR_GRAY2BGR);
//
//图像尺寸
int X = src.cols, Y = src.rows;
//解码结果
......@@ -583,31 +584,9 @@ int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileNam
cv::threshold(srcPrev, binary, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
//膨胀
cv::morphologyEx(binary, binary, cv::MORPH_DILATE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(cvRound(iBlockSize*dScaleUpAndDown / 3.), cvRound(iBlockSize*dScaleUpAndDown / 3.))));
//图像增强
//double min, max;
//cv::minMaxLoc(src, &min, &max, NULL, NULL, binary);
////条码测试用
//cv::Mat xDer, yDer;
//cv::normalize(dx, dx, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat());
//cv::convertScaleAbs(dx, xDer);
//cv::normalize(dy, dy, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat());
//cv::convertScaleAbs(dy, yDer);
cv::Mat mphyEx;
cv::morphologyEx(src, mphyEx, cv::MORPH_BLACKHAT, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(cvRound(iBlockSize*dScaleUpAndDown) * 2 + 1, cvRound(iBlockSize*dScaleUpAndDown) * 2 + 1)));
//确定背景
//cv::Mat binary4;
//cv::threshold(mphyEx, binary4, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
//double min, max;
//cv::minMaxLoc(src, &min, &max, NULL, NULL, mphyEx);
//cv::Mat background(Y, X, CV_8UC1, max);
//src.copyTo(background, binary4);
//计算角点响应
cv::Mat harMap;
cv::cornerHarris(src, harMap, cvRound(iBlockSize*dScaleUpAndDown), 3, 0.04);//对二维码效果比较好
cv::cornerHarris(src, harMap, cvRound(iBlockSize*dScaleUpAndDown), 3, 0.04);
// 归一化与转换
cv::normalize(harMap, harMap, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat());
cv::convertScaleAbs(harMap, harMap);
......@@ -639,13 +618,6 @@ int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileNam
}
}
});
//测试用
return FUNC_OK;
//确定识别类型
std::vector<std::string> hints_;
split(ccCodeType, "|", hints_);
......@@ -661,9 +633,6 @@ int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileNam
std::find(hints_.begin(), hints_.end(), "ITF") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "RSS_14") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "RSS_EXPANDED") != hints_.end();
//是否添加二维码检测
bool addTwoDReader = std::find(hints_.begin(), hints_.end(), "QR_CODE") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "DATA_MATRIX") != hints_.end() ||
......@@ -671,7 +640,6 @@ int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileNam
//未设置识别类型
if (!addOneDReader && !addTwoDReader)
return FUNC_CANNOT_CALC;
//所有解码内容
std::vector<DecodeResult> decodeResults;
//待解码区域,区分条码类型来识别
......@@ -835,14 +803,14 @@ int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileNam
//存储一维码待解码区域
if ((int)oneDMats.size() > 0)
{
//画图
for (int j = 0; j < 4; j++)
{
cv::line(showMat, pt[j], pt[(j + 1) % 4], cv::Scalar(0, 255, 255), 1);
}
cv::circle(showMat, pt[0], 2, cv::Scalar(255, 0, 0), -1);
cv::circle(showMat, pt[1], 2, cv::Scalar(0, 255, 0), -1);
cv::circle(showMat, pt[2], 2, cv::Scalar(0, 0, 255), -1);
////画图
//for (int j = 0; j < 4; j++)
//{
// cv::line(showMat, pt[j], pt[(j + 1) % 4], cv::Scalar(0, 255, 255), 1);
//}
//cv::circle(showMat, pt[0], 2, cv::Scalar(255, 0, 0), -1);
//cv::circle(showMat, pt[1], 2, cv::Scalar(0, 255, 0), -1);
//cv::circle(showMat, pt[2], 2, cv::Scalar(0, 0, 255), -1);
waitAreas.push_back(WaitArea(cv::Mat(), ptMid, getThreshVal_Otsu_8u(oneDMats[0]), _angle*180. / PI, true, oneDMats));
}
}
......@@ -904,47 +872,12 @@ int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileNam
int dynSize = cvRound(cv::max((double)rect.size().height / dScaleUpAndDown, (double)rect.size().width / dScaleUpAndDown));
//疑似二维码区域
cv::Mat waitArea = backup(cv::Range(cv::max(0, cvRound(rRect.center.y / dScaleUpAndDown) - cvRound(4.*(double)iBlockSize*dScaleUpAndDown + dynSize / 2)), cv::min(backup.rows - 1, cvRound(rRect.center.y / dScaleUpAndDown) + cvRound(4.*(double)iBlockSize*dScaleUpAndDown + dynSize / 2))), cv::Range(cv::max(0, cvRound(rRect.center.x / dScaleUpAndDown) - cvRound(4.*(double)iBlockSize*dScaleUpAndDown + dynSize / 2)), cv::min(backup.cols - 1, cvRound(rRect.center.x / dScaleUpAndDown) + cvRound(4.*(double)iBlockSize*dScaleUpAndDown + dynSize / 2)))).clone();
////获取起始阈值
//double threshVal = getThreshVal_Otsu_8u(waitArea);
////去掉背景
//cv::parallel_for_(cv::Range(0, waitArea.rows), [&](const cv::Range range)->void {
// for (int y = range.start; y < range.end; y++)
// {
// for (int x = 0; x < waitArea.cols; x++)
// {
// if (waitArea.ptr<uint8_t>(y)[x] > threshVal + 7)
// {
// waitArea.ptr<uint8_t>(y)[x] = cvRound(threshVal);
// }
// }
// }
//});
////处理后再压入识别
//cv::Mat locHarMap;
//cv::cornerHarris(waitArea, locHarMap, cvRound(iBlockSize*dScaleUpAndDown), 3, 0.04);
//// 归一化与转换
//cv::normalize(locHarMap, locHarMap, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat());
//cv::convertScaleAbs(locHarMap, locHarMap);
////二值化
//cv::Mat binary;
//cv::threshold(locHarMap, binary, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
//处理后再压入识别
waitAreas.push_back(WaitArea(waitArea, cv::Point(cvRound(rRect.center.x / dScaleUpAndDown), cvRound(rRect.center.y / dScaleUpAndDown)), 0, 0, false, std::vector<cv::Mat>()));
//画图
cv::rectangle(showMat, rect, cv::Scalar(0, 255, 0), 1);
}
////绘制图形
//for (int j = 0; j < contourFilter.size(); j++)
//{
// cv::Point2f pts[4];
// cv::minAreaRect(contourFilter[j]).points(pts);
// //画透明蒙版
// std::vector<cv::Point> vT = { cv::Point(pts[0]),cv::Point(pts[1]) ,cv::Point(pts[2]) ,cv::Point(pts[3]) };
// cv::fillConvexPoly(showMat, vT, cv::Scalar(0, 255, 255));
//}
}
//cv::Mat dst;
//showMat.copyTo(dst);
//解码
decodeMul(waitAreas, hints_, showMat, decodeResults, iBlockSize, iRangeC, dMinorStep);
//输出结果
......@@ -978,220 +911,15 @@ int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileNam
*hResults = tpResults->data();
*ipNum = static_cast<int>(tpResults->size());
*hObject = reinterpret_cast<IntPtr>(tpResults);
//cv::addWeighted(dst, 0.7, showMat, 0.3, 0, dst);
//showMat = dst;
//格式化文件名
const int bufSize = 32;
char file[bufSize * 4] = { 0 };
sprintf_s(file, "D:\\ResOut\\%s-Mark.png", ccFileName);
cv::imwrite(file, showMat);
return FUNC_OK;
////局部二值化
//cv::adaptiveThreshold(src, binary, 255, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY_INV, iBlockSize, 2);
////去掉大部分干扰项
//binary &= mask;
////对二值图像过滤
//cv::Mat labels, stats, centroids;
//int nccomps = cv::connectedComponentsWithStats(binary, labels, stats, centroids, 4);
////过滤连通域面积及长/宽比例不符合的,允许50%误差
//std::vector<uchar> colors(nccomps + 1, 0);
//for (int i = 1; i < nccomps; i++) {
// colors[i] = 255;
// if ((stats.ptr<int>(i)[cv::CC_STAT_WIDTH] > iBlockSize * 15 * 1.414*(1. + dToleErr)) | (stats.ptr<int>(i)[cv::CC_STAT_HEIGHT] > iBlockSize * 15 * 1.414*(1. + dToleErr))\
// | (false))
// {
// colors[i] = 0;
// }
//}
////第一次过滤
//cv::parallel_for_(cv::Range(0, Y), [&](const cv::Range& range)->void {
// for (int y = range.start; y < range.end; y++)
// {
// uint8_t *ptrRow = binary.ptr<uint8_t>(y);
// for (int x = 0; x < X; x++)
// {
// int label = labels.ptr<int>(y)[x];
// CV_Assert(0 <= label && label <= nccomps);
// ptrRow[x] = colors[label];
// }
// }
//});
////cv::cvtColor(binary, showMat, cv::COLOR_GRAY2BGR);
//const int iScanRadius = 35;
////最好还是用线扫描的方法,具体扫描全图还是按照中心点来扫看时间
////指定长度十字网格遍历是否满足条件黑白比例在1:1,考虑其他方式去扫描,可能速度上会慢一些,如果区分满足各自条件可能会好一些
////,这样可以将其他不必要的过滤掉,最后再合成一张图;2,扫描宽度,根据宽度流来确定是否属于条码、qr、datamatrix,黑白宽度;
////宽度打开都在一个很小变动范围内,允许50%的误差,宽度密度相较目前判断方式可也确定是否是黑白格分布,考虑到一维条码,八个方向只需满足一个方向即可
////这样可以过滤掉一些孤立长条
////vPts[0].Pt = cv::Point(3610, 2433);
//cv::Mat label(Y, X, CV_8UC1, cv::Scalar(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++)
// {
// uint8_t future_pixel = binary.ptr<uint8_t>(y)[x];
// if (!future_pixel)
// {
// continue;
// }
// bool iFlag = 0;
// //判断白色像素部分占整条线的比例
// for (double t = -180; t < 180; t += 45)
// {
// float xx = float(x + iScanRadius * cos(t* 0.01745));
// float yy = float(y + iScanRadius * sin(t* 0.01745));
// //防止越界
// if (xx < 0) xx = 0; if (xx >= X - 1) xx = X - 1; if (yy < 0) yy = 0; if (yy >= Y - 1) yy = Y - 1;
// cv::LineIterator it(binary, cv::Point(x, y), cv::Point(cvRound(xx), cvRound(yy)), 4);
// //扫描像素密度,比例接近1:1记录下来
// int length = 0;
// std::vector<double> test_lines;
// double test_line[2]{ 0 };
// for (int n = 0; n < it.count; n++, ++it)
// {
// //统计相邻由明到暗个数,并且查看均匀性
// uint8_t next_pixel = binary.ptr<uint8_t>(it.pos().y)[it.pos().x];
// test_line[next_pixel % 254]++;
// length++;
// if (next_pixel != future_pixel)
// {
// if (!next_pixel)
// {
// test_lines.push_back(length);
// }
// future_pixel = 255 - future_pixel;
// length = 0;
// }
// }
// if (cv::max(test_line[0], test_line[1]) <= 0) continue;
// //至少存在l个方向满足黑白1:1比例,并且满足黑白交替比例大概在1:1
// double dRate = cv::min(test_line[0], test_line[1]) / cv::max(test_line[0], test_line[1]);
// if (dRate >= (1. - dToleErr) && dRate <= (1. + dToleErr))
// {
// //满足条件,再判断当前方向的宽度是否
// iFlag = true;
// //cv::putText(showMat, "OK", vPts[c].Pt, cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar(0, 0, 255));
// //showMat.at<cv::Vec3b>(cv::Point(x, y)) = cv::Vec3b(0, 255, 0);
// label.ptr<uint8_t>(y)[x] = 255;
// break;
// }
// }
// }
// }
//});
//for (int c = 0; c < (int)vPts.size(); c++)
//{
// bool iFlag = 0;
// uint8_t future_pixel = binary.ptr<uint8_t>(vPts[c].Pt.y)[vPts[c].Pt.x];
// //判断白色像素部分占整条线的比例
// for (double t = -180; t < 180; t += 45)
// {
// float x = float(vPts[c].Pt.x + iScanRadius * cos(t* 0.01745));
// float y = float(vPts[c].Pt.y + iScanRadius * sin(t* 0.01745));
// //防止越界
// if (x < 0) x = 0; if (x >= X - 1) x = X - 1; if (y < 0) y = 0; if (y >= Y - 1) y = Y - 1;
// cv::LineIterator it(binary, vPts[c].Pt, cv::Point(cvRound(x), cvRound(y)), 4);
// //扫描像素密度,比例接近1:1记录下来
// //测试用
// std::vector<cv::Point> test_point;
// int length = 0;
// std::vector<double> test_lines;
// double test_line[2]{ 0 };
// for (int n = 0; n < it.count; n++, ++it)
// {
// //统计相邻由明到暗个数,并且查看均匀性
// uint8_t next_pixel = binary.ptr<uint8_t>(it.pos().y)[it.pos().x];
// test_line[next_pixel % 254]++;
// length++;
// if (next_pixel != future_pixel)
// {
// if (length > 1)
// {
// test_lines.push_back(length);
// }
// future_pixel = 255 - future_pixel;
// length = 0;
// }
// //test_point.push_back(it.pos());
// //showMat.at<cv::Vec3b>(it.pos()) = cv::Vec3b(0, 255, 0);
// }
// //至少存在l个方向满足黑白1:1比例,并且满足黑白交替比例大概在1:1
// double dRate = cv::min(test_line[0], test_line[1]) / cv::max(test_line[0], test_line[1]);
// if (dRate >= (1. - dToleErr) && dRate <= (1. + dToleErr) && (test_lines.size() >= T))
// {
// //满足条件,再判断当前方向的宽度是否
// iFlag = true;
// //cv::putText(showMat, "OK", vPts[c].Pt, cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar(0, 0, 255));
// //for (int n = 0; n < test_point.size(); n++)
// //{
// // showMat.at<cv::Vec3b>(test_point[n]) = cv::Vec3b(0, 0, 255);
// //}
// //std::cout << "xx" << std::endl;
// }
// }
// //对四个方向进行进一步进行过滤,黑白间隔跨度阈值限定
// if ((!iFlag))
// {
// colors[vPts[c].Label] = 0;
// }
//}
//二次过滤
//cv::parallel_for_(cv::Range(0, Y), [&](const cv::Range& range)->void {
// for (int y = range.start; y < range.end; y++)
// {
// uint8_t *ptrRow = binary.ptr<uint8_t>(y);
// for (int x = 0; x < X; x++)
// {
// int label = labels.ptr<int>(y)[x];
// CV_Assert(0 <= label && label <= nccomps);
// ptrRow[x] = colors[label];
// }
// }
//});
//cv::Mat binPrev;
//cv::morphologyEx(label, binPrev, cv::MORPH_CLOSE, cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(iBlockSize, iBlockSize)));
////用于轮廓检测(最终过滤过的图)
//std::vector<cv::Vec4i> hierarchy;
//std::vector<std::vector<cv::Point>> contourAll, contourFilter;
//findContours(binPrev, contourAll, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
//for (int i = 0; i < static_cast<int>(contourAll.size()); i++)
//{
// cv::RotatedRect rect = cv::minAreaRect(contourAll[i]);
// double dRate = rect.size.width / rect.size.height;
// std::vector<cv::Point> approx;
// cv::approxPolyDP(cv::Mat(contourAll[i]), approx, cv::arcLength(cv::Mat(contourAll[i]), true)*0.02, true);
// //满足四边形条件
// if (dRate >= (1. - dToleErr) && dRate <= (1. + dToleErr) && (approx.size() >= 4 && approx.size() < 8) && (cv::contourArea(contourAll[i]) > std::pow(iBlockSize * 12 * (1. - dToleErr), 2)))
// {
// contourFilter.push_back(contourAll[i]);
// }
//}
//if (contourFilter.size() < 1)
//{
// return FUNC_CANNOT_CALC;
//}
//const int bufSize = 32;
//char file[bufSize * 4] = { 0 };
//sprintf_s(file, "D:\\ResOut\\%s-Mark.png", ccFileName);
//cv::imwrite(file, showMat);
return FUNC_OK;
}
int eyemCalcDetectParameter(EyemImage tpImage, EyemRect tpRoi, const char *ccFileName, int *ipNum, int iBlockSize, const int iRangeC, int *iSymbolMin, int *iSymbolMax, double dScaleUpAndDown, double dMinorStep)
int eyemCalcDetectParameter(EyemImage tpImage, EyemRect tpRoi, const char *ccFileName, bool bTrainOneD, int iBlockSize, int *ipNum, int *iSymbolMin, int *iSymbolMax)
{
cv::Mat src = cv::Mat(tpImage.iHeight, tpImage.iWidth, tpImage.iDepth, tpImage.vpImage);
if (src.empty()) {
......@@ -1199,9 +927,6 @@ int eyemCalcDetectParameter(EyemImage tpImage, EyemRect tpRoi, const char *ccFil
}
//提取ROI
src = src(cv::Rect(tpRoi.iXs, tpRoi.iYs, tpRoi.iWidth, tpRoi.iHeight));
//降采样
if (dScaleUpAndDown != 1.)
cv::pyrDown(src, src, cv::Size(cvRound(src.cols*dScaleUpAndDown), cvRound(src.rows*dScaleUpAndDown)));
//用于显示
cv::Mat showMat;
cv::cvtColor(src, showMat, cv::COLOR_GRAY2BGR);
......@@ -1209,8 +934,296 @@ int eyemCalcDetectParameter(EyemImage tpImage, EyemRect tpRoi, const char *ccFil
int X = src.cols, Y = src.rows;
//高斯滤波去噪
cv::Mat srcPrev, binary;
int ksize = cvRound((iBlockSize + 1)*dScaleUpAndDown) % 2 == 0 ? cvRound((iBlockSize + 1)*dScaleUpAndDown) + 1 : cvRound((iBlockSize + 1)*dScaleUpAndDown);
cv::GaussianBlur(src, srcPrev, cv::Size(ksize, ksize), 0.3);
cv::GaussianBlur(src, srcPrev, cv::Size(iBlockSize, iBlockSize), 0.3);
//计算角点响应
cv::Mat harMap;
cv::cornerHarris(src, harMap, iBlockSize, 3, 0.04);
// 归一化与转换
cv::normalize(harMap, harMap, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat());
cv::convertScaleAbs(harMap, harMap);
//计算背景像素
const int histSize = 256;
float range[] = { 0,255 };
const float* histRange = { range };
//calculate the histogram
cv::Mat hist;
cv::calcHist(&harMap, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange);
//calculate the background pixels
int maxIdx[2] = { 255,255 };
cv::minMaxIdx(hist, NULL, NULL, NULL, maxIdx);
//m1用于检测一维码;m2用于检测二维码
cv::Mat m1(Y, X, CV_8UC1, cv::Scalar(0)), m2(Y, X, CV_8UC1, cv::Scalar(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 (harMap.ptr<uint8_t>(y)[x] < maxIdx[0])
{
m1.ptr<uint8_t>(y)[x] = 255;
}
else if (harMap.ptr<uint8_t>(y)[x] > maxIdx[0])
{
m2.ptr<uint8_t>(y)[x] = 255;
}
}
}
});
//允许误差
const double dToleErr = 0.35;
//所有解码内容
std::vector<DecodeResult> decodeResults;
//待解码区域,区分条码类型来识别
std::vector<WaitArea> waitAreas;
//是否是计算一维码参数
if (bTrainOneD)
{
//对于一维码如何确定参数,暂时按照能识别到的个数来判断
cv::Mat labels, stats, centroids;
int nccomps = cv::connectedComponentsWithStats(m1, labels, stats, centroids, 4);
//过滤连通域面积及长/宽比例不符合的,允许50%误差
std::vector<uchar> colors(nccomps + 1, 0);
for (int i = 1; i < nccomps; i++) {
colors[i] = 255;
if ((stats.ptr<int>(i)[cv::CC_STAT_AREA] < 20) | (m1.ptr<uint8_t>(cvRound(centroids.ptr<double>(i)[1]))[cvRound(centroids.ptr<double>(i)[0])] == 0))
{
colors[i] = 0;
}
}
//过滤
cv::parallel_for_(cv::Range(0, Y), [&](const cv::Range& range)->void {
for (int y = range.start; y < range.end; y++)
{
uint8_t *ptrRow = m1.ptr<uint8_t>(y);
for (int x = 0; x < X; x++)
{
int label = labels.ptr<int>(y)[x];
CV_Assert(0 <= label && label <= nccomps);
ptrRow[x] = colors[label];
}
}
});
//用于过滤非条码部分
cv::Mat binFilter;
cv::adaptiveThreshold(src, binFilter, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY_INV, iBlockSize, 5);
//处理断裂一维码
cv::morphologyEx(m1, m1, cv::MORPH_CLOSE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(iBlockSize, iBlockSize)));
//用于轮廓检测
std::vector<std::vector<cv::Point>> contourAll, contourFilter;
findContours(m1, contourAll, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
for (int i = 0; i < static_cast<int>(contourAll.size()); i++)
{
cv::RotatedRect rect = cv::minAreaRect(contourAll[i]);
//最大宽度限制
double minLen = cv::min(rect.size.height, rect.size.width);
if (minLen < 8.*iBlockSize*(1. + dToleErr))
{
//增加比例过滤条件
cv::Point2f pts[4];
rect.points(pts);
//起点、终点、中点
cv::Point ptStart, ptEnd, ptMid;
if (cv::norm(pts[0] - pts[1]) > cv::norm(pts[1] - pts[2]))
{
ptStart = cv::Point((pts[0] + pts[3]) / 2.); ptEnd = cv::Point((pts[1] + pts[2]) / 2.);
}
else
{
ptStart = cv::Point((pts[0] + pts[1]) / 2.); ptEnd = cv::Point((pts[2] + pts[3]) / 2.);
}
ptMid = (ptStart + ptEnd) / 2;
cv::LineIterator it(binFilter, (ptMid + ptEnd) / 2, (ptMid + ptStart) / 2, 4);
double dis = cv::norm((ptMid + ptEnd) / 2 - (ptMid + ptStart) / 2);
uint8_t future_pixel = 255;
//扫描像素密度,比例接近1:1记录下来,并且黑白间隔数目小大于长度的一半
int flag = 0;
double test_line[2]{ 0 };
for (int n = 0; n < it.count; n++, ++it)
{
if (m1.ptr<uint8_t>(cvRound(it.pos().y))[cvRound(it.pos().x)] == 0) continue;
//统计均匀性
uint8_t next_pixel = binFilter.ptr<uint8_t>(it.pos().y)[it.pos().x];
test_line[next_pixel % 254]++;
if (next_pixel != future_pixel)
{
flag++;
future_pixel = 255 - future_pixel;
}
}
//满足比例
double dRate = cv::min(test_line[0], test_line[1]) / cv::max(test_line[0], test_line[1]);
if (dRate >= (1. - dToleErr) && dRate <= (1. + dToleErr) && flag > cvRound((dis / 4.)*(1. - dToleErr)))
{
cv::Point2f pt[4];
cv::Size size(cvRound(cv::max(rect.size.height, rect.size.width) + iBlockSize / 4.), cvRound(cv::min(rect.size.height, rect.size.width)));
//获取roi位置
double _angle = std::atan2((ptEnd.y - ptStart.y), (ptEnd.x - ptStart.x));
float b = (float)cos(_angle)*0.5f;
float a = (float)sin(_angle)*0.5f;
pt[0].x = rect.center.x - a*size.height - b*size.width;
pt[0].y = rect.center.y + b*size.height - a*size.width;
pt[1].x = rect.center.x + a*size.height - b*size.width;
pt[1].y = rect.center.y - b*size.height - a*size.width;
pt[2].x = 2 * rect.center.x - pt[0].x;
pt[2].y = 2 * rect.center.y - pt[0].y;
pt[3].x = 2 * rect.center.x - pt[1].x;
pt[3].y = 2 * rect.center.y - pt[1].y;
//防止越界
for (int n = 0; n < 4; n++)
{
if (pt[n].x < 0) pt[n].x = 0.f; if (pt[n].x >= X - 1) pt[n].x = float(X - 1); if (pt[n].y < 0) pt[n].y = 0.f; if (pt[n].y >= Y - 1) pt[n].y = float(Y - 1);
}
//用采样的方式提取待解码区域
cv::LineIterator itStHeight(src, pt[0], pt[1], 4);
cv::LineIterator itEdHeight(src, pt[3], pt[2], 4);
cv::LineIterator itStWidth(src, pt[0], pt[3], 4);
cv::LineIterator itEdWidth(src, pt[1], pt[2], 4);
struct Track
{
cv::Point PosS;
cv::Point PosE;
Track() {};
Track(cv::Point PosS, cv::Point PosE) :PosS(PosS), PosE(PosE) {};
};
std::vector<Track> pairStEd(cv::min(itStHeight.count, itEdHeight.count));
for (int n = 0; n < pairStEd.size(); n++, ++itStHeight, ++itEdHeight)
{
pairStEd[n] = Track(itStHeight.pos(), itEdHeight.pos());
}
int iSamplingStep = int(pairStEd.size()) / 4;
//线采样
cv::Mat srcSampling(cv::Size(cv::max(itStWidth.count, itEdWidth.count), 1), CV_8UC1, cv::Scalar(255));
//
std::vector<cv::Mat> oneDMats;
//行
for (int n = 0; n < (int)pairStEd.size(); n += iSamplingStep)
{
cv::LineIterator it(src, pairStEd[n].PosS, pairStEd[n].PosE, 4);
for (int nn = 0; nn < it.count; nn++, ++it)//列
{
srcSampling.ptr<uint8_t>(0)[nn] = src.ptr<uint8_t>(it.pos().y)[it.pos().x];
}
//判断是否为二维码
cv::Mat testMat;
cv::threshold(srcSampling, testMat, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU);
//
cv::Mat testLabels;
if (cv::connectedComponents(testMat, testLabels) < 6)
{
//判断非二维码
break;
}
//扩展
cv::Mat waitArea;
cv::copyMakeBorder(srcSampling, waitArea, 0, 1, 60, 60, cv::BORDER_REPLICATE);
oneDMats.push_back(waitArea);
}
//存储一维码待解码区域
if ((int)oneDMats.size() > 0)
{
waitAreas.push_back(WaitArea(cv::Mat(), ptMid, getThreshVal_Otsu_8u(oneDMats[0]), _angle*180. / PI, true, oneDMats));
}
}
}
}
}
else
{
//按照识别到码的定位块尺寸来确定参数,如果是DM码该如何确定参数?
//断裂处连接在一起
cv::morphologyEx(m2, m2, cv::MORPH_DILATE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(iBlockSize, iBlockSize)));
//去掉无关区域
cv::bitwise_and(binary, m2, m2);
//对二值图像过滤
cv::Mat labels, stats, centroids;
int nccomps = cv::connectedComponentsWithStats(m2, labels, stats, centroids, 4);
//过滤连通域面积及长/宽比例不符合的,允许50%误差
std::vector<uchar> colors(nccomps + 1, 0);
for (int i = 1; i < nccomps; i++) {
colors[i] = 255;
double dRate = (double)stats.ptr<int>(i)[cv::CC_STAT_WIDTH] / (double)stats.ptr<int>(i)[cv::CC_STAT_HEIGHT];
if ((!(dRate >= (1. - dToleErr) && dRate <= (1. + dToleErr))) | (stats.ptr<int>(i)[cv::CC_STAT_WIDTH] > iBlockSize * 15 * 1.414*(1. + dToleErr)) | (stats.ptr<int>(i)[cv::CC_STAT_HEIGHT] > iBlockSize * 15 * 1.414*(1. + dToleErr))\
| ((double)stats.ptr<int>(i)[cv::CC_STAT_AREA] < std::pow(iBlockSize / 2, 2) * 15))
{
colors[i] = 0;
}
}
//第一次过滤
cv::parallel_for_(cv::Range(0, Y), [&](const cv::Range& range)->void {
for (int y = range.start; y < range.end; y++)
{
uint8_t *ptrRow = m2.ptr<uint8_t>(y);
for (int x = 0; x < X; x++)
{
int label = labels.ptr<int>(y)[x];
CV_Assert(0 <= label && label <= nccomps);
ptrRow[x] = colors[label];
}
}
});
//用于轮廓检测
std::vector<std::vector<cv::Point>> contourAll, contourFilter;
findContours(m2, contourAll, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
for (int i = 0; i < static_cast<int>(contourAll.size()); i++)
{
cv::RotatedRect rect = cv::minAreaRect(contourAll[i]);
//满足矩形条件与面积条件
double dRate = cv::min(rect.size.width, rect.size.height) / cv::max(rect.size.height, rect.size.width);
if (dRate >= (1. - dToleErr) && dRate <= (1. + dToleErr) && ((double)rect.size.width > double(8. * iBlockSize)) && ((double)rect.size.height > double(8. * iBlockSize)))
{
contourFilter.push_back(contourAll[i]);
}
}
for (int i = 0; i < contourFilter.size(); i++)
{
cv::Rect rect = cv::minAreaRect(contourFilter[i]).boundingRect();
cv::RotatedRect rRect = cv::minAreaRect(contourFilter[i]);
//外包矩形
int dynSize = cvRound(cv::max((double)rect.size().height, (double)rect.size().width));
//疑似二维码区域
cv::Mat waitArea = src(cv::Range(cv::max(0, cvRound(rRect.center.y) - cvRound(4.*(double)iBlockSize + dynSize / 2)), cv::min(Y - 1, cvRound(rRect.center.y) + cvRound(4.*(double)iBlockSize + dynSize / 2))), cv::Range(cv::max(0, cvRound(rRect.center.x) - cvRound(4.*(double)iBlockSize + dynSize / 2)), cv::min(X - 1, cvRound(rRect.center.x) + cvRound(4.*(double)iBlockSize + dynSize / 2)))).clone();
//处理后再压入识别
waitAreas.push_back(WaitArea(waitArea, cv::Point(cvRound(rRect.center.x), cvRound(rRect.center.y)), 0, 0, false, std::vector<cv::Mat>()));
}
}
enum {
NONE,
AZTEC,
CODABAR,
CODE_39,
CODE_93,
CODE_128,
DATA_MATRIX,
EAN_8,
EAN_13,
ITF,
MAXICODE,
PDF_417,
QR_CODE,
RSS_14,
RSS_EXPANDED,
UPC_A,
UPC_E,
UPC_EAN_EXTENSION
};
std::vector<std::string> hints_ = { "AZTEC","CODABAR","CODE_39","CODE_93","CODE_128","DATA_MATRIX","EAN_8","EAN_13","ITF","MAXICODE","PDF_417","QR_CODE","RSS_14","RSS_EXPANDED","UPC_A","UPC_E","UPC_EAN_EXTENSION" };
//解码
decodeMul(waitAreas, hints_, showMat, decodeResults, iBlockSize, 10, 1.0);
return FUNC_OK;
}
......
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!