使用Julia进行图像处理--图像表示与匹配算法

图像表示

ImageFeatures包用于确定图像的特征或描述符,并用于其他应用程序,例如对象识别,检测,图像匹配。

特征和描述符

在计算机视觉中,最有可能的是,搜索相邻元素特有的图案或特定特征。图像特征通过以一组参数表示图像来使它们易于识别,跟踪和比较,从而进行下游工作。
查看下列猫的两个不同的图像,然后尝试查找图像间相匹配的区域:
two_cat

首先进行预览,感受算法如何处理以下图像:
two_cat

从前面的结果中,可以看到该算法专注于猫的耳朵,并且相同的特征由白线相连。

快速角点检测

角点检测是从图像中提取特征的过程。
JuliaFeatures包实现了FAST( Features from accelerated segment test )函数。 FAST是一种计算效率高的角点检测器,适用于实时视频分析。可以使用fastcorners函数调用FAST算法。
由于其出色的性能,质量和简单的配置,在以下所有示例中使用FAST。 FAST通过扫描整个图像的角点来工作。它使用一个16像素的圆来评估点是否为角点。该过程还由两个附加参数配置:

  1. 连续像素数(N):用于比较圆中的像素是比候选点亮还是暗,N通常固定为12。
  2. 阈值:定义了所识别特征的数量,阈值参数设置的值越高,返回的特征越少。

在图像上多次运行FAST算法,用于查看随着阈值参数值的变化,角点数如何改变。
加载图像,调整大小并将其转换为灰度。对三个不同的阈值(例如0.15、0.25和0.35)应用fastcorners函数,并比较结果:

using Images, ImageFeatures, ImageView
img = Gray.(load("cat-3417184_640.jpg"))
img_f = Float16.(restrict(img))
new_img = Gray.(hcat(
 img_f .* (.~fastcorners(img_f, 12, 0.15)),
 img_f .* (.~fastcorners(img_f, 12, 0.25)),
 img_f .* (.~fastcorners(img_f, 12, 0.35))
))
imshow(new_img)

在前面的代码中将存储在img_f变量中的图像的数字表示乘以对快速角点函数结果的求反,从而能够将特征快速放置在图像上:
result

在猫上看到黑点表示由快速角点函数标识的角点。使用其他来匹配相同的对象。

使用imcorner函数进行角点检测

Imcorner函数是FAST替代方法。它也用于图像的角点,并提供不同的方法集:

  1. harris
  2. shi-tomasi
  3. kitchen-rosenfield

每种方法都以其自己独特的算法执行,因此特征表示将有所不同。为了更好地理解差异,将它们全部绘制在单个图像上。首先加载棋盘图像并将其转换为浮点表示形式:

using Images, ImageFeatures, ImageMorphology, ImageView
img = restrict(load("board-157165_640.png"))
img_f = Float16.(Gray.(img))

接下来,运行四种不同的算法。其中的三个由imcorner函数提供,最后一个是fastcorners:

img_harris = copy(img)
img_harris[dilate(imcorner(img_f, method=harris)) .> 0.01] .= colorant"yellow"
img_shi = copy(img)
img_shi[dilate(imcorner(img_f, method=shi_tomasi)) .> 0.01] .= colorant"yellow"
img_rosenfield = copy(img)
img_rosenfield[dilate(imcorner(img_f, method=kitchen_rosenfeld)) .> 0.01] .= colorant"yellow"
img_fast = copy(img)
img_fast[dilate(fastcorners(img_f, 12, 0.05)) .> 0.01] .= colorant"yellow"

通常,imcorner和fastcorners的结果很小,例如一个像素。从ImageMorphology软件包中应用了膨胀函数,以突出显示通过imcorner或FAST确定的结果,可以让用户在绘制和预览特征时更好地表示特征。还使用了着色剂技术来获得精确的颜色值。
将所有四个图像合并为一个图像:

new_img = vcat(
 hcat(img_harris, img_shi),
 hcat(img_rosenfield, img_fast)
)
new_img[Int(size(new_img, 1) / 2), :] .= colorant"yellow"
new_img[:, Int(size(new_img, 2) / 2)] .= colorant"yellow"
imshow(new_img)

combined

尝试在猫的图像上运行相同的代码,然后再次比较结果以查看是否有任何变化:

using Images, ImageFeatures, ImageMorphology, ImageView

img = Gray.(restrict(load("cat-3417184_640.jpg")))
img_f = Float16.(Gray.(img))
img_harris = copy(img)
img_harris[dilate(imcorner(img_f, method=harris)) .> 0.01] .= colorant"yellow"
img_shi = copy(img)
img_shi[dilate(imcorner(img_f, method=shi_tomasi)) .> 0.01] .= colorant"yellow"
img_rosenfield = copy(img)
img_rosenfield[dilate(imcorner(img_f, method=kitchen_rosenfeld)) .> 0.01] .= colorant"yellow"
img_fast = copy(img)
img_fast[dilate(fastcorners(img_f, 12, 0.05)) .> 0.01] .= colorant"yellow"
new_img = vcat(
 hcat(img_harris, img_shi),
 hcat(img_rosenfield,img_fast)
)
imshow(new_img)

result

除FAST以外的所有方法在图像周围都有角点。原因是,imcorner函数返回所有角点而没有任何阈值参数。

更新阈值以仅检索特征的前5%,然后再次比较结果:

img = Gray.(restrict(load("cat-3417184_640.jpg")))
img_f = Float16.(Gray.(img))
img_harris = copy(img)
img_harris[dilate(imcorner(img_f, Percentile(95), method=harris)) .> 0.01] = colorant"yellow"
img_shi = copy(img)
img_shi[dilate(imcorner(img_f, Percentile(95), method=shi_tomasi)) .> 0.01] = colorant"yellow"
img_rosenfield = copy(img)
img_rosenfield[dilate(imcorner(img_f, Percentile(95), method=kitchen_rosenfeld)) .> 0.01] = colorant"yellow"
img_fast = copy(img)
img_fast[dilate(fastcorners(img_f, 12, 0.05)) .> 0.01] = colorant"yellow"
new_img = vcat(
 hcat(img_harris, img_shi),
 hcat(img_rosenfield, img_fast)
)
imshow(new_img)

从下图可以看到,结果已经相似:

result

性能比较

进行性能比较以查看哪种算法更快也很重要。为了运行分析,在以下代码中使用了BenchmarkTools包中的@btime宏:

julia> using Images, ImageFeatures, BenchmarkTools
julia> @btime fastcorners(img_f, 12, 0.15);
# 667.308 μs (7 allocations: 40.33 KiB)
julia> @btime fastcorners(img_f, 12, 0.05);
# 916.183 μs (7 allocations: 40.33 KiB)
julia> @btime imcorner(img_f, method=harris);
# 1.050 ms (3041 allocations: 2.12 MiB)
julia> @btime imcorner(img_f, Percentile(95), method=harris);
# 821.515 μs (317 allocations: 2.13 MiB)
julia> @btime imcorner(img_f, method=shi_tomasi);
# 1.123 ms (3814 allocations: 2.15 MiB)
julia> @btime imcorner(img_f, method=kitchen_rosenfeld);
# 1.055 ms (4626 allocations: 2.05 MiB)

尽管使用fastcorners函数时阈值较小的时间会增加,但是内存消耗仍然很小且固定。

BRIEF–有效检测重复图像的方法

二元鲁棒独立基本特征(Binary Robust Independent Elementary Features, BRIEF)是二元特征点描述符。 BRIEF是一个非常简单的特征描述符,因此不能应用于缩放或旋转的图像。尽管有其局限性,它也可以有效地用于诸如重复检测之类的任务。

识别图像重复项

在以下示例中,将比较猫的原始图像与经过稍微修改和加水印的猫。
首先,加载图像并将其转换为灰度版本:

using Images, ImageFeatures, ImageDraw, ImageView
img1 = Gray.(load("cat-3417184_640.jpg"))
img2 = Gray.(load("cat-3417184_640_watermarked.jpg"))
imshow(restrict(hcat(img1, img2)))

imshow_compare

接下来,使用fastcorners函数检索关键点或角点,并比较每个图像的计数。通过在调用fastcorners时使用阈值参数的高值来减少返回的关键点的数量:

keypoints_1 = Keypoints(fastcorners(img1, 12, 0.5));
# 0.5 - very high threshold
keypoints_2 = Keypoints(fastcorners(img2, 12, 0.5));
size(keypoints_1) # result varies from 190 to 200
size(keypoints_2) # result varies from 190 to 200

参数的数量非常接近。下一步是初始化Brief,并调用create_descriptor函数来创建特征。调用match_keypoints函数以匹配两个图像的结果:

brief_params = BRIEF()
desc_1, ret_features_1 = create_descriptor(img1, keypoints_1, brief_params);
desc_2, ret_features_2 = create_descriptor(img2, keypoints_2, brief_params);
matches = match_keypoints(ret_features_1, ret_features_2, desc_1, desc_2, 0.5)

matchs变量应返回特征之间的匹配数。返回size将显示计数,如以下代码所示:

size(matches) # returns (197,)

所以,匹配的数目是197,预览结果以确认一切正确。通过合并两个图像并绘制一条连接匹配项的白线来实现此目的:

grid = hcat(img1, img2)
offset = CartesianIndex(0, size(img1, 2))
map(m -> draw!(grid, LineSegment(m[1], m[2] + offset)), matches)
imshow(grid)

尽管图像发生了变化,但可以看到主要特征已正确匹配。该过程运行相对较快,而且效果很好:
result_match

如果大规模运行重复检测,可以将create_descriptor的结果保存在某种数据库中。因此,当出现新图像时,只需要将其与已经预先计算的特征进行比较即可。
因为BRIEF仅针对单个像素使用信息,所以它对噪声敏感。为了克服此限制,它应用了高斯模糊,可以在初始化BRIEF时对其进行配置。

使用多张图像创建全景图

可以使用BRIEF执行的另一有趣活动是创建全景视图。假设有一个样本地点的许多图像,并且希望将它们连接在一起得出一张图片。可以尝试使用Brief来基于它们的共同特征来拼接图像。
先将图像加载到Julia中,然后将其分为两部分,再尝试进行连接:

using Images, ImageFeatures, ImageDraw, ImageShow
img = load("cat-3418815_640.jpg")
img_width = size(img, 2)
img_left_width = 400
img_right_width = 340

接下来,使用前面的设置创建两个新图像。保留原始版本和灰度版本。灰度版本将用于查找关键点,而彩色版本将用于创建最终结果:

img_left = view(img, :, 1:img_left_width)
img_left_gray = Gray.(img_left)
img_right = view(img, :, (img_width - img_right_width):img_width)
img_right_gray = Gray.(img_right)
imshow(img_left)
imshow(img_right)

以下图像是接下来将尝试连接的两个新图像:
imshow_pic

因此,使用以下代码从fastcorners函数的结果中找到关键点:

keypoints_1 = Keypoints(fastcorners(img_left_gray, 12, 0.3));
keypoints_2 = Keypoints(fastcorners(img_right_gray, 12, 0.3));

初始化BRIEF并找到特征之间的匹配:

brief_params = BRIEF()
desc_1, ret_features_1 = create_descriptor(img_left_gray, keypoints_1, brief_params);
desc_2, ret_features_2 = create_descriptor(img_right_gray, keypoints_2, brief_params);
matches = match_keypoints(ret_features_1, ret_features_2, desc_1, desc_2, 0.1)

查看关键点是否正确匹配:

grid = hcat(img_left, img_right)
offset = CartesianIndex(0, size(img_left_gray, 2))
map(m -> draw!(grid, LineSegment(m[1], m[2] + offset)), matches)
imshow(grid)

imshow_matches

下一步假设大多数点都已正确识别,可以计算每个关键点对之间的距离,并找到代表真实值的中值并将其称为偏移量:

offset_x = mean(map(m -> (img_left_width - m[1][2]) + m[2][2], matches))

为了简化其余步骤,将偏移量除以2,并将两个图像均缩小以适配结果。最后预览结果:

offset_x_half = Int(trunc(offset_x / 2))
img_output = hcat(
 img_left[:, 1:(img_left_width-offset_x_half)],
 img_right[:, offset_x_half:img_right_width]
)
imshow(hcat(img, img_output))

result_output

ORB–旋转不变图像匹配

ORB描述符是Brief的改进版本;它结合了FAST关键点检测器和经过改进和增强的Brief版本。
使用ORB的一个好处是使用了ORB内置的harris角点度量。它提供了选择前N个不相关的关键点的机会。最重要的是,描述符本身得到了增强,并且旋转不变。
不仅通过使用上一节中的类似示例来探索ORB,而且还将旋转应用于第二张图像。使用CoordinateTransformations包围绕中心旋转图像:

using Images, ImageFeatures, CoordinateTransformations
img1 = Gray.(load("cat-3417184_640.jpg"))
img2 = Gray.(load("cat-3417184_640_watermarked.jpg"))

由于图像已加载,并且想查看ORB是否旋转不变,因此将经变形应用于img2并使用以下代码将其围绕中心旋转:

rot = recenter(RotMatrix(5pi/6), [size(img2)...]2) 
tform = rot ∘ Translation(-50, -40)
img2 = warp(img2, tform, axes(img2))

接下来,使用一组默认参数初始化ORB。默认设置创建一组500个关键点,并使用阈值为0.25的FAST。默认设置应适合运行的大多数任务。看一下这段代码:

orb_params = ORB()

因为ORB开箱即用,所以直接进入create_descriptor函数来创建图像特征,并使用以下代码来匹配它们:

desc_1, ret_keypoints_1 = create_descriptor(img1, orb_params)
desc_2, ret_keypoints_2 = create_descriptor(img2, orb_params)
matches = match_keypoints(ret_keypoints_1, ret_keypoints_2, desc_1, desc_2, 0.2)

尝试合并图像并预览结果:

grid = Gray.(hcat(img1, img2))
offset = CartesianIndex(0, size(img1, 2))
map(m -> draw!(grid, LineSegment(m[1], m[2] + offset)), matches)
imshow(grid)

image_orb

尽管图像已旋转,但ORB能够匹配两个图像中的许多主要关键点。

BRISK–缩放不变图像匹配

BRISK是Julia中可用的另一种检测器。 BRISK的最大优点是缩放和旋转不变。规模不变性是以计算成本为代价的,这使其比ORB稍慢。
BRISK还允许使用例如imriser提供的任何其他关键点描述符,例如harris。像前面的示例一样继续使用FAST。
因此,开始使用BRISK。与往常一样,首先加载程序包和图像:

using Images, ImageFeatures, CoordinateTransformations, ImageDraw, ImageView,Rotations
img1 = Gray.(load("cat-3417184_640.jpg"))
img2 = Gray.(load("cat-3417184_640_watermarked.jpg"))

设定目标以证明BRISK是规模和旋转不变的。然后,使用以下代码对img2进行两种类型的转换:围绕中心旋转和调整图像大小:

rot = recenter(RotMatrix(5pi/6), [size(img2)...]2) 
tform = rot ∘ Translation(-50, -40)
img2 = warp(img2, tform, axes(img2))
img2 = imresize(img2, Int.(trunc.(size(img2) .* 0.7)))

类似于BRIEF,应用FAST算法查找角点。使用的阈值为0.25:

features_1 = Features(fastcorners(img1, 12, 0.25));
features_2 = Features(fastcorners(img2, 12, 0.25));

接下来,使用默认参数初始化BRISK,并尝试通过使用以下代码来匹配关键点:

brisk_params = BRISK()
desc_1, ret_features_1 = create_descriptor(img1, features_1, brisk_params);
desc_2, ret_features_2 = create_descriptor(img2, features_2, brisk_params);
matches = match_keypoints(Keypoints(ret_features_1), Keypoints(ret_features_2), desc_1, desc_2, 0.2)

将结果存储在matchs变量中。matchs变量是两个元素组成的数组,每个元素对应于一张图像上的一个位置。
由于希望将结果显示为单个图像,因此需要将img2的高度调整为等于img1的高度:

img3 = zeros(Gray, size(img1))
img3[1:size(img2, 1), 1:size(img2, 2)] = img2

绘制连接匹配的线。使用ImageDraw包中的函数draw!,并使用imshow函数通过以下代码显示结果:

grid = hcat(img1, img3)
offset = CartesianIndex(0, size(img1, 2))
map(m -> draw!(grid, LineSegment(m[1], m[2] + offset)), matches)
imshow(grid)

BRISK已成功识别并匹配缩放和旋转图像的特征:

brisk_image

FREAK–最快的缩放与旋转不变匹配

Julia中可用的最后一个描述符是FREAK描述符。该算法引入了一个新的关键点描述符,该描述符是由人类视觉系统(更确切地说是视网膜)启发而来的,是快速视网膜关键点(Fast Retina Keypoint, FREAK)创造的。
通常,FREAK以较低的计算需求来更快地进行计算。它使它们成为上一节中介绍的方法的绝佳替代方法。 FREAK被认为是最快的缩放,旋转和噪声不变算法。
首先使用以下代码加载软件包和图像:

using Images, ImageFeatures, CoordinateTransformations, ImageDraw, ImageView
img1 = Gray.(load("cat-3417184_640.jpg"))
img2 = Gray.(load("cat-3417184_640_watermarked.jpg"))

对第二张图片应用了多种变换。使用以下代码围绕中心旋转并调整图像大小:

rot = recenter(RotMatrix(5pi/6), [size(img2)...]2) 
tform = rot ∘ Translation(-50, -40)
img2 = warp(img2, tform, axes(img2))
img2 = imresize(img2, Int.(trunc.(size(img2) .* 0.7)))

执行FAST算法以找到角点,并通过以下代码使用FREAK匹配关键点:

keypoints_1 = Keypoints(fastcorners(img1, 12, 0.35));
keypoints_2 = Keypoints(fastcorners(img2, 12, 0.35));
freak_params = FREAK()
desc_1, ret_keypoints_1 = create_descriptor(img1, keypoints_1, freak_params);
desc_2, ret_keypoints_2 = create_descriptor(img2, keypoints_2, freak_params);
matches = match_keypoints(ret_keypoints_1, ret_keypoints_2, desc_1, desc_2, 0.2)

使用以下代码绘制结果并生成预览:

img3 = zeros(size(img1, 1), size(img2, 2))
img3[1:size(img2, 1), 1:size(img2, 2)] = img2
grid = Gray.(hcat(img1, img3))
offset = CartesianIndex(0, size(img1, 2))
map(m -> draw!(grid, LineSegment(m[1], m[2] + offset)), matches)
imshow(grid)

freak_result

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
使用逃逸时间算法可以生成Julia集合,Python语言也可以实现。 Julia集合是复平面上的一类分形图形,它由一组复数参数和一个复数变量组成。逃逸时间算法是一种计算Julia集合的方法,它通过迭代计算复数变量的值,判断其是否趋于无穷大来确定该点是否属于Julia集合。 在Python中,可以使用NumPy和Matplotlib库来实现逃逸时间算法生成Julia集合。具体步骤如下: 1. 定义复数参数和复数变量的范围。 2. 定义迭代次数和逃逸判断阈值。 3. 对于每个复数变量,进行迭代计算,并判断其是否逃逸。 4. 根据逃逸时间,给每个点赋予不同的颜色值。 5. 使用Matplotlib库绘制Julia集合图形。 以下是一个简单的Python代码示例: ```python import numpy as np import matplotlib.pyplot as plt # 定义复数参数和复数变量的范围 xmin, xmax, ymin, ymax = -2, 2, -2, 2 c = complex(-.4, .6) # 定义迭代次数和逃逸判断阈值 maxiter = 100 threshold = 2 # 创建复数变量的网格 xx, yy = np.meshgrid(np.linspace(xmin, xmax, 100), np.linspace(ymin, ymax, 100)) z = xx + yy * 1j # 迭代计算并判断逃逸 for i in range(maxiter): z = z**2 + c mask = (np.abs(z) > threshold) # 根据逃逸时间赋予颜色值 colors = np.zeros_like(mask, dtype=np.float) colors[mask] = i # 绘制Julia集合图形 plt.imshow(colors, cmap='hot', extent=[xmin, xmax, ymin, ymax]) plt.title('Julia Set') plt.xlabel('Real') plt.ylabel('Imaginary') plt.show() ``` 运行以上代码,即可生成一个Julia集合图形。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

盼小辉丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值