如何从 python 中的十六进制数据创建 bmp 文件?

Posted

技术标签:

【中文标题】如何从 python 中的十六进制数据创建 bmp 文件?【英文标题】:How to create bmp file from hex data in python? 【发布时间】:2016-12-29 03:38:14 【问题描述】:

十六进制数据数组只包含灰色值,没有任何bmp文件信息。

但我知道分辨率(像素的宽度和高度),图像是灰色的,这意味着每个像素是 8 位。

任何工具(在线或离线)或代码可以从数据生成 bmp 文件?

比如下面的代码不行,结果是8*33

import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np

s = "FFFFFFFF B0A7FFFF FFFFFFB3 807D8EFF FFFFFF94 707783CC FFFFFF89 7A8988A4 FFFFFFAD 929298BE FFFFFFAD 979AA8E2 FFFFFFAF 8991A9FF FFFFFF9F 808BA6FF FFFFFFAB 8694AFFF FFFFFFB2 8A96A0FF FFFFFFA8 859496FF FFFFFFB3 88809ADA FFFFFFA6 7B728DD7 FFFFFF85 6F7084D4 FFFFFF86 66647BDA FFFFFF8D 606482DD FFFFFF8B 666788DC FFFFFF7B 616282CE FFFFFF86 63657AC2 FFFFFFA1 72697FCB FFFFFF9B 75636FC6 FFFFFF88 6B596EC1 FFFFFF8B 675A80D5 FFFFFFA0 6D5E79DE FFFFFF8F 6B5C73FF FFFFD67F 605E8FFF FFFFDA7F 665B96FF FFFFD384 645A86FF FFFFFF84 6F6E86FF FFFFFFBE 979DC0FF FFFFFFFF C2CCFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFCBB9 9EB7FFFF FFBEA17C 677A99FF FFC59F66 565972C5 BACC9D70 595A76CA 9E997665 5E699EE0 977B6A6B 7F94B0FF FFBDA5A4 C4D9FFFF"

data = s.replace(" ", "").decode('hex')
plt.imsave('filename.bmp', np.array(data).reshape(8,33), cmap=cm.gray)

【问题讨论】:

怎么不工作了?您能否展示您期望图像的外观或收到的错误? 我猜数据数组只包含颜色值,不包含bmp文件信息。 请在***的搜索框中输入字符串“python write bmp file”。您将看到 29 个答案! 【参考方案1】:

我正在寻找类似问题的答案,但无法在“纯”python 中找到“快速”可理解的解决方案。 所以我写了一些代码来创建一个位图(从示例中转换为 RGB 值的字符数组数据)。请阅读下面代码示例的 MIT 许可后的 cmets。它们描述了从任何数据创建位图所需的信息。

我合并了来自http://blog.paphus.com/blog/2012/08/14/write-your-own-bitmaps/ 和enter link description here 的信息

我的输出是:

#!/usr/bin/python
# ----------------------- START MIT LICENSE -----------------------
# Copyright (c) 2018 Benjamin Spiegl

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# ----------------------- END MIT LICENSE -----------------------

# The pixel data has to be written with some minor modifications.
# First, the bottom row of the image appears first in the data.
# If you forget this step your image will be vertically flipped.
# Second, the pixes are written in BGR (blue - green - red) format, which is the opposite of the normal RGB.
# Finally, at the end of each row you must pad it with bytes till it is a multiple of 4.
# All numbers in the headers must be LITTLE-ENDIAN.
# For instance, when representing 54 as four bytes, those four bytes must be 54, 0, 0, 0.
# To represent 24 as two bytes, the two bytes must be 24, 0.

from numpy import array
from math import floor

def make_hex_list(int_val=None, force_byte_len=None, little_endian=True):
    if int_val is None:
        return []
    raw_pix_hex = hex(int_val)[2:]
    first_hex_str = ''  # is falsy
    if len(raw_pix_hex) % 2:  # odd number of hex vals
        first_hex_str = '0x0' + raw_pix_hex[0]
        raw_pix_hex = raw_pix_hex[1:]
    if first_hex_str:
        raw_pix_hex_list = [first_hex_str] + ['0x' + raw_pix_hex[lit_pair_idx * 2:lit_pair_idx * 2 + 2]
                                              for lit_pair_idx in range(int(floor(len(raw_pix_hex) / 2)))]
    else:
        raw_pix_hex_list = ['0x' + raw_pix_hex[lit_pair_idx * 2:lit_pair_idx * 2 + 2]
                            for lit_pair_idx in
                            range(int(floor(len(raw_pix_hex) / 2)))]  # max 4 bytes (32 bit) -> should be ok
    if force_byte_len and len(raw_pix_hex_list) != force_byte_len:  # force 4 bytes
        raw_pix_hex_list = ['0x00'] * (force_byte_len - len(raw_pix_hex_list)) + raw_pix_hex_list
    elif len(raw_pix_hex_list) > 4:
        raise OverflowError('base matrix too large for 32 bit bitmap.')
    if little_endian:
        return list(reversed([int(hx, 16) for hx in raw_pix_hex_list]))
    else:
        return [int(hx, 16) for hx in raw_pix_hex_list]

# A=yellow, T=red, C=green, G=blue, D=grey, N=black
bmp_BGR_colors = 'A': (0, 255, 255), 'T': (0, 0, 255), 'C': (0, 255, 0), 'G': (255, 0, 0),
                  'N': (0, 0, 0), 'D': (150, 150, 150)

bmp_name = 'bitmap_test.bmp'
magnify = 16
mybase_arr = array([['G', 'A', 'G', 'A', 'D', 'D', 'T', 'T', 'A', 'N'],
                    ['G', 'A', 'G', 'A', 'A', 'D', 'T', 'T', 'A', 'N'],
                    ['G', 'A', 'G', 'A', 'A', 'D', 'T', 'T', 'A', 'N'],
                    ['G', 'A', 'G', 'A', 'A', 'D', 'T', 'T', 'A', 'N'],
                    ['G', 'A', 'G', 'A', 'A', 'D', 'T', 'T', 'A', 'N'],
                    ['G', 'A', 'G', 'A', 'A', 'D', 'T', 'T', 'A', 'N'],
                    ['G', 'A', 'G', 'A', 'A', 'D', 'T', 'T', 'A', 'N'],
                    ['G', 'A', 'G', 'A', 'A', 'D', 'T', 'T', 'A', 'N'],
                    ['G', 'A', 'G', 'A', 'A', 'D', 'T', 'T', 'A', 'N'],
                    ['G', 'A', 'G', 'A', 'A', 'D', 'T', 'T', 'A', 'N'],
                    ['G', 'A', 'G', 'A', 'A', 'D', 'T', 'T', 'A', 'N'],
                    ['G', 'A', 'G', 'A', 'A', 'D', 'T', 'T', 'A', 'N'],
                    ['G', 'A', 'G', 'A', 'A', 'D', 'T', 'T', 'A', 'N'],
                    ['G', 'A', 'G', 'A', 'A', 'D', 'T', 'T', 'A', 'N'],
                    ['G', 'A', 'G', 'A', 'A', 'D', 'T', 'T', 'A', 'N'],
                    ['G', 'A', 'G', 'A', 'A', 'D', 'T', 'T', 'A', 'N'],
                    ['N', 'A', 'G', 'A', 'A', 'D', 'T', 'T', 'A', 'N']])
arr_height, arr_width = mybase_arr.shape
# prepare header information
pixel_width = arr_width * magnify  # force 4 bytes
pixel_height = arr_height * magnify  # force 4 bytes
linepad = pixel_width * 3 % 4  # ensure valid row bytes (3 bytes per pixel; 1 per color channel)
total_pad = pixel_height * linepad
pix_offset = [0x36, 0x00, 0x00, 0x00]  # force 4 byte
raw_pix_data_size = pixel_width * pixel_height * 3 + total_pad
raw_pix_data_size_hex_list = make_hex_list(int_val=raw_pix_data_size, force_byte_len=4)
file_size = 0x36 + raw_pix_data_size  # force 4 byte
# create header values for byte conversion
header_vals = [0x42, 0x4d] + make_hex_list(int_val=file_size, force_byte_len=4) + [0x00]*4 + pix_offset + \
              [0x28, 0x00, 0x00, 0x00] + make_hex_list(int_val=pixel_width, force_byte_len=4) + \
              make_hex_list(int_val=pixel_height, force_byte_len=4) + \
              make_hex_list(int_val=1, force_byte_len=2) + \
              make_hex_list(int_val=24, force_byte_len=2) + make_hex_list(int_val=0, force_byte_len=4) + \
              raw_pix_data_size_hex_list + [0x13, 0x0b, 0x00, 0x00]*2 + [0x00, 0x00, 0x00, 0x00]*2
assert(len(header_vals) == pix_offset[0])
# create pixel values for byte conversion
if linepad:  # need to 0-pad lines to a multiple of 4 bytes per line
    pixel_vals = list()
    for reverse_row in list(reversed(mybase_arr)):
        for cpy in range(magnify):  # repeat line 16 times (magnify)
            pixel_vals.extend([val for pix in reverse_row for val in bmp_BGR_colors[pix] * magnify] + [0x00] * linepad)
else:  # no padding needed
    pixel_vals = [val for reverse_row in list(reversed(mybase_arr))
                  for cpy in range(magnify) for pix in reverse_row
                  for val in bmp_BGR_colors[pix]*magnify]  # last for to unpack tuples
assert(len(pixel_vals) == raw_pix_data_size)
with open(bmp_name, 'wb') as bmp_file:
    bmp_file.write(bytearray(header_vals + pixel_vals))  # convert values to bytes

【讨论】:

以上是关于如何从 python 中的十六进制数据创建 bmp 文件?的主要内容,如果未能解决你的问题,请参考以下文章

将数据从结构二进制复制到 std::vector

图解Winhex解析BMP文件数据实例

如何从 iOS 中的十六进制/二进制(原始数据)值生成音频文件?

C/C++中如何将文件读取为二进制数据到数组中,再从数组中读取二进制流还原为文件,主要是后一步

BMP文件中奇怪的十六进制数字

int像素数组到java中的bmp