簡體   English   中英

來自 Python 中的二進制文件的每像素圖像 4 位,帶有 Numpy 和 CV2?

[英]4 bit per pixel image from binary file in Python with Numpy and CV2?

假設我想將二進制數據表示為黑白圖像,每個像素的灰度值只有 16 個不同的級別,以便每兩個相鄰像素(縱向)代表一個字節。 我怎樣才能做到這一點? 例如,如果我使用以下內容:

import numpy as np
path = r'mybinaryfile.bin'
bin_data = np.fromfile(path, dtype='uint8')
scalar = 20
width = int(1800/scalar)
height = int(1000/scalar)
for jj in range(50):
  wid = int(width*height)
  img = bin_data[int(jj*wid):int((jj+1)*wid)].reshape(height,width)
  final_img = cv2.resize(img, (scalar*width, scalar*height), interpolation = cv2.INTER_NEAREST)
  fn = f'tmp/output_{jj}.png'
  cv2.imwrite(fn, final_img)

我可以創建一系列代表二進制文件的 PNG 文件,每個 20 x 20 像素塊代表一個字節。 但是,這會為灰色 (256) 創建太多唯一值,因此我需要將其減少到更少 (16)。 我如何將每個像素“拆分”為具有 16 個不同灰度級(4 bpp,而不是 8 個)的兩個像素?

使用 4 bpp 而不是 8 bpp 應該會使圖像文件的數量增加一倍,因為我保持分辨率相同但將我用來表示一個字節的像素數量增加一倍(每字節 2 個像素而不是 1 個像素)。

我了解到你想取一個 8 位數字並將高四位和低四位分開。

這可以通過幾個按位運算來完成。

def split_octet(data):
    """
    For each 8-bit number in array, split them into two 4-bit numbers"""
    split_data = []
    for octet in data:
        upper = octet >> 4
        lower = octet & 0x0f
        print(f"8bit:{octet:02x} upper:{upper:01x} and lower:{lower:01x}")
        split_data.extend([upper, lower])
    return split_data

對於要創建的灰度圖像,需要將數據轉換為 0 到 255 范圍內的值。但是您只想保留 16 個離散值。 這可以通過將 0 到 1 范圍內的 4 位值歸一化來完成。將值乘以 255 以返回 uint8 值。

def create_square_grayscale(data, data_shape):
    # Normalize data from 0 to 1
    normalized = np.array(data, np.float64) / 0xf
    # fold data to image shape
    pixel_array = normalized.reshape(data_shape)
    # change 16 possible values over 0 to 255 range
    return np.array(pixel_array * 0xff, np.uint8)

我的完整測試用例是:

from secrets import token_bytes
import cv2
import numpy as np

pixel_size = 20
final_image_size = (120, 120)


def gen_data(data_size):
    # Generate some random data
    return token_bytes(data_size)


def split_octet(data):
    """
    For each 8-bit number in array, split them into two 4-bit numbers"""
    split_data = []
    for octet in data:
        upper = octet >> 4
        lower = octet & 0x0f
        print(f"8bit:{octet:02x} upper:{upper:01x} and lower:{lower:01x}")
        split_data.extend([upper, lower])
    return split_data


def create_square_grayscale(data, data_shape):
    # Normalize data from 0 to 1
    normalized = np.array(data, np.float64) / 0xf
    # fold data to image shape
    pixel_array = normalized.reshape(data_shape)
    # change 16 possible values over 0 to 255 range
    return np.array(pixel_array * 0xff, np.uint8)


def main():
    side1, side2 = (int(final_image_size[0]/pixel_size),
                    int(final_image_size[1]/pixel_size))
    rnd_data = gen_data(int((side1 * side2)/2))
    split_data = split_octet(rnd_data)
    img = create_square_grayscale(split_data, (side1, side2))
    print("image data:\n", img)
    new_res = cv2.resize(img, None, fx=pixel_size, fy=pixel_size,
                         interpolation=cv2.INTER_AREA)
    cv2.imwrite("/tmp/rnd.png", new_res)


if __name__ == '__main__':
    main()

其中提供了以下內容的成績單:

8bit:34 upper:3 and lower:4
8bit:d4 upper:d and lower:4
8bit:bd upper:b and lower:d
8bit:c3 upper:c and lower:3
8bit:61 upper:6 and lower:1
8bit:9e upper:9 and lower:e
8bit:5f upper:5 and lower:f
8bit:1b upper:1 and lower:b
8bit:a5 upper:a and lower:5
8bit:31 upper:3 and lower:1
8bit:22 upper:2 and lower:2
8bit:8a upper:8 and lower:a
8bit:1e upper:1 and lower:e
8bit:84 upper:8 and lower:4
8bit:3a upper:3 and lower:a
8bit:c0 upper:c and lower:0
8bit:3c upper:3 and lower:c
8bit:09 upper:0 and lower:9
image data:
 [[ 51  68 221  68 187 221]
 [204  51 102  17 153 238]
 [ 85 255  17 187 170  85]
 [ 51  17  34  34 136 170]
 [ 17 238 136  68  51 170]
 [204   0  51 204   0 153]]

並生成了下圖:

在此處輸入圖像描述

原始數據有 18 個字節,有 36 個塊/"20x20_pixels"

如果我將問題中的尺寸更改為 1800、1000,我會得到:

在此處輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM