图像数据二值化

3
我有来自BrainWeb的10个灰度脑MRI扫描。它们被存储为4D numpy数组brains,形状为(10, 181, 217, 181)。每个脑由181个z平面沿着z轴(通过头顶到颈部)组成,其中每个切片在x(耳朵到耳朵)和y(眼睛到后脑)平面分别为181个像素和217个像素。
所有的脑都是dtype('float64')类型。所有脑中的最大像素强度为~1328,最小值为~0。例如,对于第一个脑,我通过brains[0].max()计算得到1328.338086605072brains[0].min()计算得到0.0003886114541273855。下面是brain[0]切片的绘图:

enter image description here

我希望将所有这些脑部图像二值化,通过重新调整像素强度从[0, 1328]{0, 1}。我的方法正确吗?
首先,我将像素强度归一化为[0, 1]
normalized_brains = brains/1328 

然后通过使用二项分布将每个像素二值化:

binarized_brains = np.random.binomial(1, (normalized_brains))

绘制的结果看起来正确:

enter image description here

0像素强度代表黑色(背景),1像素强度代表白色(脑组织)。

我尝试了另一种从this post正常化图像的方法,但结果只是一张黑色图片。这是因为np.finfo(np.float64)等于1.7976931348623157e+308,所以无法正常化。

normalized_brains = brains/1.7976931348623157e+308

我刚刚返回了一个零数组,这在二值化步骤中也导致了一个零数组。

我使用的方法正确吗?


1
你的归一化是正确的,最终得到了二值图像,所以我猜这也没问题。但我不知道你在这里的意图是什么。有数百万种将图像二值化的方法,它们都有不同的目的。你在这里做的是一种非常差的抖动形式,有更好的方法可以获得一幅二值图像,从而捕捉原始灰度值。但它们唯一的用途是在非常有限的设备上进行视觉输出。那么,你想用二值图像做什么? - Cris Luengo
我想将数据二值化的原因是,我想尝试使用类似于这里的卷积变分自编码器。 - ajohnrobertson
当将像素建模为二进制变量时,模型的重构损失更容易计算。 - ajohnrobertson
3个回答

2
您将图像转换为二进制图像的方法基本上是随机抖动,这是一种在二进制介质上创建灰度值幻觉的较差方法。传统印刷是一种二进制介质,几个世纪以来他们已经精细调整了在印刷中表示灰度照片的方法。这个过程称为半色调处理,在某种程度上受到墨水在纸张上的特性的影响,在二进制图像中我们不必处理这些特性。
那么人们在印刷之外想出了什么方法呢?有顺序抖动(主要是 Bayer 矩阵)和误差扩散抖动。在维基百科上了解更多关于抖动的信息。我几年前写了一篇博客文章展示如何在MATLAB中实现所有这些方法
我建议您在您的特定应用中使用误差扩散抖动。这里是一些MATLAB代码(来自我上面提到的博客文章)用于Floyd-Steinberg算法,希望您能将其翻译成Python:
img = imread('https://istack.dev59.com/d5E9i.webp');
img = img(:,:,1);

out = double(img);
sz = size(out);
for ii=1:sz(1)
   for jj=1:sz(2)
      old = out(ii,jj);
      %new = 255*(old >= 128); % Original Floyd-Steinberg
      new = 255*(old >= 128+(rand-0.5)*100); % Simple improvement
      out(ii,jj) = new;
      err = new-old;
         if jj<sz(2)
            % right
            out(ii  ,jj+1) = out(ii  ,jj+1)-err*(7/16);
         end
      if ii<sz(1)
         if jj<sz(2)
            % right-down
            out(ii+1,jj+1) = out(ii+1,jj+1)-err*(1/16);
         end
            % down
            out(ii+1,jj  ) = out(ii+1,jj  )-err*(5/16);
         if jj>1
            % left-down
            out(ii+1,jj-1) = out(ii+1,jj-1)-err*(3/16);
         end
      end
   end
end

imshow(out)

enter image description here

在应用抖动之前对图像进行重新采样可以极大地改善结果:
img = imresize(img,4);
% (repeat code above)
imshow(out)

enter image description here

请注意,上述过程期望输入值在[0,255]范围内。很容易适应不同的范围,比如[0,1328]或[0,1],但是将图像缩放到[0,255]范围也很容易。

1

您尝试过对图像进行阈值处理吗?

这是一种常见的方法,用于将图像二值化,而不是尝试应用随机二项分布。您可以尝试类似以下的操作:

binarized_brains = (brains > threshold_value).astype(int)

该函数根据图像值是否小于或大于您选择的阈值值返回一个由0和1组成的数组。

您需要尝试不同的阈值值来找到最适合您的图像的那个,但它不需要先进行归一化处理。

如果这种方法效果不佳,您还可以尝试skimage filters软件包中提供的阈值选项。


是否适用于OP的目的,取决于OP是否指定了目的。我建议在问题清晰明确之前不要回答问题,否则当问题得到澄清时,您可能会面临负评的风险。 - Cris Luengo
我想将数据二值化的原因是,我想尝试使用类似于这里的卷积变分自编码器。我在原始帖子中没有提到这一点,因为我对寻找有效的图像数据二值化方法感兴趣。 - ajohnrobertson

0
IT is easy in OpenCV. as mentioned a very common way is defining a threshold, But your result looks like you are allocating random values to your intensities instead of thresholding it. 

import cv2
im = cv2.imread('brain.png', cv2.CV_LOAD_IMAGE_GRAYSCALE)
(th, brain_bw) = cv2.threshold(imy, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
th = (DEFINE HERE)
im_bin = cv2.threshold(im, th, 255, cv
cv2.imwrite('binBrain.png', brain_bw)

brain

binBrain


这段内容与编程有关。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接