比较简单,已基本满足我的需求。留了两个参数可调,根据不同需求来调节,我的是调的20,0.51就够了。测试了几百张图OK。
/*
* sparseDense.h
* 实验开始,第一次人工停止对焦的图片,判断样液浓度是否不符合要求
* 是否太密,太密软件会报警要求稀释样液。
* Created on: 2019年10月10日
* Author: root wangdan
* version:V1.0.0
*/
#ifndef SRC_SPARSEDENSE_H_
#define SRC_SPARSEDENSE_H_
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
class sparseDense {
public:
sparseDense();
virtual ~sparseDense();
/*input:inputbgra--输入图像,人工“停止对焦”那一刻的图像
* windowsize-局部距离值的窗口,越小,疏密判断越准确,但不能太小,建议20
* thresh-----太密的局部区域占所有局部区域的比,大于此值报警太密,建议0.55
*output:-1--------输入图像有问题,空图
* 0--------浓度符合要求(不密)。
* 1--------浓度过密,软件报警。
* */
int calculateSparseDense(unsigned char *image_pointer,int windowsize,double &thresh);
private:
int imgrows;
int imgcols;
Mat inputbwimg;
int lastrows;
int lastcols;
//距离图每20x20计算一个最频繁的距离值,局部距离值的窗口
//越小,疏密判断越准确,但不能太小,>=10;建议=20
int winsize;
//winsizexwinsize窗口内,最频繁的距离值>distanthresh,那么此窗口不密;否则过密
//越大表示希望仪器希望接受的程度偏稀;越小表示仪器希望接受更密程度 distanthresh=winsize
int distanthresh;
int fillholeCalcDistance(vector<int> &maxvalues);
};
#endif /* SRC_SPARSEDENSE_H_ */
#include "sparseDense.h"
sparseDense::sparseDense() {
// TODO Auto-generated constructor stub
imgrows=2048;
imgcols=2448;
// Mat inputbwimg;
lastrows=imgrows/4;
lastcols=imgcols/4;
winsize=20;
distanthresh=winsize;
}
int sparseDense::calculateSparseDense(unsigned char *image_pointer,int windowsize,double &thresh)
{
winsize=windowsize;
distanthresh=windowsize;
if(image_pointer==NULL)
{
return -1;
}
Mat inputbgra(imgrows,imgcols,CV_8UC4);
size_t primary_size_=imgrows*imgcols*4*sizeof(uchar);
memcpy(inputbgra.data,image_pointer,primary_size_);
Mat inputgray,inputbw;
cv::cvtColor(inputbgra,inputgray,CV_BGRA2GRAY);
cv::threshold(inputgray,inputbw,120,255,THRESH_BINARY_INV);
cv::Size lastsize;
lastsize.height=lastrows;
lastsize.width=lastcols;
resize(inputbw, inputbwimg, lastsize);
vector<int> maxsvalues;
fillholeCalcDistance(maxsvalues);
int satisfynumber=0;
int localmax=maxsvalues.size();
for(int idx=0;idx!=localmax;idx++)
{
satisfynumber+=maxsvalues[idx];
}
double ratio=double(satisfynumber)/localmax;
//cout<<"ratio is: "<<ratio<<endl;
if(ratio>thresh)
{
return 1;//太密,报警
}
return 0;
}
int sparseDense::fillholeCalcDistance(vector<int> &maxvalues)
{
std::vector<std::vector<cv::Point> > contours;
std::vector<Vec4i> hierarchy;
findContours(inputbwimg, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
Mat bwlast=Mat::zeros(lastrows,lastcols,CV_8UC1);
for (int idx = 0; idx != (int)contours.size(); idx++)
{
drawContours(bwlast, contours, idx, Scalar(255), CV_FILLED);
}
//imwrite("bw.jpg",bwlast);
cv::bitwise_not(bwlast,inputbwimg);
//imwrite("bwnot.jpg",inputbwimg);
Mat distance;
distanceTransform(inputbwimg, distance, CV_DIST_L2, 3);
//距离图每25x25计算一个局部最大值,图像共有N个局部最大值
int maxsidxr=1;
while(maxsidxr*winsize<lastrows)
{
int maxsidxc=1;
while(maxsidxc*winsize<lastcols)
{
//统计当前局部方块的距离值
int numlocal=winsize*winsize;
int localdistance[numlocal]={0};
for(int r=(maxsidxr-1)*winsize;r!=maxsidxr*winsize;r++)
{
for(int c=(maxsidxc-1)*winsize;c!=maxsidxc*winsize;c++)
{
int current=int(distance.ptr<float>(r)[c]);
if(current>0 && current<numlocal)
{
localdistance[current]+=1;
}
}
}
//当前局部方块距离值主要集中在哪个区间
int maxdistsum=0,maxdist=0;
for(int idx=0;idx!=numlocal;idx++)
{
if(localdistance[idx]>maxdistsum)
{
maxdistsum=localdistance[idx];
maxdist=idx;
}
}
//当前局部最大值计算完毕
//cout<<maxdist<<" ";
//当前局部最大值如果大于阈值,证明此局部是比规定浓度还稀,符合要求;
//否则,此局部比规定浓度密,不符合要求;
int isdense=0;
int notsparse=1;
if(maxdist<distanthresh)
{
maxvalues.push_back(notsparse);
}
else
{
maxvalues.push_back(isdense);
}
//移到此行下一列方块
maxsidxc++;
}
//cout<<endl;
//列所有局部最大值计算完毕
maxsidxr++;
}
//行所有局部最大值计算完毕
return 0;
}
sparseDense::~sparseDense() {
// TODO Auto-generated destructor stub
}
测试了几百张图片,各种形状各种大小的目标的图像,发现判断还比较准确。
但是有一个前提,图像中的目标一定要比较均匀,不能图像的左下角密密麻麻而另外四分之三图像都没目标,这种极度不均匀的情况是不行的,不能作为输入图像。因为我们判断一个试管中样液是否浓或稀,一定要摇晃均匀才看整体浓或稀。反正就是目标可以有不同形状不同大小,但输入图像不能极度不均匀。输入图像整体越均匀,那么算法判断越准确。
小不点的美图。