Commit 299c004e 张士柳

1 个父辈 88e2a6de
......@@ -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, 21, 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, 11, 5, 128, 215);
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) + "");
......
......@@ -462,11 +462,6 @@ static void decodeMul(std::vector<WaitArea> &waitAreas, std::vector<std::string>
break;
}
}
if (!bDecode)
{
//如果仍未解码
}
}
else
{
......@@ -524,6 +519,10 @@ static void decodeMul(std::vector<WaitArea> &waitAreas, std::vector<std::string>
}
}
}
//解码成功
breakLoop:
{
if (strResult != std::string())
......@@ -544,6 +543,20 @@ static void decodeMul(std::vector<WaitArea> &waitAreas, std::vector<std::string>
mtx.unlock();
}
static int calcHist(cv::Mat src)
{
const int histSize = 256;
float range[] = { 0,255 };
const float* histRange = { range };
//calculate the histogram
cv::Mat hist;
cv::calcHist(&src, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange);
//calculate the background pixels
int maxIdx[2] = { 255,255 };
cv::minMaxIdx(hist, NULL, NULL, NULL, maxIdx);
return maxIdx[0];
}
int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileName, const char *ccCodeType, IntPtr *hObject, EyemBarCode **hResults, int *ipNum, bool bUseNiBlack, int iBlockSize, const int iRangeC, int iSymbolMin, int iSymbolMax, double dScaleUpAndDown, double dToleErr, double dMinorStep)
{
cv::Mat src = cv::Mat(tpImage.iHeight, tpImage.iWidth, tpImage.iDepth, tpImage.vpImage);
......@@ -552,25 +565,323 @@ int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileNam
}
//提取ROI
src = src(cv::Rect(tpRoi.iXs, tpRoi.iYs, tpRoi.iWidth, tpRoi.iHeight));
//图像原图备份
cv::Mat backup = src.clone();
//真实图像数据与尺寸
cv::Mat realSrc = src.clone();
int iRealX = realSrc.cols, iRealY = realSrc.rows, iRealBlockSize = iBlockSize;
//降采样
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);
//
//图像尺寸
int X = src.cols, Y = src.rows;
//解码结果
std::vector<EyemBarCode> *tpResults = new std::vector<EyemBarCode>();
//图像尺寸,可能为缩放后尺寸
int iX = src.cols, iY = src.rows;
//测试用
cv::Mat srcPrev, binary;
if (dScaleUpAndDown != 1.)
{
//太小则不考虑缩小窗口尺寸
if (iBlockSize > 3)
{
iBlockSize = cvRound((double)(iBlockSize + 1)*dScaleUpAndDown) % 2 == 0 ? cvRound((double)(iBlockSize + 1)*dScaleUpAndDown) - 1 : cvRound((double)(iBlockSize + 1)*dScaleUpAndDown) - 1;
}
}
//高斯滤波去噪
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::Mat srcPrev, binary, mask;
//确定识别类型
std::vector<std::string> hints_;
split(ccCodeType, "|", hints_);
//是否添加一维码检测
bool addOneDReader = std::find(hints_.begin(), hints_.end(), "UPC_A") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "UPC_E") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "EAN_8") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "EAN_13") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "CODABAR") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "CODE_39") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "CODE_93") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "CODE_128") != hints_.end() ||
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() ||
std::find(hints_.begin(), hints_.end(), "AZTEC") != hints_.end();
//未设置识别类型
if (!addOneDReader && !addTwoDReader)
return FUNC_CANNOT_CALC;
//检测热点图,s1用来检测一维码;s2用来检测二维码(条码来说会比背景值小,二维码来说会比背景值大)
cv::Mat s1(iY, iX, CV_8UC1, cv::Scalar(0)), s2(iY, iX, CV_8UC1, cv::Scalar(0));
//<//////////////////////通用预处理方式//////////////////////>//
cv::adaptiveThreshold(src, binary, 255, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY_INV, iBlockSize, 2);
//突出条码部分
cv::morphologyEx(src, srcPrev, cv::MORPH_GRADIENT, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)));
//二值化
cv::Mat srcPrevBin;
cv::threshold(srcPrev, srcPrevBin, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
//略微膨胀覆盖条码
cv::morphologyEx(srcPrevBin, srcPrevBin, cv::MORPH_DILATE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(iBlockSize, iBlockSize)));
//尽量去掉无关区域
cv::bitwise_and(srcPrevBin, binary, binary);
//连通域分析
cv::Mat labels, stats, centroids;
int nccomps = cv::connectedComponentsWithStats(binary, labels, stats, centroids);
//过滤连通域面积及长/宽比例不符合的,允许50%误差
std::vector<uchar> colors(nccomps + 1, 0);
for (int i = 1; i < nccomps; i++) {
colors[i] = 255;
double maxSize = cv::max(stats.ptr<int>(i)[cv::CC_STAT_WIDTH], stats.ptr<int>(i)[cv::CC_STAT_HEIGHT]);
if ((stats.ptr<int>(i)[cv::CC_STAT_AREA] < 15) || (maxSize < ((double)iBlockSize)*(1. - dToleErr)) || (maxSize > 35 * iBlockSize))
{
colors[i] = 0;
}
}
//过滤
cv::parallel_for_(cv::Range(0, iY), [&](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 < iX; x++)
{
int label = labels.ptr<int>(y)[x];
CV_Assert(0 <= label && label <= nccomps);
ptrRow[x] = colors[label];
}
}
});
//膨胀区域
cv::Mat binaryEx;
cv::morphologyEx(binary, binaryEx, cv::MORPH_DILATE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(iBlockSize * 2, iBlockSize * 2)));
//连通域分析
nccomps = cv::connectedComponentsWithStats(binaryEx, labels, stats, centroids);
for (int i = 1; i < nccomps; i++)
{
//角点响应图
cv::Mat harMap;
cv::Rect rec(stats.ptr<int>(i)[cv::CC_STAT_LEFT], stats.ptr<int>(i)[cv::CC_STAT_TOP], stats.ptr<int>(i)[cv::CC_STAT_WIDTH], stats.ptr<int>(i)[cv::CC_STAT_HEIGHT]);
if ((cv::max(rec.size().width, rec.size().height) > 5 * iBlockSize) && (rec.area() > 5 * std::pow(iBlockSize, 2)))
{
cv::cornerHarris(src(rec), harMap, iBlockSize, 3, 0.04);
// 归一化与转换
cv::normalize(harMap, harMap, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat());
cv::convertScaleAbs(harMap, harMap);
// 尺寸
cv::Size sz = rec.size();
// 用于一维码检测
cv::Mat m1 = harMap < calcHist(harMap);
const uchar *s1ptr = m1.data;
uchar *d1ptr = s1.data;
// 叠加图像
cv::parallel_for_(cv::Range(0, sz.height), [&](const cv::Range& range)->void {
for (int y = range.start; y < range.end; y++) {
for (int x = 0; x < sz.width; x++) {
d1ptr[(x + rec.x) + (y + rec.y)*iX] += s1ptr[(x)+(y)*sz.width];
}
}
});
// 用于二维码检测
cv::Mat m2 = harMap > calcHist(harMap);
const uchar *s2ptr = m2.data;
uchar *d2ptr = s2.data;
// 叠加图像
cv::parallel_for_(cv::Range(0, sz.height), [&](const cv::Range& range)->void {
for (int y = range.start; y < range.end; y++) {
for (int x = 0; x < sz.width; x++) {
d2ptr[(x + rec.x) + (y + rec.y)*iX] += s2ptr[(x)+(y)*sz.width];
}
}
});
}
}
//输出解码结果
std::vector<EyemBarCode> *tpResults = new std::vector<EyemBarCode>();
//解码内容
std::vector<DecodeResult> decodeResults;
//待解码区域,区分条码类型来识别
std::vector<WaitArea> waitAreas;
//判断要增加的识别,从这一步可以进行分开处理
if (addOneDReader)
{
//添加一维码识别
cv::morphologyEx(binary, mask, cv::MORPH_CLOSE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(2 * iBlockSize + 1, 2 * iBlockSize + 1)));
//去掉干扰
cv::bitwise_and(s1, mask, s1);
///<进一步去除干扰
cv::morphologyEx(s1, s1, cv::MORPH_CLOSE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)));
//连通域分析
nccomps = cv::connectedComponentsWithStats(s1, labels, stats, centroids);
std::vector<uchar> colors2(nccomps + 1, 0);
for (int i = 1; i < nccomps; i++) {
colors2[i] = 255;
if ((stats.ptr<int>(i)[cv::CC_STAT_AREA] < std::pow(iBlockSize, 2)))
{
colors2[i] = 0;
}
}
//过滤
cv::parallel_for_(cv::Range(0, iY), [&](const cv::Range& range)->void {
for (int y = range.start; y < range.end; y++)
{
uint8_t *ptrRow = s1.ptr<uint8_t>(y);
for (int x = 0; x < iX; x++)
{
int label = labels.ptr<int>(y)[x];
CV_Assert(0 <= label && label <= nccomps);
ptrRow[x] = colors2[label];
}
}
});
//后续识别
}
//添加二维码识别
if (addTwoDReader)
{
//突出条码部分
cv::morphologyEx(src, srcPrev, cv::MORPH_BLACKHAT, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(2 * iBlockSize + 1, 2 * iBlockSize + 1)));
//二值化,用于分割粘连
cv::Mat srcPrevBin;
cv::threshold(srcPrev, srcPrevBin, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
//
cv::morphologyEx(srcPrevBin, srcPrevBin, cv::MORPH_DILATE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(cvRound((double)iBlockSize / 3.), cvRound((double)iBlockSize / 3.))));
//断裂处连接在一起
cv::morphologyEx(s2, s2, cv::MORPH_DILATE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(2 * iBlockSize + 1, 2 * iBlockSize + 1)));
//去除干扰
cv::bitwise_and(srcPrevBin, s2, s2);
//对图像过滤
cv::Mat labels, stats, centroids;
int nccomps = cv::connectedComponentsWithStats(s2, 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)) && ((double)stats.ptr<int>(i)[cv::CC_STAT_AREA] > std::pow(20 * 1.414* dScaleUpAndDown, 2))))
{
colors[i] = 0;
}
}
//第一次过滤
cv::parallel_for_(cv::Range(0, iY), [&](const cv::Range& range)->void {
for (int y = range.start; y < range.end; y++)
{
uint8_t *ptrRow = s2.ptr<uint8_t>(y);
for (int x = 0; x < iX; 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(s2, contourAll, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
for (int i = 0; i < static_cast<int>(contourAll.size()); i++)
{
cv::RotatedRect rec = cv::minAreaRect(contourAll[i]);
//偏移量
cv::Point2f pts[4];
rec.points(pts);
cv::Point ptStart, ptEnd;
ptStart = cv::Point((pts[0] + pts[3]) / 2.); ptEnd = cv::Point((pts[1] + pts[2]) / 2.);
//满足矩形条件与面积条件
double dRate = cv::min(rec.size.width, rec.size.height) / cv::max(rec.size.height, rec.size.width);
if (dRate >= (1. - dToleErr) && dRate <= (1. + dToleErr) && cv::min(rec.size.width, rec.size.height) > 20)
{
int dynSize = cvRound(cv::max((double)rec.boundingRect().size().height, (double)rec.boundingRect().size().width));
cv::Mat waitArea = src(cv::Range(cv::max(0, cvRound(rec.center.y) - (2 * iBlockSize + dynSize / 2)), cv::min(iY - 1, cvRound(rec.center.y) + (2 * iBlockSize + dynSize / 2))), cv::Range(cv::max(0, cvRound(rec.center.x) - (2 * iBlockSize + dynSize / 2)), cv::min(iX - 1, cvRound(rec.center.x) + (2 * iBlockSize + dynSize / 2))));
//计算响应图
cv::Mat harMap;
cv::cornerHarris(waitArea, harMap, iBlockSize, 3, 0.04);
// 归一化与转换
cv::normalize(harMap, harMap, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat());
cv::convertScaleAbs(harMap, harMap);
//进一步判断
cv::Mat m2 = harMap > calcHist(harMap);
//用于轮廓检测
std::vector<std::vector<cv::Point>> contours;
findContours(m2, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
//最大轮廓
std::vector<cv::Point> contourMax = contours[0];
for (int cc = 0; cc < contours.size(); cc++)
{
if (cv::contourArea(contours[cc]) > cv::contourArea(contourMax))
{
contourMax = contours[cc];
}
}
//未过滤前
cv::rectangle(showMat, cv::minAreaRect(contourAll[i]).boundingRect(), cv::Scalar(0, 0, 255), 1);
rec = cv::minAreaRect(contourMax);
dRate = cv::min(rec.size.width, rec.size.height) / cv::max(rec.size.height, rec.size.width);
//判断比例
if (dRate >= (1. - dToleErr) && dRate <= (1. + dToleErr) && cv::min(rec.size.width, rec.size.height) > 20)
{
//按照比例过滤
int flags = 0;
double test_line[6]{ 0 };
cv::LineIterator it(binary, ptStart, ptEnd, 4);
uint8_t future_pixel = 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[flags]++;
if (next_pixel != future_pixel)
{
flags++;
future_pixel = 255 - future_pixel;
if (flags == 6) { break; }
}
}
//满足比例
double dRate = cv::min((test_line[0] + test_line[2] + test_line[4]), (test_line[1] + test_line[3] + test_line[5])) / cv::max((test_line[0] + test_line[2] + test_line[4]), (test_line[1] + test_line[3] + test_line[5]));
if (dRate >= (1. - dToleErr) && dRate <= (1. + dToleErr) && flags >= 6)
{
//符合特征
cv::line(showMat, ptStart, ptEnd, cv::Scalar(0, 255, 255), 1);
cv::rectangle(showMat, cv::minAreaRect(contourAll[i]).boundingRect(), cv::Scalar(0, 255, 0), 1);
}
}
}
}
//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 / dScaleUpAndDown, (double)rect.size().width / dScaleUpAndDown));
// //疑似二维码区域
// cv::Mat waitArea = realSrc(cv::Range(cv::max(0, cvRound(rRect.center.y / dScaleUpAndDown) - cvRound(4.*(double)iBlockSize + dynSize / 2)), cv::min(realSrc.rows - 1, cvRound(rRect.center.y / dScaleUpAndDown) + cvRound(4.*(double)iBlockSize + dynSize / 2))), cv::Range(cv::max(0, cvRound(rRect.center.x / dScaleUpAndDown) - cvRound(4.*(double)iBlockSize + dynSize / 2)), cv::min(realSrc.cols - 1, cvRound(rRect.center.x / dScaleUpAndDown) + cvRound(4.*(double)iBlockSize + dynSize / 2))));
// //处理后再压入识别
// 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);
// //cv::Point2f points[4];
// //rec.points(points);
// //for (int j = 0; j < 4; j++)
// //{
// // cv::line(showMat, points[j], points[(j + 1) % 4], cv::Scalar(0, 165, 255, 255), 1);
// //}
//}
}
//最后解码用原图来解码
//格式化文件名
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::Mat dx, dy, mag;
cv::Sobel(srcPrev, dx, CV_32F, 1, 0);
......@@ -601,68 +912,52 @@ int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileNam
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 {
cv::Mat /*m1(Y, X, CV_8UC1, cv::Scalar(0)),*/ m2(iY, iX, CV_8UC1, cv::Scalar(0));
cv::parallel_for_(cv::Range(0, iY), [&](const cv::Range& range)->void {
for (int y = range.start; y < range.end; y++)
{
for (int x = 0; x < X; x++)
for (int x = 0; x < iX; x++)
{
if (harMap.ptr<uint8_t>(y)[x] < maxIdx[0])
{
m1.ptr<uint8_t>(y)[x] = 255;
s1.ptr<uint8_t>(y)[x] = 255;
}
else if (harMap.ptr<uint8_t>(y)[x] > maxIdx[0])
{
m2.ptr<uint8_t>(y)[x] = 255;
s2.ptr<uint8_t>(y)[x] = 255;
}
}
}
});
//确定识别类型
std::vector<std::string> hints_;
split(ccCodeType, "|", hints_);
//是否添加一维码检测
bool addOneDReader = std::find(hints_.begin(), hints_.end(), "UPC_A") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "UPC_E") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "EAN_8") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "EAN_13") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "CODABAR") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "CODE_39") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "CODE_93") != hints_.end() ||
std::find(hints_.begin(), hints_.end(), "CODE_128") != hints_.end() ||
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() ||
std::find(hints_.begin(), hints_.end(), "AZTEC") != hints_.end();
//未设置识别类型
if (!addOneDReader && !addTwoDReader)
return FUNC_CANNOT_CALC;
//所有解码内容
std::vector<DecodeResult> decodeResults;
//待解码区域,区分条码类型来识别
std::vector<WaitArea> waitAreas;
if (addOneDReader)
{
//测试用
cv::morphologyEx(binary, binary, cv::MORPH_DILATE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(iBlockSize, iBlockSize)));
cv::Mat binFilter;
cv::adaptiveThreshold(realSrc, binFilter, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY_INV, iBlockSize, 2);
//去掉非条码部分
cv::bitwise_and(binFilter, binary, binFilter);
//连通域分析
cv::Mat labels, stats, centroids;
int nccomps = cv::connectedComponentsWithStats(m1, labels, stats, centroids, 4);
int nccomps = cv::connectedComponentsWithStats(binFilter, labels, stats, centroids);
//过滤连通域面积及长/宽比例不符合的,允许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] < std::pow(iBlockSize*dScaleUpAndDown, 2) * 5) | (m1.ptr<uint8_t>(cvRound(centroids.ptr<double>(i)[1]))[cvRound(centroids.ptr<double>(i)[0])] == 0))
double maxSize = cv::max(stats.ptr<int>(i)[cv::CC_STAT_WIDTH], stats.ptr<int>(i)[cv::CC_STAT_HEIGHT]);
if ((stats.ptr<int>(i)[cv::CC_STAT_AREA] < 15) | (maxSize < iBlockSize*(1. + dToleErr)) | (maxSize > 25 * iBlockSize))
{
colors[i] = 0;
}
}
//过滤
cv::parallel_for_(cv::Range(0, Y), [&](const cv::Range& range)->void {
cv::parallel_for_(cv::Range(0, iY), [&](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++)
uint8_t *ptrRow = binFilter.ptr<uint8_t>(y);
for (int x = 0; x < iX; x++)
{
int label = labels.ptr<int>(y)[x];
CV_Assert(0 <= label && label <= nccomps);
......@@ -670,14 +965,112 @@ int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileNam
}
}
});
//用于过滤非条码部分
cv::Mat binFilter;
cv::adaptiveThreshold(backup, binFilter, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY_INV, ksize, 5);
//处理断裂一维码
cv::morphologyEx(m1, m1, cv::MORPH_CLOSE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(cvRound(iBlockSize*dScaleUpAndDown), cvRound(iBlockSize*dScaleUpAndDown))));
cv::Mat back4Filter = binFilter.clone();
//cv::cvtColor(back4Filter, showMat, cv::COLOR_GRAY2BGR);
//用于轮廓检测
std::vector<std::vector<cv::Point>> contourAll, contourFilter;
findContours(m1, contourAll, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
findContours(binFilter, contourAll, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
//初步过滤
for (int i = 0; i < int(contourAll.size()); i++)
{
cv::RotatedRect rect = cv::minAreaRect(contourAll[i]);
double dRate = (double)cv::max(rect.size.height, rect.size.width) / (double)cv::min(rect.size.height, rect.size.width), rgt = cv::contourArea(contourAll[i]) / rect.size.area();
if (!(cv::min(rect.size.height, rect.size.width) > iBlockSize * 4) && (cv::contourArea(contourAll[i]) / rect.size.area()) > 0.35)
{
contourFilter.push_back(contourAll[i]);
}
else
cv::drawContours(binFilter, contourAll, i, cv::Scalar(0), -1);
}
const float tipLength = 128;
for (int c = 0; c < 8; c++)
{
std::vector<cv::Point> approx;
//符合条件,继续增加比例过滤条件
for (int i = 0; i < int(contourFilter.size()); i++)
{
//首先进行四边形过滤
cv::approxPolyDP(cv::Mat(contourFilter[i]), approx, cv::arcLength(cv::Mat(contourFilter[i]), true)*0.02, true);
if (approx.size() > 10)
{
cv::drawContours(binFilter, contourFilter, i, cv::Scalar(0), -1);
continue;
}
cv::RotatedRect rect = cv::minAreaRect(contourFilter[i]);
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;
double _angle = std::atan2(-(ptEnd.x - ptStart.x), ptEnd.y - ptStart.y);
cv::Point pt[2];
pt[0] = cv::Point(cvRound(ptMid.x + tipLength * cos(_angle)),
cvRound(ptMid.y + tipLength * sin(_angle)));
pt[1] = cv::Point(cvRound(ptMid.x + tipLength * cos(_angle + CV_PI)),
cvRound(ptMid.y + tipLength * sin(_angle + CV_PI)));
//防止越界
for (int n = 0; n < 2; n++)
{
if (pt[n].x < 0) pt[n].x = 0.f; if (pt[n].x >= iX - 1) pt[n].x = float(iX - 1); if (pt[n].y < 0) pt[n].y = 0.f; if (pt[n].y >= iY - 1) pt[n].y = float(iY - 1);
}
bool bFit = false;
//扫描像素密度,比例接近1:1记录下来,并且黑白间隔数目小大于长度的一半
for (int ii = 0; ii < 2; ii++)
{
int flags = 0;
double test_line[6]{ 0 };
cv::LineIterator it(back4Filter, ptMid, pt[ii], 4);
uint8_t future_pixel = back4Filter.ptr<uint8_t>(ptMid.y)[ptMid.x];
for (int n = 0; n < it.count; n++, ++it)
{
//统计均匀性
uint8_t next_pixel = back4Filter.ptr<uint8_t>(it.pos().y)[it.pos().x];
//统计黑白像素
test_line[flags]++;
if (next_pixel != future_pixel)
{
flags++;
future_pixel = 255 - future_pixel;
if (flags == 6) { break; }
}
}
//满足比例
double dRate = cv::min((test_line[0] + test_line[2] + test_line[4]), (test_line[1] + test_line[3] + test_line[5])) / cv::max((test_line[0] + test_line[2] + test_line[4]), (test_line[1] + test_line[3] + test_line[5]));
if (dRate >= (1. - dToleErr*1.5) && dRate <= (1. + dToleErr*1.5) && flags >= 6)
{
bFit = true;
//符合条码特征
break;
}
}
if (!bFit)
{
//不符合条码特征
cv::drawContours(binFilter, contourFilter, i, cv::Scalar(0), -1);
}
}
findContours(binFilter, contourFilter, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
back4Filter = binFilter;
}
for (int i = 0; i < static_cast<int>(contourAll.size()); i++)
{
cv::RotatedRect rect = cv::minAreaRect(contourAll[i]);
......@@ -710,7 +1103,7 @@ int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileNam
double test_line[2]{ 0 };
for (int n = 0; n < it.count; n++, ++it)
{
if (m1.ptr<uint8_t>(cvRound(it.pos().y * dScaleUpAndDown))[cvRound(it.pos().x * dScaleUpAndDown)] == 0) continue;
if (s1.ptr<uint8_t>(cvRound(it.pos().y * dScaleUpAndDown))[cvRound(it.pos().x * dScaleUpAndDown)] == 0) continue;
//统计均匀性
uint8_t next_pixel = binFilter.ptr<uint8_t>(it.pos().y)[it.pos().x];
test_line[next_pixel % 254]++;
......@@ -744,16 +1137,16 @@ int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileNam
//防止越界
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);
if (pt[n].x < 0) pt[n].x = 0.f; if (pt[n].x >= iX - 1) pt[n].x = float(iX - 1); if (pt[n].y < 0) pt[n].y = 0.f; if (pt[n].y >= iY - 1) pt[n].y = float(iY - 1);
}
//用采样的方式提取待解码区域
cv::LineIterator itStHeight(backup, pt[0], pt[1], 4);
cv::LineIterator itStHeight(realSrc, pt[0], pt[1], 4);
cv::LineIterator itEdHeight(backup, pt[3], pt[2], 4);
cv::LineIterator itEdHeight(realSrc, pt[3], pt[2], 4);
cv::LineIterator itStWidth(backup, pt[0], pt[3], 4);
cv::LineIterator itStWidth(realSrc, pt[0], pt[3], 4);
cv::LineIterator itEdWidth(backup, pt[1], pt[2], 4);
cv::LineIterator itEdWidth(realSrc, pt[1], pt[2], 4);
struct Track
{
......@@ -779,11 +1172,11 @@ int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileNam
//行
for (int n = 0; n < (int)pairStEd.size(); n += iSamplingStep)
{
cv::LineIterator it(backup, pairStEd[n].PosS, pairStEd[n].PosE, 4);
cv::LineIterator it(realSrc, pairStEd[n].PosS, pairStEd[n].PosE, 4);
for (int nn = 0; nn < it.count; nn++, ++it)//列
{
//showMat.at<cv::Vec3b>(it.pos()) = cv::Vec3b(0, 255, 0);
srcSampling.ptr<uint8_t>(0)[nn] = backup.ptr<uint8_t>(it.pos().y)[it.pos().x];
srcSampling.ptr<uint8_t>(0)[nn] = realSrc.ptr<uint8_t>(it.pos().y)[it.pos().x];
}
//判断是否为二维码
cv::Mat testMat;
......@@ -803,11 +1196,11 @@ 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);
//}
//画图
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);
......@@ -838,11 +1231,11 @@ int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileNam
}
}
//第一次过滤
cv::parallel_for_(cv::Range(0, Y), [&](const cv::Range& range)->void {
cv::parallel_for_(cv::Range(0, iY), [&](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++)
for (int x = 0; x < iX; x++)
{
int label = labels.ptr<int>(y)[x];
CV_Assert(0 <= label && label <= nccomps);
......@@ -871,7 +1264,7 @@ 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();
cv::Mat waitArea = realSrc(cv::Range(cv::max(0, cvRound(rRect.center.y / dScaleUpAndDown) - cvRound(4.*(double)iBlockSize*dScaleUpAndDown + dynSize / 2)), cv::min(realSrc.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(realSrc.cols - 1, cvRound(rRect.center.x / dScaleUpAndDown) + cvRound(4.*(double)iBlockSize*dScaleUpAndDown + dynSize / 2)))).clone();
//处理后再压入识别
waitAreas.push_back(WaitArea(waitArea, cv::Point(cvRound(rRect.center.x / dScaleUpAndDown), cvRound(rRect.center.y / dScaleUpAndDown)), 0, 0, false, std::vector<cv::Mat>()));
//画图
......@@ -911,11 +1304,6 @@ int eyemDetectAndDecode(EyemImage tpImage, EyemRect tpRoi, const char *ccFileNam
*hResults = tpResults->data();
*ipNum = static_cast<int>(tpResults->size());
*hObject = reinterpret_cast<IntPtr>(tpResults);
//格式化文件名
//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;
}
......@@ -1200,30 +1588,9 @@ int eyemCalcDetectParameter(EyemImage tpImage, EyemRect tpRoi, const char *ccFil
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!