导入 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。
This commit is contained in:
2026-09-23 14:17:31 +08:00
commit d6aeede2bc
901 changed files with 327267 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
#ifndef __COMPARER_H__
#define __COMPARER_H__
#include "ContourSelector.h"
template <GF_FLAG T,bool isGreater=true>
struct Comparer
{
bool operator()(const ContourSelector::PairTExtend& s1, const ContourSelector::PairTExtend& s2)
{
ContourGfExtend *pContourGf1=s1.second;
ContourGfExtend *pContourGf2=s2.second;
double gfValue1=0.0;
double gfValue2=0.0;
switch (T)
{
case Perimeter:
{
gfValue1 = pContourGf1->getContourPerimeter();
gfValue2 = pContourGf2->getContourPerimeter();
}
break;
case Area:
{
gfValue1 = pContourGf1->getContourArea();
gfValue2 = pContourGf2->getContourArea();
}
break;
case CenterX:
{
gfValue1 = pContourGf1->getContourCenter().x;
gfValue2 = pContourGf2->getContourCenter().x;
}
break;
case CenterY:
{
gfValue1 = pContourGf1->getContourCenter().y;
gfValue2 = pContourGf2->getContourCenter().y;
}
break;
case LeftTopX:
{
gfValue1 = pContourGf1->getLeftTop().y;
gfValue2 = pContourGf2->getLeftTop().y;
}
break;
case LeftTopY:
{
gfValue1 = pContourGf1->getLeftTop().y;
gfValue2 = pContourGf2->getLeftTop().y;
}
break;
case RightBottomX:
{
gfValue1 = pContourGf1->getRightBottom().x;
gfValue2 = pContourGf2->getRightBottom().x;
}
break;
case RightBottomY:
{
gfValue1 = pContourGf1->getRightBottom().y;
gfValue2 = pContourGf2->getRightBottom().y;
}
break;
case AspectRatio: //长宽比
{
gfValue1=pContourGf1->getContourAspectRatio();
gfValue2=pContourGf2->getContourAspectRatio();
}
break;
case Rectangularity: //矩形度
{
gfValue1=pContourGf1->getContourRectangularity();
gfValue2=pContourGf2->getContourRectangularity();
}
break;
//灰度值
case GrayValue:
{
gfValue1=pContourGf1->getContourGrayValue();
gfValue2=pContourGf2->getContourGrayValue();
}
break;
case Width:
{
gfValue1=pContourGf1->getSize().width;
gfValue2=pContourGf2->getSize().width;
}
break;
case Height:
{
gfValue1=pContourGf1->getSize().height;
gfValue2=pContourGf2->getSize().height;
}
break;
case RedPercentage:
{
gfValue1=pContourGf1->getColorPercentage(0);
gfValue2=pContourGf2->getColorPercentage(0);
}
break;
case GreenPercentage:
{
gfValue1=pContourGf1->getColorPercentage(1);
gfValue2=pContourGf2->getColorPercentage(1);
}
break;
case BluePercentage:
{
gfValue1=pContourGf1->getColorPercentage(2);
gfValue2=pContourGf2->getColorPercentage(2);
}
break;
case RotRectArea:
{
gfValue1 = pContourGf1->getRotatedRect().size.height * pContourGf1->getRotatedRect().size.width;
gfValue2 = pContourGf2->getRotatedRect().size.height * pContourGf2->getRotatedRect().size.width;
}
break;
}
if (isGreater)
{
return gfValue1 > gfValue2; //从da到xiao排序
}
else
{
return gfValue1 < gfValue2;
}
}
};
#endif
+141
View File
@@ -0,0 +1,141 @@
#ifndef __CONTOURGF_H__
#define __CONTOURGF_H__
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
//#include <opencv2/core/types_c.h>
using namespace cv;
enum GF_FLAG
{
Perimeter,
Area,
CenterX,
CenterY,
LeftTopX,
LeftTopY,
RightBottomX,
RightBottomY,
AspectRatio,
Rectangularity,
GrayValue,
Width,
Height,
RedPercentage,
GreenPercentage,
BluePercentage,
RotRectArea
};
class ContourGf {
private:
int contourPerimeter; //
int contourArea; //
cv::Point contourCenter; //
cv::Point leftTop;
cv::Point rightBottom;
public:
ContourGf()
{
// TODO Auto-generated constructor stub
memset(&contourCenter,0,sizeof(cv::Point));
memset(&leftTop,0,sizeof(cv::Point));
memset(&rightBottom,0,sizeof(cv::Point));
contourPerimeter=0;
contourArea=0;
}
ContourGf(int contourPerimeter,
int contourArea,
cv::Point& contourCenter,
cv::Point& leftTop,
cv::Point& rightBottom)
{
this->contourPerimeter = contourPerimeter;
this->contourArea = contourArea;
this->contourCenter = contourCenter;
this->leftTop = leftTop;
this->rightBottom = rightBottom;
}
public:
int getContourPerimeter()
{
return contourPerimeter;
}
void setContourPerimeter(int contourPerimeter)
{
this->contourPerimeter = contourPerimeter;
}
int getContourArea()
{
return contourArea;
}
void setContourArea(int contourArea)
{
this->contourArea = contourArea;
}
cv::Point getContourCenter()
{
return contourCenter;
}
void setContourCenter(cv::Point& contourCenter)
{
this->contourCenter = contourCenter;
}
void setContourCenter(int x, int y)
{
this->contourCenter.x=x;
this->contourCenter.y=y;
}
cv::Point getLeftTop()
{
return leftTop;
}
void setLeftTop(cv::Point& leftTop)
{
this->leftTop = leftTop;
}
void setLeftTop(int x, int y)
{
this->leftTop.x=x;
this->leftTop.y=y;
}
cv::Point getRightBottom()
{
return rightBottom;
}
void setRightBottom(cv::Point& rightBottom)
{
this->rightBottom = rightBottom;
}
void setRightBottom(int x, int y)
{
this->rightBottom.x=x;
this->rightBottom.y=y;
}
};
#endif
@@ -0,0 +1,130 @@
#ifndef __CONTOURGF_EXTEND_H__
#define __CONTOURGF_EXTEND_H__
#include "ContourGf.h"
class ContourGfExtend :public ContourGf
{
public:
ContourGfExtend():ContourGf()
{
contourAspectRatio=0.0;
contourRectangularity=0.0;
contourGrayValue=0;
colorPercentage[0]=0.0;
colorPercentage[1]=0.0;
colorPercentage[2]=0.0;
}
ContourGfExtend(double contourAspectRatio,
double contourRectangularity,
int contourGrayValue):ContourGf()
{
this->contourAspectRatio=contourAspectRatio;
this->contourRectangularity=contourRectangularity;
this->contourGrayValue=contourGrayValue;
}
ContourGfExtend(double contourAspectRatio,
double contourRectangularity,
int contourGrayValue,
int contourPerimeter,
int contourArea,
cv::Point& contourCenter,
cv::Point& leftTop,
cv::Point& rightBottom)
:
ContourGf(contourPerimeter,
contourArea,
contourCenter,
leftTop,
rightBottom)
{
this->contourAspectRatio=contourAspectRatio;
this->contourRectangularity=contourRectangularity;
this->contourGrayValue=contourGrayValue;
}
~ContourGfExtend(){}
private:
double contourAspectRatio;
double contourRectangularity;
int contourGrayValue;
cv::RotatedRect rotRect;
int contourIndex;
cv::Size size;//选择矩形的宽和高
double colorPercentage[3];//颜色比例,rgb
public:
void setColorPercentage(double v1,double v2,double v3)
{
colorPercentage[0]=v1;
colorPercentage[1]=v2;
colorPercentage[2]=v3;
}
double getColorPercentage(int vIndex)
{
return colorPercentage[vIndex];
}
double getContourAspectRatio()
{
return contourAspectRatio;
}
void setContourAspectRatio(double contourAspectRatio)
{
this->contourAspectRatio=contourAspectRatio;
}
double getContourRectangularity()
{
return contourRectangularity;
}
void setContourRectangularity(double contourRectangularity)
{
this->contourRectangularity=contourRectangularity;
}
int getContourGrayValue()
{
return contourGrayValue;
}
void setContourGrayValue(int contourGrayValue)
{
this->contourGrayValue=contourGrayValue;
}
const cv::RotatedRect& getRotatedRect()
{
return rotRect;
}
void setRotatedRect(const cv::RotatedRect& rotRect)
{
this->rotRect=rotRect;
}
const int getRotRectArea()
{
return rotRect.size.width * rotRect.size.height;
}
void setContourIndex(int contourIndex)
{
this->contourIndex=contourIndex;
}
int getContourIndex()
{
return contourIndex;
}
void setSize(cv::Size size)
{
this->size=size;
}
const cv::Size& getSize()
{
return size;
}
};
#endif
@@ -0,0 +1,190 @@
#include "ContourSelector.h"
/*
#include <android/log.h>
#include "Utils.h"
#define LOG_TAG "NDK"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define ITEM_SIZE 16
*/
#define MIN_VALUE 5
ContourSelector::ContourSelector()
{
}
ContourSelector:: ~ContourSelector()
{
}
void ContourSelector::CalContoursGf(Mat & src,
ContoursTExtend & contours,
VectorPT &vectorPT)
{
for (unsigned int i = 0; i < contours.size(); i++)
{
ContourGfExtend *pContourGf = new ContourGfExtend();
vector<cv::Point> &pContour = contours.at(i);
cv::Rect rect = boundingRect(pContour);
cv::RotatedRect roteRect=minAreaRect(pContour);
pContourGf->setContourArea((int)contourArea(pContour));
pContourGf->setContourPerimeter((int)arcLength(pContour, true));
pContourGf->setRotatedRect(roteRect);
pContourGf->setContourIndex(i);
int width=max(roteRect.size.height,roteRect.size.width);
int height=min(roteRect.size.height,roteRect.size.width);
//pContourGf->setContourArea(width*height);
pContourGf->setSize(cv::Size(rect.width,rect.height));
pContourGf->setContourAspectRatio((double)height/width);//�����
pContourGf->setContourRectangularity((double)pContourGf->getContourArea()/(width*height));//���ζ�
pContourGf->setContourCenter((int)roteRect.center.x, (int)roteRect.center.y);
pContourGf->setLeftTop(rect.x, rect.y);
pContourGf->setRightBottom(rect.x + rect.width, rect.y + rect.height);
cv::Rect roi((int)roteRect.center.x,(int)roteRect.center.y,MIN_VALUE,MIN_VALUE);
if (roi.x+MIN_VALUE>(src.cols-1)
||roi.y+MIN_VALUE>(src.rows-1)
||width<MIN_VALUE
||height<MIN_VALUE)
{
delete pContourGf;
continue;
}
Mat roiSrc(src,roi);
cv::Scalar avg=mean(roiSrc);
double avgSum=avg.val[0]+avg.val[1]+avg.val[2];
double bp=avg.val[0]/avgSum;
double gp=avg.val[1]/avgSum;
double rp=avg.val[2]/avgSum;
pContourGf->setColorPercentage(rp,gp,bp);
pContourGf->setContourGrayValue(avgSum/3);
PairTExtend v ;
v.first = &pContour;
v.second = pContourGf;
vectorPT.push_back(v);
}
}
void ContourSelector::SelectObj(const VectorPT& vectorPT1,
VectorPT& vectorPT2,
const vector<int>&indices)
{
int count=vectorPT1.size();
for (int i=0;i<indices.size();++i)
{
int index=indices[i];
if (index>=0&&index<count)
{
vectorPT2.push_back(vectorPT1[index]);
}
}
}
void ContourSelector::SelectContour(VectorPT& vectorPT1,
VectorPT& vectorPT2,
double minValue,
double maxValue,
GF_FLAG gf)
{
VectorPT::iterator it=vectorPT1.begin();
for (;it!=vectorPT1.end();++it)
{
ContourGfExtend *pContourGf=it->second;
double gfValue=0.0;
switch (gf)
{
case Perimeter:
{
gfValue = pContourGf->getContourPerimeter();
}
break;
case Area:
{
gfValue = pContourGf->getContourArea();
}
break;
case CenterX:
{
gfValue = pContourGf->getContourCenter().x;
}
break;
case CenterY:
{
gfValue = pContourGf->getContourCenter().y;
}
break;
case LeftTopX:
{
gfValue = pContourGf->getLeftTop().x;
}
break;
case LeftTopY:
{
gfValue = pContourGf->getLeftTop().y;
}
break;
case RightBottomX:
{
gfValue = pContourGf->getRightBottom().x;
}
break;
case RightBottomY:
{
gfValue = pContourGf->getRightBottom().y;
}
break;
case AspectRatio:
{
gfValue=pContourGf->getContourAspectRatio();
}
break;
case Rectangularity:
{
gfValue=pContourGf->getContourRectangularity();
}
break;
//�Ҷ�ֵ
case GrayValue:
{
gfValue=pContourGf->getContourGrayValue();
}
break;
case Width:
{
gfValue=pContourGf->getSize().width;
}
break;
case Height:
{
gfValue=pContourGf->getSize().height;
}
break;
case RedPercentage:
{
gfValue=pContourGf->getColorPercentage(0);
}
break;
case GreenPercentage:
{
gfValue=pContourGf->getColorPercentage(1);
}
break;
case BluePercentage:
{
gfValue=pContourGf->getColorPercentage(2);
}
break;
}
if (gfValue>=minValue&&gfValue<=maxValue)
{
PairTExtend pt;
pt.first=it->first;
pt.second=it->second;
vectorPT2.push_back(pt);
}
}
}
@@ -0,0 +1,38 @@
#ifndef __CONTOURSELECTOR_H__
#define __CONTOURSELECTOR_H__
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <vector>
#include "ContourGfExtend.h"
using namespace std;
using namespace cv;
class ContourSelector
{
public:
typedef pair<vector<cv::Point>*,ContourGfExtend*> PairTExtend;
typedef vector<PairTExtend> VectorPT;
typedef vector<vector<cv::Point> > ContoursTExtend;
typedef vector<cv::Vec4i> HierarchyTExtend;
public:
ContourSelector();
virtual ~ContourSelector();
public:
void CalContoursGf(Mat & src,
ContoursTExtend & contours,
VectorPT &vectorPT);
void SelectObj(const VectorPT& vectorPT1,
VectorPT& vectorPT2,
const vector<int>&indices);
void SelectContour(VectorPT& vectorPT1,
VectorPT& vectorPT2,
double minValue,
double maxValue,
GF_FLAG gf);
};
#endif
@@ -0,0 +1,61 @@
#include "ImageLocation.h"
ImageLocation::ImageLocation()
{
score=0;
}
ImageLocation::~ImageLocation()
{
}
double ImageLocation::GetDefinition()
{
return imgLocDryChemistry.GetDefinition();
}
int ImageLocation::GetPaperColor()
{
return imgLocDryChemistry.GetLocatorColor();
}
int ImageLocation::GetPaperItems()
{
return imgLocDryChemistry.GetPaperItems();//14,12,11,,,,
}
int ImageLocation::GetPaperLocLength()
{
return imgLocDryChemistry.GetLocatorType();//2/3/0
}
Mat& ImageLocation::GetPaperImg()
{
return imgLocDryChemistry.GetPaperImg();
}
cv::Mat ImageLocation::LocationProc(cv::Mat& image, cv::Rect roi, bool bDraw,cv::Scalar& paperParams,
cv::Scalar& wbAvgRgb, std::vector<cv::Point>& roiPositions, cv::Mat& rotRoiImage)
{
score=0;
roiPositions.clear();
int err=-1;
cv::Mat r = imgLocDryChemistry.GetPosition(image, roi, bDraw, wbAvgRgb,
roiPositions);
err = imgLocDryChemistry.GetLastError();
wbAvgRgb.val[3] = err;
if (err == 0) {
paperParams.val[0] = imgLocDryChemistry.GetLocatorColor();
paperParams.val[1] = imgLocDryChemistry.GetLocatorType();
paperParams.val[2] = imgLocDryChemistry.GetPaperItems();
cv::Mat(image,roi).copyTo(rotRoiImage);
score= imgLocDryChemistry.GetScore();
}
return r;
}
@@ -0,0 +1,55 @@
#ifndef __IMAGELOCATION_H__
#define __IMAGELOCATION_H__
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <map>
#include <vector>
#include "ImgLocateDryChemistry.h"
class ImageLocation
{
public:
ImageLocation();
virtual ~ImageLocation();
private:
ImgLocateDryChemistry imgLocDryChemistry;
int score;
public:
/*
*be just used for all
*/
Mat& GetPaperImg();
/*
*be just used for all
*/
double GetDefinition();
/*
*be just used for ImageLocation14i
*/
int GetPaperColor();//RED, GREEN,BLUE
int GetPaperItems();//14,12,11,10
int GetPaperLocLength();//2/3/0
/************************************************************************/
/*get the score of the location */
/************************************************************************/
int GetScore()
{
return score;
}
public:
cv::Mat LocationProc(cv::Mat& image, cv::Rect roi, bool bDraw,cv::Scalar& paperParams,
cv::Scalar& wbAvgRgb, std::vector<cv::Point>& roiPositions, cv::Mat& rotRoiImage);
};
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,168 @@
#pragma once
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <vector>
#include "ContourSelector.h"
using namespace std;
using namespace cv;
class PaperConfig {
public:
PaperConfig();
PaperConfig(int pst,int i, int s, double f, double st, double d, double r, double l);
virtual ~PaperConfig();
public:
int itemsNum;
int paperSpatialType;
int slicesNum;
double firstPos; //real first postion == firstPos * avg slice's width
double step;
double distTimes;//(distance of 2 center points ) % ( height of paper) such as 21.2 (106.5 % 5 )
double whRRatio;//big locator : 2 / 3
double whLRatio;//small locator : 0.6
void Init();
};
class ImgLocateDryChemistry
{
public:
ImgLocateDryChemistry();
~ImgLocateDryChemistry();
private:
enum BlockItems{
BI_14 = 14,
BI_13 = 13,
BI_12 = 12,
BI_11 = 11,
BI_10 = 10,
BI_9 = 9,
BI_8 = 8,
BI_7 = 7,
BI_6 = 6,
BI_5 = 5,
BI_4 = 4,
};
enum BigLocatorType{
BLT_SHORT = 0,//10mm
BLT_LONG = 1,//15mm
};
//locator color: Red, Green, Blue
enum LocatorColorClassification{
LCC_UNKNOWN = 0,
LCC_RED = 1,
LCC_GREEN = 2,
LCC_BLUE = 3,
};
enum PaperSpatialType{
PST_UNKNOWN = 0,
PST_14_S = 1, // ON
PST_14_L = 2, // ON
PST_13_S = 3,
PST_13_L = 4,
PST_12_S = 5, // ON
PST_12_L = 6,
PST_11_S = 7,
PST_11_L = 8, // ON
PST_10_S = 9, // ON
PST_10_L = 10,
PST_9_S = 11,
PST_9_L = 12, // ON
PST_8_S = 13, // ON
PST_8_L = 14,
PST_7_S = 15,
PST_7_L = 16, // ON
PST_6_S = 17, // ON
PST_6_L = 18,
PST_5_S = 19,
PST_5_L = 20, // ON
PST_4_S = 21, // ON
PST_4_L = 22,
};
Mat src; /*保存原始图片 */
Mat roiSrc; /*由SRC裁剪而来,大小ROI */
Mat hsv; /*由rgb convert而来 */
Mat rgb; /*由roiSrc convert而来 */
Mat paperImg; /*定位出的进一步 裁剪后的图片 */
int recode; /* */
int score; /* */
int locatorColor; /*定位块颜色类型 1: green 2:red 3:blue */
int paperItems;
int paperSpatialType;/*试纸类型*/
// int expectedPaperType; /* */
double avgDist; /* */
double totalDist; /* */
double distTimes; /* */
bool bRot; /* */
double topDefinitionValue;/* */
ContourSelector cs; /* 轮廓选择器 */
ContourSelector::VectorPT vecPt; /* */
ContourSelector::PairTExtend lRectContour; /* 左定位块外包轮廓 */
ContourSelector::PairTExtend rRectContour; /* */
cv::RotatedRect lRotRect; /* 左定位块 旋转矩形 */
cv::RotatedRect rRotRect; /* */
int lRotRectWidth; /* */
int rRotRectWidth; /* */
int lRotRectHeight; /* */
int rRotRectHeight; /* */
int paperHeight; /* */
map<int, PaperConfig*> mapPaperConfig;
PaperConfig* paperConfig;
public:
Mat& GetPosition(Mat& s, cv::Rect& roi, bool bDraw, cv::Scalar& wbAvgRgb, vector<cv::Point>&roiPositions);
int GetLastError() { return recode; }
Mat& GetPaperImg() { return paperImg; }
int GetLocatorColor() { return locatorColor; }
int GetLocatorType();
int GetPaperItems();
double GetDefinition() { return topDefinitionValue; }
int GetPaperHeight() { return paperHeight; }
int GetScore() { return score; }
private:
int analyseLocatorColor(Mat& sImg, double& slope);
bool analyseDefinition(Mat& sImg , cv::Rect& roi);
Mat& warpAffineImg(Mat &img);
// bool verifyDistance();
int verifyPaperConfig(double distRadio,double areaRadio);
void initPaperConfig();
void savePaperImg();
bool calGrayImgDefinition(Mat &img,double m,int u);
double calImgDefinition(Mat &img, double &diff, double &avg, int &lowLines, int &upLines);
int readLocatorColor(ContourGfExtend* pContourGfE);//0: green 1:red 2:blue -1:other
void releaseContoursGf(ContourSelector::VectorPT& vectorPt, int code = 0);
int processImage(Mat& sImg, Mat& b, Mat&v, cv::Rect& roi, bool useSThres, bool bDraw, cv::Scalar& wbAvgRgb, vector<cv::Point>&roiPositions);
};
+25
View File
@@ -0,0 +1,25 @@
//
// ImgResult.h
// XuXuCheck
//
// Created by henseycai on 2025/6/26.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface ImgResult : NSObject
@property UIImage* srcImg;
@property UIImage* roiImg;
//@property NSMutableArray<NSNumber*> *paperWbValue;
//@property NSMutableArray<NSNumber*> *paperTypeDesc;
@property NSMutableArray *paperWbValue;
@property NSMutableArray *paperTypeDesc;
@property NSMutableArray *paperAnalResults;
@end
NS_ASSUME_NONNULL_END
+42
View File
@@ -0,0 +1,42 @@
//
// ImgResult.m
// XuXuCheck
//
// Created by henseycai on 2025/6/26.
//
#import "ImgResult.h"
NS_ASSUME_NONNULL_BEGIN
@implementation ImgResult
- (instancetype)init{
self = [super init];
if(self){
_paperWbValue = [NSMutableArray arrayWithObjects:@[@0,@0,@0,@0], nil];
_paperTypeDesc = [NSMutableArray arrayWithObjects:@[@0,@0,@0,@0], nil];
_paperAnalResults = [NSMutableArray arrayWithObjects:
[NSMutableArray arrayWithObjects:@0.0, @0.0, @0.0, @0.0, @0.0, @0.0,nil],//1
[NSMutableArray arrayWithObjects:@0.0, @0.0, @0.0, @0.0, @0.0, @0.0,nil],//1
[NSMutableArray arrayWithObjects:@0.0, @0.0, @0.0, @0.0, @0.0, @0.0,nil],//1
[NSMutableArray arrayWithObjects:@0.0, @0.0, @0.0, @0.0, @0.0, @0.0,nil],//1
[NSMutableArray arrayWithObjects:@0.0, @0.0, @0.0, @0.0, @0.0, @0.0,nil],//1
[NSMutableArray arrayWithObjects:@0.0, @0.0, @0.0, @0.0, @0.0, @0.0,nil],//1
[NSMutableArray arrayWithObjects:@0.0, @0.0, @0.0, @0.0, @0.0, @0.0,nil],//1
[NSMutableArray arrayWithObjects:@0.0, @0.0, @0.0, @0.0, @0.0, @0.0,nil],//1
[NSMutableArray arrayWithObjects:@0.0, @0.0, @0.0, @0.0, @0.0, @0.0,nil],//1
[NSMutableArray arrayWithObjects:@0.0, @0.0, @0.0, @0.0, @0.0, @0.0,nil],//10
[NSMutableArray arrayWithObjects:@0.0, @0.0, @0.0, @0.0, @0.0, @0.0,nil],//1
[NSMutableArray arrayWithObjects:@0.0, @0.0, @0.0, @0.0, @0.0, @0.0,nil],//1
[NSMutableArray arrayWithObjects:@0.0, @0.0, @0.0, @0.0, @0.0, @0.0,nil],//1
[NSMutableArray arrayWithObjects:@0.0, @0.0, @0.0, @0.0, @0.0, @0.0,nil],//1
[NSMutableArray arrayWithObjects:@0.0, @0.0, @0.0, @0.0, @0.0, @0.0,nil],//1
[NSMutableArray arrayWithObjects:@0.0, @0.0, @0.0, @0.0, @0.0, @0.0,nil]//16
,nil];
}
return self;
}
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,20 @@
//
// OpencvWrapper.h
// XuXuCheck
//
// Created by henseycai on 2025/5/26.
//
// OpenCVWrapper.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "ImgResult.h"
NS_ASSUME_NONNULL_BEGIN
@interface OpenCVWrapper : NSObject
+ (UIImage *)toGrayscale:(UIImage *)image;
+ (UIImage *)gaussianBlur:(UIImage *)image: (int)blurAmount;
+ (ImgResult *)analysisImage:(UIImage *)image;
@end
NS_ASSUME_NONNULL_END
+181
View File
@@ -0,0 +1,181 @@
//
// OpencvWrapper.m
// XuXuCheck
//
// Created by henseycai on 2025/5/26.
//
// OpenCVWrapper.mm
#import <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h>
#import <vector>
#import "ImageLocation.h"
#import "OpenCVWrapper.h"
@interface UIImage (OpenCVWrapper)
- (void)convertToMat: (cv::Mat *)pMat: (bool)alphaExists;
@end
@implementation UIImage (OpenCVWrapper)
- (void)convertToMat: (cv::Mat *)pMat: (bool)alphaExists {
if (self.imageOrientation == UIImageOrientationRight) {
UIImageToMat([UIImage imageWithCGImage:self.CGImage scale:1.0 orientation:UIImageOrientationUp], *pMat, alphaExists);
cv::rotate(*pMat, *pMat, cv::ROTATE_90_CLOCKWISE);
} else if (self.imageOrientation == UIImageOrientationLeft) {
UIImageToMat([UIImage imageWithCGImage:self.CGImage scale:1.0 orientation:UIImageOrientationUp], *pMat, alphaExists);
cv::rotate(*pMat, *pMat, cv::ROTATE_90_COUNTERCLOCKWISE);
} else {
UIImageToMat(self, *pMat, alphaExists);
if (self.imageOrientation == UIImageOrientationDown) {
cv::rotate(*pMat, *pMat, cv::ROTATE_180);
}
}
}
@end
@implementation OpenCVWrapper
ImageLocation imgLoc;
+ (UIImage *)gaussianBlur:(UIImage *)image: (int)blurAmount {
cv::Mat mat;
[image convertToMat:&mat :false];
cv::Mat blur;
mat.copyTo(blur);
cv::GaussianBlur(mat, blur, cv::Size(blurAmount, blurAmount), 0.0);
UIImage* blurImage = MatToUIImage(blur);
return blurImage;
}
+ (UIImage *)toGrayscale:(UIImage *)image {
cv::Mat mat;
[image convertToMat: &mat :false];
std::vector<cv::Point> positions;
cv::Scalar scPaperParams(0);
cv::Rect roi(0,0,mat.cols,mat.rows / 3);
cv:Scalar scalar(0);
Mat roiRotImage;
Mat srcImgMatBgra;
cvtColor(mat, srcImgMatBgra, COLOR_RGBA2BGRA); //rgba---->>rgb
Mat reImag = imgLoc.LocationProc(srcImgMatBgra, roi, true,scPaperParams,scalar, positions, roiRotImage);
cvtColor(reImag, reImag, COLOR_BGRA2RGBA);
// reImag.copyTo(retImgMat);
// roiRotImage.copyTo(roiRotImgMat);
UIImage *grayImg = MatToUIImage(reImag);
return grayImg;
// cv::Mat gray;
// if (mat.channels() > 1) {
// cv::cvtColor(mat, gray, cv::COLOR_RGB2GRAY);
// } else {
// mat.copyTo(gray);
// }
// UIImage *grayImg = MatToUIImage(gray);
// return grayImg;
}
int ITEM_SIZE = 16;
ImgResult* result = [ImgResult new];
+ (ImgResult *)analysisImage:(UIImage *)image {
cv::Mat mat;
[image convertToMat: &mat :false];
std::vector<cv::Point> positions;
cv::Scalar scPaperParams(0);
cv::Rect roi(0,0,mat.cols,mat.rows / 3);
cv:Scalar scalar(0);
Mat roiRotImage;
Mat srcImgMatBgra;
cvtColor(mat, srcImgMatBgra, COLOR_RGBA2BGRA); //rgba---->>rgb
Mat reImag = imgLoc.LocationProc(srcImgMatBgra, roi, true,scPaperParams,scalar, positions, roiRotImage);
cvtColor(reImag, reImag, COLOR_BGRA2RGBA);
// reImag.copyTo(retImgMat);
// roiRotImage.copyTo(roiRotImgMat);
result.srcImg = MatToUIImage(reImag);
// result.roiImg = MatToUIImage(roiRotImage);
result.paperWbValue[0] = [NSNumber numberWithDouble:scalar.val[0]];
result.paperWbValue[1] = [NSNumber numberWithDouble:scalar.val[1]] ;
result.paperWbValue[2] = [NSNumber numberWithDouble:scalar.val[2]] ;
result.paperWbValue[3] = [NSNumber numberWithDouble:scalar.val[3]] ;
// result.paperWbValue[0] = [NSNumber numberWithInt:imgLoc.GetPaperColor()];
// result.paperWbValue[1] = [NSNumber numberWithInt:imgLoc.GetPaperItems()] ;
// result.paperWbValue[2] = [NSNumber numberWithInt:imgLoc.GetPaperLocLength()] ;
// result.paperWbValue[3] = [NSNumber numberWithInt:scalar.val[3]] ;
result.paperTypeDesc[0] = @0.0;
result.paperTypeDesc[1] = @0.0;
result.paperTypeDesc[2] = @0.0;
result.paperTypeDesc[3] = @0.0;
if(scalar.val[3] == 0){
result.paperTypeDesc[0] = [NSNumber numberWithDouble:scPaperParams.val[0]];
result.paperTypeDesc[1] = [NSNumber numberWithDouble:scPaperParams.val[1]] ;
result.paperTypeDesc[2] = [NSNumber numberWithDouble:scPaperParams.val[2]] ;
result.paperTypeDesc[3] = [NSNumber numberWithDouble:scPaperParams.val[3]] ;
result.paperTypeDesc[0] = [NSNumber numberWithInt:imgLoc.GetPaperColor()];
result.paperTypeDesc[1] = [NSNumber numberWithInt:imgLoc.GetPaperItems()] ;
result.paperTypeDesc[2] = [NSNumber numberWithInt:imgLoc.GetPaperLocLength()] ;
Mat rgbMat;
cvtColor(roiRotImage, rgbMat, COLOR_BGRA2RGB);
Mat mediaMat;
cv::medianBlur(rgbMat, mediaMat, 3);
Mat meanMat;
cv::blur(mediaMat, meanMat, cv::Size(3, 3));
int xs[ITEM_SIZE];
int ys[ITEM_SIZE];
int totalPt = 0;//ITEM_SIZE;
for (int i = 0; i < ITEM_SIZE; ++i) {
if (i >= positions.size())
break;
xs[i] = positions[i].x;
ys[i] = positions[i].y;
if (xs[i] > 0 && ys[i] > 0) {
totalPt += 1;
}
}
for (int i = 0; i < totalPt - 1; ++i) {
cv::Point pt(xs[i], ys[i]);
cv::Rect roi(pt.x - 5, pt.y - 5, 10, 10);
Mat roiRgbMat(meanMat, roi);
Mat hsvMat, labMat;
cvtColor(roiRgbMat, hsvMat, COLOR_RGB2HSV);
cvtColor(roiRgbMat, labMat, COLOR_RGB2Lab);
Scalar avgHsv = mean(hsvMat);
Scalar avgLab = mean(labMat);
result.paperAnalResults[i][0] = [NSNumber numberWithDouble:avgHsv.val[0]] ;
result.paperAnalResults[i][1] = [NSNumber numberWithDouble:avgHsv.val[1]] ;
result.paperAnalResults[i][2] = [NSNumber numberWithDouble:avgHsv.val[2]] ;
result.paperAnalResults[i][3] = [NSNumber numberWithDouble:avgLab.val[0]] ;
result.paperAnalResults[i][4] = [NSNumber numberWithDouble:avgLab.val[1]] ;
result.paperAnalResults[i][5] = [NSNumber numberWithDouble:avgLab.val[2]] ;
}
}
return result;
}
@end
@@ -0,0 +1,16 @@
//
// PrefixHeader.pch
// XuXuCheck
//
// Created by henseycai on 2025/5/26.
//
#ifndef PrefixHeader_pch
#define PrefixHeader_pch
// Include any system framework and library headers here that should be included in all compilation units.
// You will also need to set the Prefix Header build setting of one or more of your targets to reference this file.
#ifdef __cplusplus
#include <opencv2/opencv.hpp>
#endif
#endif /* PrefixHeader_pch */
+155
View File
@@ -0,0 +1,155 @@
/*
* Utils.cpp
*
* Created on: 2018-1-14
* Author: neo
*/
#include "Utils.h"
#define TV0 8
#define TV1 5
void Whitebalance(Mat src,Point wbPt)
{
Rect roi(wbPt.x-5,wbPt.y-5,10,10);
Mat wbMat(src,roi);
cv::Scalar avgRgb = mean(wbMat);
// double d1 = avgRgb.val[0] - avgRgb.val[2];
// double d2= avgRgb.val[1]-avgRgb.val[2];
// double d3 = abs(avgRgb.val[0] - avgRgb.val[1]);
//
// if (d1 >= TV0 && d2 >= TV0 && d3 <= TV1)
// {
// avgRgb = mean(src);
// }
double scale = (avgRgb.val[0] + avgRgb.val[1] + avgRgb.val[2]) / 3;
Mat r,g,b;
extractChannel(src, r, 0);
extractChannel(src, g, 1);
extractChannel(src, b, 2);
convertScaleAbs(r, r, scale / avgRgb.val[0], 0.0);
convertScaleAbs(g, g, scale / avgRgb.val[1], 0.0);
convertScaleAbs(b, b, scale / avgRgb.val[2], 0.0);
vector<Mat> mv;
mv.push_back(r);
mv.push_back(g);
mv.push_back(b);
merge(mv, src);
}
//Perfect reflection color correction
void WhitebalancePR(Mat src,Point wbPt)
{
// 通道分离
vector<cv::Mat> Channel;
cv::split(src, Channel);
// 定义参数
int row = src.rows;
int col = src.cols;
int RGBSum[766] = { 0 };
uchar maxR, maxG, maxB;
for (int i = 0; i < row; ++i)
{
uchar *b = Channel[0].ptr<uchar>(i);
uchar *g = Channel[1].ptr<uchar>(i);
uchar *r = Channel[2].ptr<uchar>(i);
for (int j = 0; j < col; ++j)
{
int sum = b[j] + g[j] + r[j];
RGBSum[sum]++;
maxB = max(maxB, b[j]);
maxG = max(maxG, g[j]);
maxR = max(maxR, r[j]);
}
}
int T = 0;
int num = 0;
int K = static_cast<int>(row * col * 0.1);
for (int i = 765; i >= 0; --i)
{
num += RGBSum[i];
if (num > K)
{
T = i;
break;
}
}
double Bm = 0.0, Gm = 0.0, Rm = 0.0;
int count = 0;
for (int i = 0; i < row; ++i)
{
uchar *b = Channel[0].ptr<uchar>(i);
uchar *g = Channel[1].ptr<uchar>(i);
uchar *r = Channel[2].ptr<uchar>(i);
for (int j = 0; j < col; ++j)
{
int sum = b[j] + g[j] + r[j];
if (sum > T)
{
Bm += b[j];
Gm += g[j];
Rm += r[j];
count++;
}
}
}
Bm /= count;
Gm /= count;
Rm /= count;
Channel[0] *= maxB / Bm;
Channel[1] *= maxG / Gm;
Channel[2] *= maxR / Rm;
cv::merge(Channel, src);
}
bool pred(const std::pair<int, int>& lhs,const std::pair<int, int>& rhs)
{
return lhs.second < rhs.second;
}
int GetHModal(Mat h)
{
uchar *pData=h.data;
int step=h.step;
//key:h,value:counter
map<int,int> mapVals;
map<int,int>::iterator it;
for (int y=0;y<h.rows;++y){
for(int x=0;x<h.cols;++x){
uchar val=pData[y*step+x];
it=mapVals.find(val);
if (it!=mapVals.end()){
mapVals[val]++;
}else{
mapVals.insert(make_pair(val,1));
}
}
}
it=std::max_element(mapVals.begin(),mapVals.end(),pred);
int maxHval=it->first;
int sum=0;
int pc=0;
for (it=mapVals.begin();it!=mapVals.end();++it){
int h=it->first;
int c=it->second;
if (h>=(maxHval-2)&&
h<=(maxHval+2)){
sum+=h*c;
pc+=c;
}
}
return (sum/pc);
}
+20
View File
@@ -0,0 +1,20 @@
/*
* Utils.h
*
* Created on: 2018-1-14
* Author: neo
*/
#ifndef UTILS_H_
#define UTILS_H_
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <vector>
using namespace cv;
using namespace std;
void Whitebalance(Mat src,Point wbPt);
bool pred(const std::pair<int, int>& lhs,const std::pair<int, int>& rhs);
int GetHModal(Mat h);
#endif /* UTILS_H_ */