## 斑点检测

2014-09-28 16:39  ☆Ronny丶  阅读(27521)  评论(12编辑  收藏  举报

# 1. 什么是斑点

1. 基于求导的微分方法，这类的方法称为微分检测器；
2. 基于局部极值的分水岭算法。

## 2.1 基本原理

$$G(x,y;\sigma) = \frac{1}{2\pi\sigma^2}exp(-\frac{x^2+y^2}{2\sigma^2})$$

$$\nabla^2g = \frac{\partial^2g}{\partial x^2}+\frac{\partial^2g}{\partial y^2}$$

$$\nabla^2_{norm}=\sigma^2\nabla^2g=\sigma^2(\frac{\partial^2g}{\partial x^2}+\frac{\partial^2g}{\partial y^2}) = -\frac{1}{2\pi\sigma^2}[1-\frac{x^2+y^2}{\sigma^2}]\cdot exp(-\frac{x^2+y^2}{2\sigma^2})$$

## 2.2 LOG原理解释

$$L(x,y;\sigma) = f(x,y) * G(x,y;\sigma)$$

$$\nabla^2 = \frac{\partial^2L}{\partial x^2}+\frac{\partial^2L}{\partial y^2}$$

$$\nabla^2[G(x,y)*f(x,y)] = \nabla^2[G(x,y)]*f(x,y)$$

## 2.3 LOG算子的实现

Mat Feat::getHOGKernel(Size& ksize, double sigma)
{
Mat kernel(ksize, CV_64F);
Point centPoint = Point((ksize.width -1)/2, ((ksize.height -1)/2));
// first calculate Gaussian
for (int i=0; i < kernel.rows; i++)
{
double* pData = kernel.ptr<double>(i);
for (int j = 0; j < kernel.cols; j++)
{
double param = -((i - centPoint.y) * (i - centPoint.y) + (j - centPoint.x) * (j - centPoint.x)) / (2*sigma*sigma);
pData[j] = exp(param);
}
}
double maxValue;
minMaxLoc(kernel, NULL, &maxValue);
for (int i=0; i < kernel.rows; i++)
{
double* pData = kernel.ptr<double>(i);
for (int j = 0; j < kernel.cols; j++)
{
if (pData[j] < EPS* maxValue)
{
pData[j] = 0;
}
}
}

double sumKernel = sum(kernel)[0];
if (sumKernel != 0)
{
kernel = kernel / sumKernel;
}
// now calculate Laplacian
for (int i=0; i < kernel.rows; i++)
{
double* pData = kernel.ptr<double>(i);
for (int j = 0; j < kernel.cols; j++)
{
double addition = ((i - centPoint.y) * (i - centPoint.y) + (j - centPoint.x) * (j - centPoint.x) - 2*sigma*sigma)/(sigma*sigma*sigma*sigma);
}
}
// make the filter sum to zero
sumKernel = sum(kernel)[0];
kernel -= (sumKernel/(ksize.width  * ksize.height));

return kernel;
}

## 2.4 多尺度检测

$$\nabla^2_{norm} = -\frac{1}{2\pi\sigma^2}[1-\frac{x^2+y^2}{\sigma^2}]\cdot exp(-\frac{x^2+y^2}{2\sigma^2})$$

$$\frac{\partial(\nabla^2_{norm})}{\partial\sigma} = 0$$

$$(x^2+y^2-2\sigma^2)\cdot exp(-\frac{(x^2+y^2)}{2\sigma^2})$$

$$r^2-2\sigma^2=0$$

# 3 OpenCV进行斑点检测

opencv中检测Blobs的类为SimpleBlobDetector，这个类在opencv中的定义如下：

class SimpleBlobDetector : public FeatureDetector
{
public:
struct Params
{
Params();
float thresholdStep;
float minThreshold;
float maxThreshold;
size_t minRepeatability;
float minDistBetweenBlobs;

bool filterByColor;
uchar blobColor;

bool filterByArea;
float minArea, maxArea;

bool filterByCircularity;
float minCircularity, maxCircularity;

bool filterByInertia;
float minInertiaRatio, maxInertiaRatio;

bool filterByConvexity;
float minConvexity, maxConvexity;
};

SimpleBlobDetector(const SimpleBlobDetector::Params &parameters = SimpleBlobDetector::Params());

protected:
...
};

1. 对[minThreshold,maxThreshold)区间，以thresholdStep为间隔，做多次二值化。
2. 对每张二值图片，使用findContours()提取连通域并计算每一个连通域的中心。
3. 根据2得到的中心，全部放在一起。一些很接近的点［由theminDistBetweenBlobs控制多少才算接近］被归为一个group,对应一个bolb特征..
4. 从3得到的那些点,估计最后的blob特征和相应半径，并以key points返回。

int main(int argc, char** argv)
{
vector<KeyPoint> keyPoints;
SimpleBlobDetector::Params params;

SimpleBlobDetector blobDetect(params);
blobDetect.create("SimpleBlob");
blobDetect.detect(image, keyPoints);
cout << keyPoints.size() << endl;
drawKeypoints(image, keyPoints, image, Scalar(255,0,0));

namedWindow("blobs");
imshow("blobs", image);
waitKey();
return 0;
}

# 4. 扩展阅读

$$D(x,y,\sigma) = (G(x,y,k\sigma) – G(x,y,\sigma))*I(x,y) = L(x,y,k\sigma)-L(x,y,\sigma)$$

DOG可以看作为LOG的一个近似，但是它比LOG的效率更高。

$$G(x,y)=\mathcal{A}\cdot exp(-[(ax^2+2bxy+cy^2)])$$

$$a = \frac{cos^2\theta}{2\sigma^2_x}+\frac{sin^2\theta}{2\sigma^2_y}, b=-\frac{sin2\theta}{2\sigma^2_x}+\frac{sin2\theta}{4\sigma^2_y},c = \frac{sin^2\theta}{2\sigma^2_x}+\frac{cos^2\theta}{2\sigma_y^2}$$

# 5. 参考资料

1. 《现代数字图像 -- 处理技术提高与应用案例详解》

2. 《图像局部不变性特征与描述》

3.  Lindeberg, T. Feature Detection with Automatic Scale Selection

4. Hui Kong. A Generalized Laplacian Of Gaussian Filter for Blob Detection and Its Applications.