如何检测图像中的光斑? [英] How to detect light spots in image?

查看:754
本文介绍了如何检测图像中的光斑?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了从某个地理区域检测 inselberg ,我已经下载了地形图(浮雕和磁偏角).磁偏角图像似乎最适合该任务.

In order to detect inselbergs from a geographic region, I've downloaded topographic imagery (relief and declivity) from that region. The declivity images seems best suited for the task.

应用高斯模糊(或使用ImageMagick进行更快的普通模糊处理)后,图像似乎可以自动检测了.

After applying a gaussian blur (or a much faster common blur, with ImageMagick), the image seems ready for automatic detection.

现在,我想知道最好/最快的方法来检测黑色背景上的这些白色污渍.我的第一个想法是使用一个简单的功能(无外部库),该功能类似于绘图程序的桶油漆"功能,计算比预定义阈值更轻的对象的面积.问题是:由于图像非常大,为5400x3600像素,因此通常的递归函数可能会导致堆栈溢出,尤其是在如下所示的大山脉上.

Now I'm wondering the best/fastest way to detect these white stains on the black background. My first idea is to use a simple function (no external library) that works like the "bucket paint" function of drawing programs, calculating the area of an object lighter than a predefined threshold. Problem is: since the images are quite large, 5400x3600 pixels, the usual recursion function will probably cause a stack overflow, specially on massive mountain ranges like below.

那么,有什么建议吗?我认为理想的语言可能是C ++(也许是JavaScript).我不习惯Python.使用像OpenCV这样的库可能会变得更加容易,但是问题可能太琐碎了,无法请求外部库(包括隐含的学习曲线).

So, any suggestions? I think the ideal language may be C++ (perhaps JavaScript). I'm not used to Python. Maybe it gets easier using a library like OpenCV, but maybe the problem is too trivial to ask for an external library (including the implied learning curve).

TIFF图像来自此处(我可以将其转换为其他格式进行处理).示例图像位于17S42的四边形("Declividade"选项),靠近坐标18°S 41°W.在中间的图像(上)中,每个白球"都是一个inselberg.精度取决于所选的灰度阈值.

The TIFF images came from here (I can convert them to other format for processing). The example image is at the 17S42 quadricule ("Declividade" option), close to the coordinates 18°S 41°W. In the middle image (above), each "white ball" is an inselberg. The precision will depend on the chosen threshold of gray.

推荐答案

简而言之,使用您的方法得出的尺寸估计值很差.无论选择哪种阈值,模糊区域的边缘都不对应于要测量的Inselberg的边缘. 相反,我建议您按照以下食谱操作.我为此在Python中使用 DIPlib (免责声明:我是作者). Python绑定是C ++库上的一薄层,将下面的Python代码转换为C ++是相当简单的(对我来说,用Python交互式开发它更容易).

In short, you'll get a very poor estimate of size using your method. The edges of the blurred regions, no matter what threshold you pick, do not correspond to the edges of the inselbergs you want to measure. Instead, I suggest you follow the recipe below. I'm using DIPlib in Python for this (disclaimer: I'm an author). The Python bindings are a thin layer on the C++ library, it's fairly simple to translate the Python code below to C++ (it's just easier for me to develop it interactively in Python).

我从您提供的链接(而不是磁偏角)中下载了原始高度数据. DIPlib可以直接读取浮点值的TIFF文件,因此不需要任何特殊转换.我裁切了一个与OP用于此演示的区域类似的区域,但是没有理由不将该方法应用于整个图块.

I downloaded the original height data from the link you provided (rather than the declivity). DIPlib can directly read floating-point valued TIFF files, so there is no need for any special conversion. I cropped a region similar to the one used by OP for this demo, but there is no reason to not apply the method to the whole tile.

import PyDIP as dip

height = dip.ImageRead('17S42_ZN.tif')
height.SetPixelSize(0.000278, 'rad')  # not really radian, but we don't have degrees
height = height[3049:3684, 2895:3513];

代码还根据TIFF文件中的数据设置像素大小(使用弧度单位,因为DIPlib不做度数.)

The code also sets the pixel size according to data in the TIFF file (using units of radian, because DIPlib doesn't do degrees).

接下来,我应用具有特定直径(25像素)的礼帽式过滤器.这样可以隔离直径小于或等于25个像素的所有峰.根据您认为的inselberg的最大宽度来调整此大小.

Next, I apply the top-hat filter with a specific diameter (25 pixels). This isolates all peaks with a diameter of 25 pixels or less. Adjust this size according to what you think the maximum width an inselberg should be.

local_height = dip.Tophat(height, 25)

实际上,结果是局部高度,该高度是某个基线之上的高度,该高度由过滤器的尺寸决定.

In effect, the result is a local height, the height above some baseline determined by the size of the filter.

接下来,我应用一个磁滞阈值(双阈值).这将产生一个二进制图像,其阈值位于基线以上100m处,其中地形超出该基线以上200m以上.也就是说,我决定一个inselberg线至少应比基线高200m,但要在100m处将它们切掉.在这个高度,我们将测量大小(面积).再次调整您认为合适的阈值.

Next, I apply a hysteresis threshold (double threshold). This yields a binary image, thresholded at 100m above the baseline, where the terrain goes above 200m above that baseline. That is, I decided that an inselberg should be at least 200m above the baseline, but cut each of them off at 100m. At this height we'll be measuring the size (area). Again, adjust the thresholds as you see fit.

inselbergs = dip.HysteresisThreshold(local_height, 100, 200)

现在剩下的就是测量我们发现的区域:

Now all that is left is measuring the regions we found:

labels = dip.Label(inselbergs)
result = dip.MeasurementTool.Measure(labels, features=['Size', 'Center'])
print(result)

这将输出:

   |       Size |                  Center | 
-- | ---------- | ----------------------- | 
   |            |       dim0 |       dim1 | 
   |     (rad²) |      (rad) |      (rad) | 
-- | ---------- | ---------- | ---------- | 
 1 |  1.863e-05 |     0.1514 |    0.01798 | 
 2 |  4.220e-05 |     0.1376 |    0.02080 | 
 3 |  6.214e-05 |    0.09849 |    0.04429 | 
 4 |  6.492e-06 |     0.1282 |    0.04710 | 
 5 |  3.022e-05 |     0.1354 |    0.04925 | 
 6 |  4.274e-05 |     0.1510 |    0.05420 | 
 7 |  2.218e-05 |     0.1228 |    0.05802 | 
 8 |  1.932e-05 |     0.1420 |    0.05689 | 
 9 |  7.690e-05 |     0.1493 |    0.06960 | 
10 |  3.285e-05 |     0.1120 |    0.07089 | 
11 |  5.248e-05 |     0.1389 |    0.07851 | 
12 |  4.637e-05 |     0.1096 |    0.09016 | 
13 |  3.787e-05 |    0.07146 |     0.1012 | 
14 |  2.133e-05 |    0.09046 |    0.09908 | 
15 |  3.895e-05 |    0.08553 |     0.1064 | 
16 |  3.308e-05 |    0.09972 |     0.1143 | 
17 |  3.277e-05 |    0.05312 |     0.1174 | 
18 |  2.581e-05 |    0.07298 |     0.1167 | 
19 |  1.955e-05 |    0.04038 |     0.1304 | 
20 |  4.846e-05 |    0.03657 |     0.1448 | 

(请记住,它说的是弧度",它的确是度.)以平方度为单位的面积有点奇怪,但是您可以将其转换为平方米,因为您知道地球上的位置.实际上,在计算之前将像素大小转换为米可能更容易.

(Remember, where it says 'rad' it is really degrees.) An area in square degrees is a bit weird, but you can convert this to square meters since you know the location on the globe. It might in fact be easier to translate the pixel sizes to meters before the computations.

此处为中心"指定的值是相对于左上角像素的,如果我们没有裁剪图块的开始,我们可能已经添加了图块的坐标(可以从相应的位置获得) TIFF文件中的代码):(-42.0,-17.0).

The values given here for 'Center' are with respect to the top-left pixel, if we hadn't cropped the tile to begin with, we could have added the coordinates for the tile (as can be obtained from the corresponding tag in the TIFF file): (-42.0, -17.0).

在C ++中,代码应如下所示:

In C++ the code should look like this:

#include <diplib/simple_file_io.h>
#include <diplib/morphology.h>
#include <diplib/segmentation.h>
#include <diplib/regions.h>
#include <diplib/measurement.h>

//...

dip::Image height = dip::ImageRead("17S42_ZN.tif");
height.SetPixelSize(0.000278 * dip::Units::Radian());
height = height.At(dip::Range(3049, 3684), dip::Range(2895, 3513));

dip::Image local_height = dip::Tophat(height, 25);

dip::Image inselbergs = dip::HysteresisThreshold(local_height, 100, 200);

dip::Image labels = dip::Label(inselbergs);
dip::MeasurementTool measurementTool;
dip::Measurement result = measurementTool.Measure(labels, {}, {"Size", "Center"});
std::cout << result;

这篇关于如何检测图像中的光斑?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆