12

私は頻繁に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)
4

5 に答える 5

17

あなたがしていることはあなたのイメージをハーフトーンにすることです。

他の人が提案した方法はうまく機能しますが、多くの高価な計算を何度も繰り返しています。には最大65,536の異なる値があるためuint16、ルックアップテーブル(LUT)を使用すると、作業を大幅に合理化できます。また、LUTが小さいため、適切な場所で処理を実行したり、ブール配列を作成しなかったりすることについてそれほど心配する必要はありません。次のコードは、BiRicoの関数を再利用して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のスピードアップがありますが、これは悪いことではないと思います...

于 2013-01-22T20:01:03.930 に答える
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)

注:一時的なfloat配列は、配列が作成される前の最後の行に引き続き作成されることに注意してくださいuint8

于 2013-01-22T17:39:28.790 に答える
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ビットのコピーが作成されます。

于 2013-01-22T19:24:10.833 に答える
0

これは、このソリューションの下のコメントで相互検証されたボードで見つけた答えです https://stats.stackexchange.com/a/70808/277040

基本的に、uint16からuint8アルゴリズムへの変換は次のようになります。

a = (255 - 0) / (65535 - 0)
b = 255 - a * 65535
newvalue = (a * img + b).astype(np.uint8)

一般化されたバージョンは次のようになります

def convert(img, target_type_min, target_type_max, target_type):
    imin = img.min()
    imax = img.max()

    a = (target_type_max - target_type_min) / (imax - imin)
    b = target_type_max - a * imax
    new_img = (a * img + b).astype(target_type)
    return new_img

例えば

imgu8 = convert(img16u, 0, 255, np.uint8)

于 2020-03-19T22:33:37.280 に答える
0

これが古いトレッドであることは知っていますが、GPUアクセラレーションを備えたキューピーがあります。キューピーを使用すると、キューピーを使用すると常に高速になります(Jaimeのすばらしいコメントの両方の方法がより高速で実行されます)。

import numpy as np
import cupy as cp
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)


def displaycp(image2, 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.
    image2 = cp.array(image2, copy=True)
    image2.clip(display_min, display_max, out=image2)
    image2 -= display_min
    cp.floor_divide(image2, (display_max - display_min + 1) / 256,
                    out=image2, casting='unsafe')
    return image2.astype(cp.uint8)

def lut_displaycp(image2, display_min, display_max) :
    lut = cp.arange(2**16, dtype='uint16')
    lut = displaycp(lut, display_min, display_max)
    return cp.take(lut, image2)

np.all(display(image, display_min, display_max) ==
           lut_display(image, display_min, display_max))

imagecp = cp.asarray(image)
type(imagecp)

cp.all(displaycp(imagecp, display_min, display_max) ==
           lut_displaycp(imagecp, display_min, display_max))

np.all(cp.asnumpy(displaycp(imagecp, display_min, display_max)) ==
          display(image, display_min, display_max))

タイミング

timeit.timeit('display(image, display_min, display_max)',
                  'from __main__ import display, image, display_min, display_max',
                   number=100)

1.2715457340000285

timeit.timeit('lut_display(image, display_min, display_max)',
                  'from __main__ import lut_display, image, display_min, display_max',
                  number=100)

0.27357000399933895

timeit.timeit('displaycp(imagecp, display_min, display_max)',
                  'from __main__ import displaycp, imagecp, display_min, display_max',
                   number=100)

0.018452465999871492

timeit.timeit('lut_displaycp(imagecp, display_min, display_max)',
                  'from __main__ import lut_displaycp, imagecp, display_min, display_max',
                  number=100)

0.015030614999886893

于 2020-12-02T05:08:18.267 に答える