2013-01-22 24 views
10

我经常将16位灰度图像数据转换为8位图像数据进行显示。调整最小和最大显示强度以突出图像的“有趣”部分几乎总是有用的。使用numpy高效地将16位图像数据转换为8位显示,并进行强度缩放

下面的代码大致上是我想要的,但它丑陋而且效率低下,并且产生了很多图像数据的中间副本。 如何以最小的内存占用量和处理时间实现相同的结果?

import numpy 

image_data = numpy.random.randint(#Realistic images would be much larger 
    low=100, high=14000, size=(1, 5, 5)).astype(numpy.uint16) 

display_min = 1000 
display_max = 10000.0 

print(image_data) 
threshold_image = ((image_data.astype(float) - display_min) * 
        (image_data > display_min)) 
print(threshold_image) 
scaled_image = (threshold_image * (255./(display_max - display_min))) 
scaled_image[scaled_image > 255] = 255 
print(scaled_image) 
display_this_image = scaled_image.astype(numpy.uint8) 
print(display_this_image) 

回答

13

你在做什么是halftoning你的形象。

其他人提出的方法效果很好,但他们一遍又一遍地重复了大量昂贵的计算。由于在uint16中最多有65,536个不同的值,所以使用查找表(LUT)可以简化很多事情。而且由于LUT很小,所以您不必担心就地操作或不创建布尔数组。下面的代码重用毕波多黎各的函数来创建LUT:

import numpy as np 
import timeit 

rows, cols = 768, 1024 
image = np.random.randint(100, 14000, 
          size=(1, rows, cols)).astype(np.uint16) 
display_min = 1000 
display_max = 10000 

def display(image, display_min, display_max): # copied from Bi Rico 
    # Here I set copy=True in order to ensure the original image is not 
    # modified. If you don't mind modifying the original image, you can 
    # set copy=False or skip this step. 
    image = np.array(image, copy=True) 
    image.clip(display_min, display_max, out=image) 
    image -= display_min 
    np.floor_divide(image, (display_max - display_min + 1)/256, 
        out=image, casting='unsafe') 
    return image.astype(np.uint8) 

def lut_display(image, display_min, display_max) : 
    lut = np.arange(2**16, dtype='uint16') 
    lut = display(lut, display_min, display_max) 
    return np.take(lut, image) 


>>> np.all(display(image, display_min, display_max) == 
      lut_display(image, display_min, display_max)) 
True 
>>> timeit.timeit('display(image, display_min, display_max)', 
        'from __main__ import display, image, display_min, display_max', 
        number=10) 
0.304813282062 
>>> timeit.timeit('lut_display(image, display_min, display_max)', 
        'from __main__ import lut_display, image, display_min, display_max', 
        number=10) 
0.0591987428298 

所以就有了X5加速,这不是一件坏事,我猜...

+1

非常好!这是我自己不想拿出来的东西。 – Andrew

+0

美丽而优雅的解决方案! – RockJake28

2

为了减少内存使用量,请在原地进行裁剪并避免创建布尔数组。

dataf = image_data.astype(float) 
numpy.clip(dataf, display_min, display_max, out=dataf) 
dataf -= display_min 
datab = ((255./(display_max - display_min)) * dataf).astype(numpy.uint8) 

如果你把你的削波限制为整数值,可以交替地做到这一点:

numpy.clip(image_data, display_min, display_max, out=image_data) 
image_data-= display_min 
datab = numpy.empty_like(image_data) 
numpy.multiply(255./(display_max - display_min), image_data, out=datab) 

注意:临时int数组仍将在最后一行创建之前uint8阵列创建。

+0

不错!我不知道'剪辑'。 – Andrew

+0

不知用'numpy.multiply'与'out'参数设置为image_data是否等效?我知道它试图将浮点数据写入uint16数组,但这可能是我们想要的呢? – Andrew

+0

这个想法正在消除float数组。 – Andrew

2

我会避免铸造的图像浮动,你可以这样做:

import numpy as np 

def display(image, display_min, display_max): 
    # Here I set copy=True in order to ensure the original image is not 
    # modified. If you don't mind modifying the original image, you can 
    # set copy=False or skip this step. 
    image = np.array(image, copy=True) 

    image.clip(display_min, display_max, out=image) 
    image -= display_min 
    image //= (display_min - display_max + 1)/256. 
    image = image.astype(np.uint8) 
    # Display image 

在这里,图像的可选副本是由在它的原生数据类型和一个8位的副本做出最后线。

相关问题