Files
ios/Xuxucheck/XuXuCheck/opencv/ImgLocateDryChemistry.cpp
T
admin d6aeede2bc 导入 XuXuCheck 工程(iOS 干化学尿液试纸分析 App)
从桌面工程复制而来,作为本仓库的 Xuxucheck/ 子目录。

为控制仓库体积做了两处删减:
- 移除 Frameworks/opencv2.xcframework 下的两个 visionOS 切片
  (xros-arm64、xros-arm64-simulator,合计约 106 MB),并同步删掉该
  xcframework 内 Info.plist 里对应的两条 AvailableLibraries 记录,
  只保留 ios-arm64 与 ios-arm64_x86_64-simulator。
- 移除全部 .DS_Store 与 xcuserdata/。

已用 xcodebuild 从本目录完整编译验证:BUILD SUCCEEDED,
ProcessXCFramework 以 --platform ios 成功选中框架,产物含 3699 个 cv:: 符号
(OpenCV 确实链入),嵌入 framework 3 个,与移除无用 pod 之后的状态一致。

仓库约 206 MB,其中 OpenCV 203 MB、其余源码与依赖约 3 MB。
2026-09-23 14:17:31 +08:00

1040 lines
34 KiB
C++

//#include <opencv2/imgproc/types_c.h>
#include "ImgLocateDryChemistry.h"
#include "Comparer.h"
#include "ImageLocation.h"
using namespace std;
using namespace cv;
PaperConfig::PaperConfig()
{
PaperConfig(0,0, 0, 0.0, 0.0, 0.0, 0.0, 0.0);
}
PaperConfig::PaperConfig(int pst,int i, int s, double f, double st, double d, double r, double l)
{
paperSpatialType = pst;
distTimes = d;
itemsNum = i;
slicesNum = s;
firstPos = f;
step = st;
whRRatio = r;
whLRatio = l;
}
void PaperConfig::Init()
{
paperSpatialType = 0;
distTimes = 0.0;//21.3
itemsNum = 0;
slicesNum = 0;
firstPos = 0;
step = 0;
whRRatio = 0.0;
whLRatio = 0.0;
}
PaperConfig::~PaperConfig()
{
}
static double max3(double a, double b, double c)
{
if (a >= b && a >= c) return a;
if (b >= a && b >= c) return b;
return c;
}
static double min3(double a, double b, double c)
{
if (a <= b && a <= c) return a;
if (b <= a && b <= c) return b;
return c;
}
ImgLocateDryChemistry::ImgLocateDryChemistry()
{
initPaperConfig();
}
ImgLocateDryChemistry::~ImgLocateDryChemistry()
{
}
void ImgLocateDryChemistry::initPaperConfig()
{
PaperConfig* pc = new PaperConfig();
pc->itemsNum = 14;
pc->paperSpatialType = PST_14_S;
pc->slicesNum = 50;//最小分割单位
pc->firstPos = 2.25;
pc->step = 3.5;
pc->distTimes = 21.3;//定位块中心点的距离/试纸高度
pc->whRRatio = 2;//10毫米宽/5毫米高
pc->whLRatio = 0.6;//3毫米宽/5毫米高
mapPaperConfig.insert(std::make_pair(PST_14_S, pc));
pc = new PaperConfig();
pc->itemsNum = 14;
pc->paperSpatialType = PST_14_L;
pc->slicesNum = 50;//最小分割单位
pc->firstPos = 2.25;
pc->step = 3.5;
pc->distTimes = 21.8;//定位块中心点的距离/试纸高度
pc->whRRatio = 3;//15毫米宽/5毫米高
pc->whLRatio = 0.6;//3毫米宽/5毫米高
mapPaperConfig.insert(std::make_pair(PST_14_L, pc));
//----------------------------------
pc = new PaperConfig();
pc->paperSpatialType = PST_11_L;
pc->itemsNum = 11;
pc->slicesNum = 39;//最小分割单位
pc->firstPos = 2.25;
pc->step = 3.5;
pc->distTimes = 17.6;//定位块中心点的距离/试纸高度
pc->whRRatio = 3;//10毫米宽/5毫米高
pc->whLRatio = 0.6;//3毫米宽/5毫米高
mapPaperConfig.insert(std::make_pair(PST_11_L, pc));
pc = new PaperConfig();
pc->paperSpatialType = PST_10_S;
pc->itemsNum = 10;
pc->slicesNum = 36;//最小分割单位
pc->firstPos = 2.25;
pc->step = 3.5;
pc->distTimes = 15.7;//定位块中心点的距离/试纸高度
pc->whRRatio = 2;//10毫米宽/5毫米高
pc->whLRatio = 0.6;//3毫米宽/5毫米高
mapPaperConfig.insert(std::make_pair(PST_10_S, pc));
//----------------------------------
pc = new PaperConfig();
pc->paperSpatialType = PST_8_S;
pc->itemsNum = 8;
pc->slicesNum = 29;//最小分割单位
pc->firstPos = 2.25;
pc->step = 3.5;
pc->distTimes = 12.9;//定位块中心点的距离/试纸高度
pc->whRRatio = 2;//10毫米宽/5毫米高
pc->whLRatio = 0.6;//3毫米宽/5毫米高
mapPaperConfig.insert(std::make_pair(PST_8_S, pc));
pc = new PaperConfig();
pc->paperSpatialType = PST_8_L;
pc->itemsNum = 8;
pc->slicesNum = 29;//最小分割单位
pc->firstPos = 2.25;
pc->step = 3.5;
pc->distTimes = 13.4;//定位块中心点的距离/试纸高度
pc->whRRatio = 3;//15毫米宽/5毫米高
pc->whLRatio = 0.6;//3毫米宽/5毫米高
mapPaperConfig.insert(std::make_pair(PST_8_L, pc));
//----------------------------------
pc = new PaperConfig();
pc->paperSpatialType = PST_6_S;
pc->itemsNum = 6;
pc->slicesNum = 22;//最小分割单位
pc->firstPos = 2.25;
pc->step = 3.5;
pc->distTimes = 10.1;//定位块中心点的距离/试纸高度
pc->whRRatio = 2;//10毫米宽/5毫米高
pc->whLRatio = 0.6;//3毫米宽/5毫米高
mapPaperConfig.insert(std::make_pair(PST_6_S, pc));
pc = new PaperConfig();
pc->paperSpatialType = PST_6_L;
pc->itemsNum = 6;
pc->slicesNum = 22;//最小分割单位
pc->firstPos = 2.25;
pc->step = 3.5;
pc->distTimes = 10.6;//定位块中心点的距离/试纸高度
pc->whRRatio = 3;//15毫米宽/5毫米高
pc->whLRatio = 0.6;//3毫米宽/5毫米高
mapPaperConfig.insert(std::make_pair(PST_6_L, pc));
//----------------------------------
pc = new PaperConfig();
pc->paperSpatialType = PST_4_S;
pc->itemsNum = 4;
pc->slicesNum = 15;//最小分割单位
pc->firstPos = 2.25;
pc->step = 3.5;
pc->distTimes = 7.3;//定位块中心点的距离/试纸高度
pc->whRRatio = 2;//10毫米宽/5毫米高
pc->whLRatio = 0.6;//3毫米宽/5毫米高
mapPaperConfig.insert(std::make_pair(PST_4_S, pc));
pc = new PaperConfig();
pc->paperSpatialType = PST_4_L;
pc->itemsNum = 4;
pc->slicesNum = 15;//最小分割单位
pc->firstPos = 2.25;
pc->step = 3.5;
pc->distTimes = 7.8;//定位块中心点的距离/试纸高度
pc->whRRatio = 3;//15毫米宽/5毫米高
pc->whLRatio = 0.6;//3毫米宽/5毫米高
mapPaperConfig.insert(std::make_pair(PST_4_L, pc));
// PaperConfig* configGreen = new PaperConfig();
// configGreen->color = ColorGreen;
// configGreen->itemsNum = 15;
// configGreen->slicesNum = 46;//最小分割单位
// configGreen->firstPos = 2;//第一点的步长
// configGreen->step = 3;//步长 × 最小分割长度(总距离/最小分割单位)
// configGreen->distTimes = 20.7;//中心点的距离 除以 试纸的高度之比值
// configGreen->whRRatio = 2;//10毫米宽/5毫米高
// configGreen->whLRatio = 0.6;//3毫米宽/5毫米高
// mapPaperConfig.insert(std::make_pair(ColorGreen, configGreen));
}
Mat& ImgLocateDryChemistry::GetPosition(Mat& s, cv::Rect& roi, bool bDraw, cv::Scalar& wbAvgRgb, vector<cv::Point>&roiPositions)
{
assert(!s.empty());
s.copyTo(src);
Mat(src, roi).copyTo(roiSrc);
cv::cvtColor(roiSrc, rgb, COLOR_RGBA2RGB);//rgba---->>rgb //COLOR_RGBA2RGB = COLOR_BGRA2BGR,
medianBlur(rgb, rgb, 5);
vector<Mat> mats;
cv::split(rgb, mats);
Mat blue = mats[0];
cv::cvtColor(rgb, hsv, COLOR_BGR2HSV);//bgr---->>hsv
vector<Mat> ml;
split(hsv, ml);
Mat& matV = ml[2];
Mat& matS = ml[1];
processImage(matS, blue, matV, roi, true, bDraw, wbAvgRgb, roiPositions);
rectangle(src, cv::Point(roi.x, roi.y), cv::Point(roi.x + roi.width, roi.y + roi.height), cv::Scalar(0, 255, 0, 255), 2);// 画ROI
if (recode == 0)
{
if (bDraw)
{
for (int i = 0; i<roiPositions.size() - 1; ++i)
{
circle(src, cv::Point(roiPositions[i].x + roi.x, roiPositions[i].y + roi.y), (int)(avgDist*0.5), cv::Scalar(0, 0, 255, 255), 3);
}
}
}
releaseContoursGf(vecPt);
return src;
}
int ImgLocateDryChemistry::analyseLocatorColor(Mat& sImg, double& slope)
{
Mat matT;
cv::threshold(sImg, matT, 128, 255, THRESH_BINARY | THRESH_OTSU);
//cv::erode(matT, matT, Mat(), Point(), 2); //cv::dilate(matT, matT, Mat(), Point(), 2);
morphologyEx(matT, matT, MORPH_OPEN, cv::getStructuringElement(MORPH_RECT, cv::Size(9,9)));
ContourSelector::ContoursTExtend contours;
ContourSelector::HierarchyTExtend hierarchy;
findContours(matT, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
for (int i = 0; i<vecPt.size(); ++i)
{
delete vecPt[i].second;
vecPt[i].second = NULL;
}
vecPt.clear();
cs.CalContoursGf(rgb, contours, vecPt);
if (vecPt.size() < 2)
{
return LCC_UNKNOWN;
}
//
// drawContours(src, contours, -1, Scalar(255, 0, 255));
ContourSelector::VectorPT vecPt0_0;
cs.SelectContour(vecPt, vecPt0_0, sImg.cols * 0.05, sImg.cols * 0.95,RightBottomX);
ContourSelector::VectorPT vecPt0_1;
cs.SelectContour(vecPt0_0, vecPt0_1, sImg.cols * 0.05, sImg.cols * 0.95, LeftTopX);
ContourSelector::VectorPT vecPt0;
cs.SelectContour(vecPt0_1, vecPt0, 150/*10X20*/, 100000/*200X400*/, Area);
//cs.SelectContour(vecPt, vecPt0, 150/*10X20*/, 100000/*200X400*/, Area);
if (vecPt0.size()<2)
{
return LCC_UNKNOWN;
}
ContourSelector::VectorPT vecPt1;
cs.SelectContour(vecPt0, vecPt1, 0.25, 0.75, AspectRatio);// 3/5, 5/10, 5/15, 5/5
if (vecPt1.size()<2)
{
return LCC_UNKNOWN;
}
ContourSelector::VectorPT vecPt2;
cs.SelectContour(vecPt1, vecPt2, 0.85, 1.1, Rectangularity);
if (vecPt2.size()<2)
{
return LCC_UNKNOWN;
}
ContourSelector::VectorPT vecPt3;
ContourSelector::VectorPT::iterator it = vecPt2.begin();
for (; it != vecPt2.end(); ++it)
{
if(readLocatorColor(it->second) > 0){
ContourSelector::PairTExtend pt;
pt.first = it->first;
pt.second = it->second;
vecPt3.push_back(pt);
}
// ContourGfExtend *pContourGf = it->second;
// std::map< int, PaperConfig* >::iterator iter = mapPaperConfig.find(readLocatorColor(pContourGf));
// if (iter != mapPaperConfig.end())
// {
// ContourSelector::PairTExtend pt;
// pt.first = it->first;
// pt.second = it->second;
// vecPt3.push_back(pt);
// }
}
if (vecPt3.size() < 2)
{
return LCC_UNKNOWN;
}
/*
ContourSelector::VectorPT vecPt4;
sort(vecPt3.begin(), vecPt3.end(), Comparer<Height>());
double maxH = vecPt3[0].second->getSize().height;
cs.SelectContour(vecPt3, vecPt4, maxH*0.725, maxH*1.0, Height);
if (vecPt4.size()<2)
{
vecPt4.clear();
sort(vecPt3.begin(), vecPt3.end(), Comparer<Width>());
double maxW = vecPt3[0].second->getSize().width;
cs.SelectContour(vecPt3, vecPt4, maxW*0.725, maxW*1.0, Width);
if (vecPt4.size()<2)
return ColorUnknown;
}
*/
ContourSelector::VectorPT vecPt4;
sort(vecPt3.begin(), vecPt3.end(), Comparer<Area>());
double maxArea = vecPt3[0].second->getContourArea();
cs.SelectContour(vecPt3, vecPt4, maxArea*0.12, maxArea*1.05, Area);
// cs.SelectContour(vecPt3, vecPt4, maxArea*0.15, maxArea*1.05, Area);
if (vecPt4.size()<2)
{
return LCC_UNKNOWN;
}
// ContourSelector::VectorPT::iterator itr = vecPt4.begin();
// vector<vector<cv::Point> > bulidContours;
// bulidContours.clear();
ContourSelector::VectorPT::iterator iter = vecPt4.begin();
vector<Point> centerPtVec;
for (; iter != vecPt4.end(); ++iter)
{
ContourGfExtend *pContourGf = iter->second;
cv::Point p= pContourGf->getContourCenter();
centerPtVec.push_back(p);
}
Vec4f line;
fitLine(centerPtVec, line, DIST_L2, 0, 0.01, 0.01);//CV_DIST_L2为最简单快速的最小二乘法,推荐使用
double s = abs(line[1] / (line[0] + 0.000001));
slope = s;
//if(s < 5 && s > 0.2)
if(s > 0.2)
{//斜率大于5说明,角度约在75~105之间吧
return LCC_UNKNOWN;
}
sort(vecPt4.begin(), vecPt4.end(), Comparer<CenterX, false>());
lRectContour = vecPt4.at(0);
rRectContour = vecPt4.at(vecPt4.size() - 1);
lRotRect = lRectContour.second->getRotatedRect();//save it
rRotRect = rRectContour.second->getRotatedRect(); //save it
int lColor = readLocatorColor(lRectContour.second);
int rColor = readLocatorColor(rRectContour.second);
if (lColor != rColor || lColor == LCC_UNKNOWN)
{
return LCC_UNKNOWN;
}
locatorColor = lColor;
int x2 = (rRotRect.center.x - lRotRect.center.x) * (rRotRect.center.x - lRotRect.center.x);
int y2 = (rRotRect.center.y - lRotRect.center.y) * (rRotRect.center.y - lRotRect.center.y);
totalDist = sqrt(x2 + y2);//distance between two locators.
// int lh = min(lRotRect.size.width, lRotRect.size.height);
// int rh = min(rRotRect.size.width, rRotRect.size.height);
int h = lRectContour.second->getContourArea() > rRectContour.second->getContourArea() ?
min(lRotRect.size.width, lRotRect.size.height)
:
min(rRotRect.size.width, rRotRect.size.height);
distTimes = totalDist / (h + 0.001);
// distTimes = totalDist / (max(lh, rh) + 0.001);
double areaTimes =
lRectContour.second->getContourArea() > rRectContour.second->getContourArea() ?
(lRectContour.second->getContourArea() + 0.01) / (rRectContour.second->getContourArea() + 0.01)
:
(rRectContour.second->getContourArea() + 0.01) / (lRectContour.second->getContourArea() + 0.01);
// return verifyPaperConfig(totalDist, areaTimes);
return verifyPaperConfig(distTimes, areaTimes);
// paperConfig = mapPaperConfig.at(locatorColor);
// return locatorColor;
}
int ImgLocateDryChemistry::verifyPaperConfig(double distRadio, double areaRadio) {
//rules for areaRadio
//3*5 / 10 * 5 --> 3.3
//3*3 / 15 * 5 --> 5
//so L --> 4 < areaRadio <6
double Max_AreaRadio_L = 6.0;
double Min_AreaRadio_L = 4.0;
//so S --> 2.5 < areaRadio < 4
double Max_AreaRadio_S = 4.0;
double Min_AreaRadio_S = 2.5;
//rules for distRadio
//14 items: 21.3 / 21.8 --> 21.3 * 0.9 == 19.17 < XXX < 21.8 * 1.1 = 23.98
double Max_DistRadio_14 = 23.5;
double Mix_DistRadio_14 = 19.5;
//14L 21.8
double Max_DistRadio_14L = 23.98;
double Mix_DistRadio_14L = 19.5;
//11L items: 17.6 --> 17.6 * 0.9 == 15.84 < XXX < 17.6 *1.1 == 19.36
double Max_DistRadio_11 = 19.2;
double Mix_DistRadio_11 = 15.9;
//10S items: 15.7 --> 15.7 * 0.9 == 14.13 < XXX < 15.7 *1.1 == 17.27
double Max_DistRadio_10 = 17.2;
double Mix_DistRadio_10 = 14.6;
//8S items: 12.9
double Max_DistRadio_8 = 14.1;
double Mix_DistRadio_8 = 11.6;
//8L items: 13.4
double Max_DistRadio_8L = 14.5;
double Mix_DistRadio_8L = 12;
//6S items: 10.1
double Max_DistRadio_6 = 11.2;
double Mix_DistRadio_6 = 9;
//6L items: 10.6
double Max_DistRadio_6L = 11.4;
double Mix_DistRadio_6L = 9.2;
//4S items: 7.3
double Max_DistRadio_4 = 8.2;
double Mix_DistRadio_4 = 6.6;
//4L items: 7.8
double Max_DistRadio_4L = 8.5;
double Mix_DistRadio_4L = 6.6;
if(areaRadio < Min_AreaRadio_S || areaRadio > Max_AreaRadio_L){
return LCC_UNKNOWN;
}
if(areaRadio < Max_AreaRadio_S){// this is 10mm and 3mm
//small paper
if(distRadio > Mix_DistRadio_14 && distRadio < Max_DistRadio_14){
//14
paperSpatialType = PST_14_S;
paperConfig = mapPaperConfig.at(PST_14_S);
}else if(distRadio > Mix_DistRadio_10 && distRadio < Max_DistRadio_10){
//10
paperSpatialType = PST_10_S;
paperConfig = mapPaperConfig.at(PST_10_S);
}
else if(distRadio > Mix_DistRadio_8 && distRadio < Max_DistRadio_8){
paperSpatialType = PST_8_S;
paperConfig = mapPaperConfig.at(PST_8_S);
}
else if(distRadio > Mix_DistRadio_6 && distRadio < Max_DistRadio_6){
paperSpatialType = PST_6_S;
paperConfig = mapPaperConfig.at(PST_6_S);
}
else if(distRadio > Mix_DistRadio_4 && distRadio < Max_DistRadio_4){
paperSpatialType = PST_4_S;
paperConfig = mapPaperConfig.at(PST_4_S);
}
}else{//this is 15mm and 3mm
if(distRadio > Mix_DistRadio_14L && distRadio < Max_DistRadio_14L){
//14
paperSpatialType = PST_14_L;
paperConfig = mapPaperConfig.at(PST_14_L);
}
else if(distRadio > Mix_DistRadio_11 && distRadio < Max_DistRadio_11){
//11
paperSpatialType = PST_11_L;
paperConfig = mapPaperConfig.at(PST_11_L);
}
else if(distRadio > Mix_DistRadio_8L && distRadio < Max_DistRadio_8L){
//11
paperSpatialType = PST_8_L;
paperConfig = mapPaperConfig.at(PST_8_L);
}
else if(distRadio > Mix_DistRadio_6L && distRadio < Max_DistRadio_6L){
//11
paperSpatialType = PST_6_L;
paperConfig = mapPaperConfig.at(PST_6_L);
}
else if(distRadio > Mix_DistRadio_4L && distRadio < Max_DistRadio_4L){
//11
paperSpatialType = PST_4_L;
paperConfig = mapPaperConfig.at(PST_4_L);
}
}
return locatorColor;
}
//bool ImgLocateDryChemistry::verifyDistance()
//{
// int x2 = (rRotRect.center.x - lRotRect.center.x)*(rRotRect.center.x - lRotRect.center.x);
// int y2 = (rRotRect.center.y - lRotRect.center.y)*(rRotRect.center.y - lRotRect.center.y);
// totalDist = sqrt(x2 + y2);//distance between two locators.
// int lh = min(lRotRect.size.width, lRotRect.size.height);
// int rh = min(rRotRect.size.width, rRotRect.size.height);
// //line(src, rRotRect.center, lRotRect.center, cv::Scalar(0, 0, 255, 255), 3);
// if (abs(totalDist - paperConfig->distTimes * max(lh, rh)) > max(lh, rh) * 3.3 )//类似梯形失真 4 --> 3.3
// {
// return false;
// }
// return true;
//}
Mat& ImgLocateDryChemistry::warpAffineImg(Mat &img)
{
double rotAngle = 0.0;
if (lRectContour.second->getContourArea() > rRectContour.second->getContourArea())
rotAngle = lRotRect.angle;
else
rotAngle = rRotRect.angle;
//double rotAngle = realBigAngle;
if (rotAngle < -45) rotAngle += 90.0;
cv::Point autoRoiCenter(img.cols * 0.5, img.rows * 0.5);
Mat waMat = getRotationMatrix2D(autoRoiCenter, rotAngle, 1);
Mat affineMat;
warpAffine(img, affineMat, waMat, img.size(), INTER_LINEAR);
waMat.release();
affineMat.copyTo(img);
return img;
}
bool ImgLocateDryChemistry::analyseDefinition(Mat& sImg, cv::Rect& roi)
{
cv::Point2f lPts[4];
cv::Point2f rPts[4];
int lRectArea = lRectContour.second->getContourArea();
int rRectArea = rRectContour.second->getContourArea();
lRotRect.points(lPts);
rRotRect.points(rPts);
cv::Size lRectSize = lRotRect.size;
cv::Size rRectSize = rRotRect.size;
cv::Rect rcBigCross;
cv::Rect rcSideEdge;
cv::Rect rcSmallSideEdge;
if (lRectArea>rRectArea)
{
bRot = true;//放反,左边定位块面积大于右边。即:左大右小
lRotRectWidth = max(lRectSize.width, lRectSize.height);
lRotRectHeight = min(lRectSize.width, lRectSize.height);
if (paperConfig->whLRatio >= 1)
{
rRotRectWidth = max(rRectSize.width, rRectSize.height);
rRotRectHeight = min(rRectSize.width, rRectSize.height);
}
else
{
rRotRectWidth = min(rRectSize.width, rRectSize.height);
rRotRectHeight = max(rRectSize.width, rRectSize.height);
}
double whLRatio = (double)lRotRectWidth / lRotRectHeight;
double whRRatio = (double)rRotRectWidth / rRotRectHeight;
if ((abs(whLRatio - paperConfig->whRRatio) / paperConfig->whRRatio) > 0.2
|| (abs(whRRatio - paperConfig->whLRatio) / paperConfig->whLRatio) > 0.25)
{
return false;
}
rcBigCross.x = lRotRect.center.x - lRotRectWidth * 0.25;
rcBigCross.y = lRotRect.center.y - lRotRectHeight;
rcBigCross.width = lRotRectWidth * 0.5;
rcBigCross.height = lRotRectHeight * 2;
rcSideEdge.x = lRotRect.center.x - lRotRectWidth*0.75;
rcSideEdge.y = lRotRect.center.y - lRotRectHeight*0.25;
rcSideEdge.width = lRotRectWidth * 0.5;
rcSideEdge.height = lRotRectHeight * 0.5;
rcSmallSideEdge.x = rRotRect.center.x;// -rRotRectWidth*0.75;
rcSmallSideEdge.y = rRotRect.center.y - rRotRectHeight*0.25;
rcSmallSideEdge.width = rRotRectHeight * 0.5;
rcSmallSideEdge.height = rRotRectHeight * 0.5;
}
else
{
bRot = false; //即:左小右大
if (paperConfig->whLRatio >= 1)
{
lRotRectWidth = max(lRectSize.width, lRectSize.height);
lRotRectHeight = min(lRectSize.width, lRectSize.height);
}
else
{
lRotRectWidth = min(lRectSize.width, lRectSize.height);
lRotRectHeight = max(lRectSize.width, lRectSize.height);
}
rRotRectWidth = max(rRectSize.width, rRectSize.height);
rRotRectHeight = min(rRectSize.width, rRectSize.height);
double whLRatio = (double)lRotRectWidth / lRotRectHeight;
double whRRatio = (double)rRotRectWidth / rRotRectHeight;
if ((abs(whLRatio - paperConfig->whLRatio) / paperConfig->whLRatio) > 0.25
|| (abs(whRRatio - paperConfig->whRRatio) / paperConfig->whRRatio) > 0.2)
{
return false;
}
rcBigCross.x = rRotRect.center.x - rRotRectWidth * 0.25;
rcBigCross.y = rRotRect.center.y - rRotRectHeight;
rcBigCross.width = rRotRectWidth * 0.5;
rcBigCross.height = rRotRectHeight * 2;
rcSideEdge.x = rRotRect.center.x + rRotRectWidth*0.25;
rcSideEdge.y = rRotRect.center.y - rRotRectHeight*0.25;
rcSideEdge.width = rRotRectWidth * 0.5;
rcSideEdge.height = rRotRectHeight * 0.5;
rcSmallSideEdge.x = lRotRect.center.x - lRotRectHeight*0.5;
rcSmallSideEdge.y = lRotRect.center.y - lRotRectHeight*0.25;
rcSmallSideEdge.width = lRotRectHeight * 0.5;
rcSmallSideEdge.height = lRotRectHeight * 0.5;
}
//判断高度比,比例为1
if (abs((double)lRotRectHeight / rRotRectHeight - 1) > 0.18//梯形失真
|| lRotRectHeight < 15
|| rRotRectHeight < 15
|| rcBigCross.x <= 0
|| rcBigCross.y <= 0
|| rcBigCross.x + rcBigCross.width >= (roi.width - 1)
|| rcBigCross.y + rcBigCross.height >= (roi.height - 1)
|| rcSmallSideEdge.x <= 0
|| rcSmallSideEdge.y <= 0
|| rcSmallSideEdge.x + rcSmallSideEdge.width >= (roi.width - 1)
|| rcSmallSideEdge.y + rcSmallSideEdge.height >= (roi.height - 1)
|| rcSideEdge.x <= 0
|| rcSideEdge.y <= 0
|| rcSideEdge.x + rcSideEdge.width >= (roi.width - 1)
|| rcSideEdge.y + rcSideEdge.height >= (roi.height - 1))
{
return false;
}
rcBigCross.x += roi.x; rcBigCross.y += roi.y;
rcSideEdge.x += roi.x; rcSideEdge.y += roi.y;
rcSmallSideEdge.x += roi.x; rcSmallSideEdge.y += roi.y;
Mat m1(src, rcBigCross);
Mat m2 = Mat(src, rcSideEdge).t();
Mat m3;
Mat m4;
Mat m6;
cv::cvtColor(m1, m3, COLOR_BGR2GRAY);
cv::cvtColor(m2, m4, COLOR_BGR2GRAY);
Mat m5 = Mat(src, rcSmallSideEdge).t();
cv::cvtColor(m5, m6, COLOR_BGR2GRAY);
// rectangle(src, rcBigCross, cv::Scalar(0, 0, 255, 255), 2);
// rectangle(src, rcSideEdge, cv::Scalar(0, 0, 255, 255), 2);
// rectangle(src, rcSmallSideEdge, cv::Scalar(0, 0, 255, 255), 2);
//easy to scan
// if(calGrayImgDefinition(m3,3.5,4)==true && calGrayImgDefinition(m4,2.5,2)==true && calGrayImgDefinition(m6,2,2)==true)
// return true;
if(calGrayImgDefinition(m3, 4, 5) && calGrayImgDefinition(m4, 3, 2))
return true;
// if(locatorColor==ColorRed || locatorColor == ColorOrange || locatorColor==ColorBlue_14)
// {
// if(calGrayImgDefinition(m3, 2.5, 4) && calGrayImgDefinition(m4, 2, 2) &&
// calGrayImgDefinition(m6, 2, 1))
// return true;
// }/*else if(locatorColor==ColorBlue_14){
// if(calGrayImgDefinition(m3,3.7,5)==true && calGrayImgDefinition(m4,2.8,2)==true && calGrayImgDefinition(m6,2.3,2)==true)
// return true;
// }*/else{
// if(calGrayImgDefinition(m3, 4, 5) && calGrayImgDefinition(m4, 3, 2))
// return true;
// }
return false;
//
// Mat bigCrossMat(sImg, rcBigCross);
// Mat sideEdgeMat = Mat(sImg, rcSideEdge).t();
// Mat smallsideEdgeMat = Mat(sImg, rcSmallSideEdge).t();
///*
// rcBigCross.x += roi.x; rcBigCross.y += roi.y;
// rcSideEdge.x += roi.x; rcSideEdge.y += roi.y;
// rcSmallSideEdge.x += roi.x; rcSmallSideEdge.y += roi.y;
// rectangle(src, rcBigCross, cv::Scalar(0, 255, 0, 255), 1);
// rectangle(src, rcSideEdge, cv::Scalar(0, 255, 0, 255), 1);
// rectangle(src, rcSmallSideEdge, cv::Scalar(0, 255, 0, 255), 1);
//*/
// double diff, avg, diffC, diffBS, diffSS, avgC, avgBS, avgSS;
// int low, top, lowC, lowBS, lowSS, topC, topBS, topSS;
// double crossDefinitionValue, sideDefinitionValue, definitionValueSmall;
// crossDefinitionValue = calImgDefinition(bigCrossMat, diffC, avgC, lowC, topC);
// sideDefinitionValue = calImgDefinition(sideEdgeMat, diffBS, avgBS, lowBS, topBS);
// definitionValueSmall = calImgDefinition(smallsideEdgeMat, diffSS, avgSS, lowSS, topSS);
//
// topDefinitionValue = max3(crossDefinitionValue, sideDefinitionValue, definitionValueSmall);
// diff = max3(diffC, diffBS, diffSS);
// avg = min3(avgC, avgBS, avgSS);//下界
// low = min3(lowC, lowBS, lowSS);//下界
// top = max3(topC, topBS, topSS);//上界
// //cout <<diff << " " << crossDefinitionValue << " " << avg << " " << low << " " << top << " " << sideDefinitionValue << " " << definitionValueSmall << endl;
// if(diff < 85 || topDefinitionValue < 1.85 || low > 15 || top < 2){
//
// //if (diff < 85 || definitionValue < 1.85 || avg > 40 || low > 15 || top < 2) {
// return false;
// }
// return true;
}
bool ImgLocateDryChemistry::calGrayImgDefinition(Mat &img,double m,int u)
{
Mat sobelX,sobelY, absSum;
cv::Scalar scalar;
Scharr(img, sobelX,CV_16S, 1, 0, 1, 0, cv::BORDER_DEFAULT);//Sobel
cv::convertScaleAbs(sobelX, sobelX);
Scharr(img, sobelY,CV_16S, 0, 1, 1, 0, cv::BORDER_DEFAULT);//Sobel
cv::convertScaleAbs(sobelY, sobelY);
cv::addWeighted(sobelX, 0.1, sobelY, 0.9, 0, absSum);
Scalar Sum = cv::sum(absSum);
double avg = Sum.val[0] / (absSum.rows * absSum.cols);
double diff;
int low,up;
uchar *pData = absSum.data;
double multiple,val,pre;
int step = absSum.step;
bool first = true;
multiple= val= pre=diff = 0;
low = up=0;
for (int y = 0; y < absSum.rows; ++y) {
for (int x = 0; x < absSum.cols; ++x) {
val += pData[y * step + x];
}
val = val / absSum.cols;
if (first) {
pre = val + 0.00001;
first = false;
continue;
}
// if (val > avg) low++;//60,大于均值的行数
if (val > 180) up++;//大值的行数,200已经非常好的梯度
if (abs(val - pre) > diff) {
multiple = max(val, pre) / (min(val, pre) + 0.0001);
diff = abs(val - pre);
}
pre = val;
}
if(multiple > m && up > u) return true;
return false;
}
double ImgLocateDryChemistry::calImgDefinition(Mat &img, double &diff, double &avg, int &lowLines, int &upLines)
{
Mat sobelX,sobelY, absSum;
cv::Scalar scalar;
Scharr(img, sobelX, CV_16S, 1, 0);//Sobel
Scharr(img, sobelY, CV_16S, 0, 1);//Sobel
absdiff(sobelX, scalar, sobelX);//abs负值变正值
absdiff(sobelY, scalar, sobelY);//abs负值变正值
add(sobelX, sobelY, absSum);
absSum.convertTo(absSum, CV_8UC1, 0.25);
Scalar Sum = cv::sum(absSum);
avg = Sum.val[0] / (absSum.rows * absSum.cols + 0.001);
uchar *pData = absSum.data;
double multiple,val,pre;
int step = absSum.step;
multiple= val= pre=diff = lowLines = upLines=0;
for (int y = 0; y < absSum.rows; ++y) {
for (int x = 0; x < absSum.cols; ++x) {
val += pData[y * step + x];
}
val = val / absSum.cols;
if (pre == 0 || val < 10) {
pre = val + 0.00001;
continue;
}
if (val > avg) lowLines++;//60,大于均值的行数
if (val > 185) upLines++;//大值的行数,200已经非常好的梯度
if (abs(val - pre) > diff) {
multiple = max(val, pre) / (min(val, pre) + 0.0001);
diff = abs(val - pre);
}
pre = val;
}
return multiple;
}
int ImgLocateDryChemistry::readLocatorColor(ContourGfExtend* pContourGfE)
{
/* Point pt = pContourGfE->getContourCenter();
if(pt.x < 6 || (pt.x + 10) > src.cols || pt.y < 6 || (pt.y + 10) > src.rows) return ColorUnknown;
Rect rc(pt.x - 5, pt.y - 5, 10, 10);
Mat rcMat(src, rc);
Mat hMat, labMat;
cvtColor(rcMat, hMat, COLOR_BGR2HSV);
Scalar avgHsv = mean(hMat);
double h = avgHsv[0];
if(h < 10 || h > 165) return ColorRed;
if(h > 100 && h < 140) return ColorBlue;
if(h > 50 && h < 90) return ColorGreen;*/
double r = pContourGfE->getColorPercentage(0);
double g = pContourGfE->getColorPercentage(1);
double b = pContourGfE->getColorPercentage(2);
if (g > b && g > r) return LCC_GREEN;
if (r > g && r > b) return LCC_RED;
if (b > r && b > g) return LCC_BLUE;
Point pt = pContourGfE->getContourCenter();
if (pt.x < 10 || pt.y < 10 || (pt.x+10) > rgb.cols || (pt.y+10) > rgb.rows)
return LCC_UNKNOWN;
cv::Rect rect(pt.x - 5, pt.y - 5, 10, 10);
Mat mat(hsv, rect);
Scalar mv, dv;
meanStdDev(mat, mv, dv);
double h = mv[0];
if (h < 25 || h > 150)
{
return LCC_RED;
}
else if (h >40 && h < 95)
{
return LCC_GREEN;
}
else if (h >100 && h < 135) {
return LCC_BLUE;
}
if (r<0.225&&b>0.1) {
double t = g / (b + 0.00001);
if (t >= 0.85&&t <= 1.0)
{
return LCC_GREEN;
}
}
return LCC_UNKNOWN;
}
void ImgLocateDryChemistry::releaseContoursGf(ContourSelector::VectorPT& vectorPt, int code)
{
for (int i = 0; i<vectorPt.size(); ++i)
{
delete vectorPt[i].second;
vectorPt[i].second = NULL;
}
vectorPt.clear();
}
int ImgLocateDryChemistry::GetPaperItems()
{
if(paperConfig != nullptr){
return paperConfig->itemsNum;
}
return 0;
}
int ImgLocateDryChemistry::GetLocatorType()
{
if(paperConfig != nullptr){
if(paperConfig->whRRatio > 2)
return 3;
else return 2;
}
return 0;
}
void ImgLocateDryChemistry::savePaperImg()
{
Point2f pts1[4];
rRotRect.points(pts1);
Point2f pts2[4];
lRotRect.points(pts2);
vector<Point2f> points;
for (int i = 0; i < 4; i++)
{
points.insert(points.begin(), pts1[i]);
points.insert(points.begin(), pts2[i]);
}
Rect maxRect = boundingRect(points);
if (maxRect.x < 1) maxRect.x = 1;
if (maxRect.y < 1) maxRect.y = 1;
if ((maxRect.width + maxRect.x) >= rgb.cols)
{
maxRect.width = rgb.cols - maxRect.x;
}
if ((maxRect.height + maxRect.y) >= rgb.rows)
{
maxRect.height = rgb.rows - maxRect.y;
}
paperImg.release();
if ((maxRect.width + maxRect.x) <= rgb.cols && (maxRect.height + maxRect.y) <= rgb.rows)
{
Mat(rgb, maxRect).copyTo(paperImg);//roiSrc
}
else
{
rgb.copyTo(paperImg);//roiSrc
}
cvtColor(paperImg, paperImg, COLOR_BGRA2RGB);
}
int ImgLocateDryChemistry::processImage(Mat& sImg, Mat& b, Mat&v, cv::Rect& roi, bool useSThres, bool bDraw, cv::Scalar& wbAvgRgb, vector<cv::Point>&roiPositions)
{
recode = 0;
double s = 0;
paperSpatialType = PST_UNKNOWN;
int c = analyseLocatorColor(sImg, s);
//ColorUnknown
if (c == LCC_UNKNOWN || paperSpatialType == PST_UNKNOWN) {
/* if (s > 5)
return (recode = 9);*/
return (
recode = 1
);
}
// if (false == verifyDistance())
// {
// return (recode = 2);
// }
sImg = warpAffineImg(sImg);
if (false == analyseDefinition(sImg, roi))
{
return (recode = 4);
}
//开始计算各个反应快的具体位置
paperHeight = (lRotRectHeight + rRotRectHeight)*0.5;
int dx = rRotRect.center.x - lRotRect.center.x;//dx一定大于0
int dy = rRotRect.center.y - lRotRect.center.y;//dy可能大于0,小于0,等于0
double dx2 = dx*dx;
double dy2 = dy*dy;
double sinVal = sqrt(dy2 / (dx2 + dy2));
double cosVal = sqrt(1 - (dy2 / (dx2 + dy2)));
int leftRotRectHalfWidth = lRotRectWidth*0.5;
int rightRotRectHalfWidth = rRotRectWidth*0.5;
cv::Point leftEdgeCenter;
cv::Point rightEdgeCenter;
int absDy = abs(dy);
double k = 0.0;
if (absDy >= 0 && absDy <= 2)
{//左右定位块的高度差在2像素范围之内,我们认为图像摆的很正,不需要调整
leftEdgeCenter.x = lRotRect.center.x + leftRotRectHalfWidth;
leftEdgeCenter.y = lRotRect.center.y;
rightEdgeCenter.x = rRotRect.center.x - rightRotRectHalfWidth;
rightEdgeCenter.y = rRotRect.center.y;
k = 0.0;
}
else if (absDy>2)
{
k = (double)dy / dx;//K值实际上是三角函数的Tan函数,对边 除以 临边
if (k<0)
{//右低,左高
leftEdgeCenter.x = lRotRect.center.x + leftRotRectHalfWidth*cosVal;
leftEdgeCenter.y = lRotRect.center.y - leftRotRectHalfWidth*sinVal;//上移
rightEdgeCenter.x = rRotRect.center.x - rightRotRectHalfWidth*cosVal;
rightEdgeCenter.y = rRotRect.center.y + rightRotRectHalfWidth*sinVal;//下移
}
else if (k>0)
{//右高,左低
leftEdgeCenter.x = lRotRect.center.x + leftRotRectHalfWidth*cosVal;
leftEdgeCenter.y = lRotRect.center.y + leftRotRectHalfWidth*sinVal;
rightEdgeCenter.x = rRotRect.center.x - rightRotRectHalfWidth*cosVal;
rightEdgeCenter.y = rRotRect.center.y - rightRotRectHalfWidth*sinVal;
}
}
avgDist = (double)(rightEdgeCenter.x - leftEdgeCenter.x) / paperConfig->slicesNum;
roiPositions.clear();
double firstPointDistance = avgDist * paperConfig->firstPos;
double stepDistance = avgDist * paperConfig->step;
cv::Point firstPt(leftEdgeCenter.x + firstPointDistance, leftEdgeCenter.y + k*firstPointDistance);//第一个块位置
roiPositions.push_back(firstPt);
double tmp = 0;
for (int i = 1; i < paperConfig->itemsNum; ++i)
{
cv::Point p;
tmp += stepDistance;
p.x = (int)(firstPt.x + tmp);
p.y = (int)(firstPt.y + k*tmp);
roiPositions.push_back(p);
}
//计算白色块位置
cv::Point wbPt;//白平衡位置
int wbDist = firstPointDistance*3.1;
if (bRot)
{
wbPt.x = leftEdgeCenter.x - wbDist;
wbPt.y = leftEdgeCenter.y - k*wbDist;
}
else
{
wbPt.x = rightEdgeCenter.x + wbDist;
wbPt.y = rightEdgeCenter.y + k*wbDist;
}
cv::Rect wbRc((int)wbPt.x - 5, (int)wbPt.y - 5, 10, 10);
if (wbRc.x <= 0 || (wbRc.x + wbRc.width) >= (roi.width - 1)
|| wbRc.y <= 0
|| (wbRc.y + wbRc.height) >= (roi.height - 1))
{
return (recode = 4);
}
Mat wbImg(rgb, wbRc);//roiSrc
wbAvgRgb = mean(wbImg);
if (bRot)//旋转
{
vector<cv::Point> cpyRoiPositions(roiPositions);
roiPositions.clear();
for (int i = cpyRoiPositions.size() - 1; i >= 0; --i)
{
roiPositions.push_back(cpyRoiPositions[i]);
}
}
roiPositions.push_back(wbPt);
score = 100;
savePaperImg();
return (recode = 0);
}