用局部平均值填充图像NaN值
最近,我们有一位客户询问如何用邻域局部平均值填充图像中的NaN值。我的朋友,同事,偶尔也写博客,布雷特Shoelson他今天和我一起向你展示几种可行的技术。
内容
创建数据
让我们创建一个图像,并人为地用nan创建一些洞。这是一个类型的图像uint8,不能表示NaN值,因此我们将转换为浮点数。
Img = imread(“rice.png”);谁img
名称大小字节类型属性img 256x25665536 uint8
thisiswhathhappens = uint8(NaN)
thisiswhathhappens = uint8 0
转换图像从uint8到单一。
singleImg = im2single(img);Tlo = tiledlayout(2,2,“TileSpacing”,“紧凑”,“填充”,“没有”);nexttile (tlo);imshow (singleImg)
添加一些NaN值。单个米粒小于270像素,因此我们选择在比典型米粒大的区域引入nan。首先,我们对图像进行阈值以分割米粒。我们丢弃小于270像素的斑点。这给了我们一个大的或连续的米粒面具。我们将原始图像中的这些区域替换为nan。你可以在第三张图中看到。
nanMask = imbinalize (singleImg,“自适应”);nanMask = bwareaopen(nanMask, 270);nexttile(tlo) imshow(nanMask) singleImgNaN = singleImg;singleImgNaN(nanMask) = NaN;nexttile (tlo) imshow (singleImgNaN)
现在让我们考虑填充这些NaN区域的几种方法。
使用regionfill替换nan -解决方案1 .单击“确定”
regionfill提供“开箱即用”的文档化区域填充方法。它工作得很好,而且速度很快。它使用向内插值填充图像中的区域——在这种情况下,用局部背景值的平滑表示取代nan。如果您对算法感兴趣,请访问文档页面。这是满足大多数区域填充需求的首选算法。
imgrf = regionfill(img, nanMask);nexttile (tlo) imshow (imgrf)
这很有效,但不一定是客户所要求的。
用本地平均代替nan -解决方案2
我们再试试别的吧。让我们首先定义“相邻像素”的含义。
如果我们有一个像素,我们可以将它的邻居定义为“4连通”,这意味着像素北、东、南和西是邻居,或者我们可以将邻居定义为“8连通”,在这种情况下,我们还包括NE、SE、SW和NW。我们可以将这些表示为相对于图像中像素位置的偏移量。
一个警告:我们需要小心不要超出图像的边缘,所以我们将用1像素的零边界填充图像。(/www.ru-cchi.com/products/image.html图像2022世界杯八强谁会赢?处理工具箱>中的函数在必要时为您处理这些事情。)
在图像周围添加一个单像素的黑色边框:
paddedImg = padarray(singleImgNaN, [1 1], 0,“两个”);
创建4连接和8连接偏移量。
[m, n] = size(singleImg);neighbor4 = [- 1,1, m, -m];Neighbors8 = [neighbor4, -m-1, -m+1, m-1, m+1];
我们不一定要改变图像的输出大小。为了适应这一点,我们可以将原始图像中的位置与填充图像分开处理。
让我们先找到nan。
originalNaNPos = find(isnan(singleImgNaN));paddedImageNaNs = find(isnan(paddedigg));
我们将覆盖输出图像中的单个像素,因此我们正在制作一个可以更改的副本。
imglocav = singleImgNaN;
我们可以创建并使用一些匿名函数来计算使用两个不同的邻域定义的平均值,忽略nan。
f4 = @(ind) mean(paddedImg(ind + neighbors4),“omitnan”);f8 = @(ind) mean(paddedigg (ind + neighbors8),“omitnan”);
让我们来看看4连通邻居的情况。我们找到填充图像中nan的位置,并计算每个nan的邻居均值。然后我们用这些计算值替换原始图像中相应的位置。
为ii = 1:numel(paddedImageNaNs) imglocav(originalNaNPos(ii)) = f4(paddedImageNaNs(ii));结束imshow (imglocav)
嗯
好了,我们看到了这种方法的第一个问题:任何完全被NaN邻居包围的NaN像素都将被NaN取代!
whyNaN = mean(nan(4,1)),“omitnan”)
whyNaN = NaN
所以,虽然我们缩小了纳米孔,但还没有完全消除它们。(注意,当我们放大并使用impixelinfo我们可以看到我们正在谈论的像素的值。)
那么该怎么办呢?
让我们重复!现在让我们考虑一下,如果我们迭代到没有更多的nan,会发生什么。
让我们先重置图像。
imglocav = singleImgNaN;
现在迭代
而nnz(isnan(imglocav)) > 0 originalNaNPos = find(isnan(imglocav));为ii = 1:numel(originalNaNPos) imglocav(originalNaNPos(ii)) = f4(paddedImageNaNs(ii));结束结束imshow (imglocav)
我们认为这是客户想要的,但现在我们看到了潜在的第二个问题:NaN像素在它们的4个或8个连接中完全被NaN包围,已经被0取代了。
电脑做了我们要求的,但不一定是我们想要的!
使用区域标记来填充-解决方案3
我们要利用nanMask我们之前计算过。我们找到轮廓,并标记出不同的连接区域。接下来,我们循环遍历每个区域,并用周长值的平均值填充每个区域。不需要迭代,但我们最终将得到均匀强度的平坦区域。
edgeMask = edge(nanMask);bwl = bwlabel(edgeMask);bwl2 = bwlabel(nanMask);imX = singleImgNaN;为ii = 1:max(bwl(:)) thisEdgeMean = mean(imX(bwl == ii),“omitnan”);imX(bwl2 == ii) = thisEdgeMean;结束imshow (imX)
其他人呢?
你对这个问题还有什么别的看法?我们知道有很多不同的方法。让我们知道在这里.
- 类别:
- 图像处理
评论
如欲留言,请点击在这里登录您的MathWorks帐户或创建一个新帐户。