You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DCM转Raw遇‘bytes无法隐式转str’错误及MHD文件生成咨询

解决DCM转Raw的类型错误 & 生成MHD文件方案

一、先搞定Can't convert 'bytes' object to str implicitly错误

你遇到这个问题的核心原因很简单:字符串和字节类型不能直接拼接。

看你的代码,你初始化allInOne = ""(这是字符串类型),但dataset.PixelData是字节(bytes)类型,用+=拼接时Python就会报错。你尝试用encode("utf-8")完全没用,因为空字符串编码后还是空字节,反而把原本的拼接逻辑搞乱了。

修复方法:

  1. 把allInOne的初始值改成字节空对象b"",这样后续拼接字节数据就完全匹配了
  2. 你的路径拼接方式dicomPath + "".join(filename)有坑——如果遍历到子目录里的DICOM文件,这个路径会找不到文件,建议用os.path.join(dirName, filename)来自动处理路径分隔符
  3. numpy里的tostring()已经被废弃了,推荐用tobytes()替代

修改后的核心循环代码:

for dirName, subdirList, fileList in os.walk(dicomPath):
    allInOne = b""  # 关键:改为字节类型初始值
    print(subdirList)
    i=0
    for filename in fileList:
        i+=1
        if filename.endswith((".dcm", ".DCM")):
            # 正确拼接路径
            path = os.path.join(dirName, filename)
            dataset = dicom.read_file(path)
            # 像素值处理逻辑保留
            for n,val in enumerate(dataset.pixel_array.flat):
                dataset.pixel_array.flat[n] = val / 60
                if val < 0:
                    dataset.pixel_array.flat[n] = 0
            # 用tobytes()替代tostring()
            dataset.PixelData = numpy.uint8(dataset.pixel_array).tobytes()
            # 现在可以正常拼接字节数据了
            allInOne += dataset.PixelData
            print(f"slice {filename} done {i}")  # 用f-string更简洁直观

二、生成对应的MHD文件

MHD文件是Raw数据的“描述文件”,它告诉读取工具Raw数据的维度、像素类型、间距等信息。下面是适配你Raw生成逻辑的MHD生成代码:

完整的MHD生成函数

import dicom
import os
import numpy as np

def generate_mhd(raw_file_path, mhd_file_path, dicom_root_path):
    # 先获取第一个DICOM文件的元信息(作为序列基准)
    first_dcm = None
    total_slices = 0

    # 遍历统计切片数量并获取第一个DICOM文件
    for dirpath, _, filenames in os.walk(dicom_root_path):
        for fname in filenames:
            if fname.lower().endswith(".dcm"):
                total_slices += 1
                if not first_dcm:
                    first_dcm = dicom.read_file(os.path.join(dirpath, fname))
        if first_dcm:
            break

    if not first_dcm:
        print("Error: No DICOM files found in the path!")
        return

    # 从DICOM中提取关键参数
    img_width = first_dcm.Columns
    img_height = first_dcm.Rows
    pixel_spacing = first_dcm.PixelSpacing
    # 如果DICOM没有SliceThickness字段,默认用像素间距的第一个值替代
    slice_thickness = first_dcm.SliceThickness if hasattr(first_dcm, "SliceThickness") else pixel_spacing[0]

    # MHD文件的内容模板,注意ElementType要和你转换的像素类型匹配(这里是uint8,对应MET_UCHAR)
    mhd_content = f"""ObjectType = Image
NDims = 3
BinaryData = True
BinaryDataByteOrderMSB = False
CompressedData = False
TransformMatrix = 1 0 0 0 1 0 0 0 1
Offset = 0 0 0
CenterOfRotation = 0 0 0
ElementSpacing = {pixel_spacing[0]} {pixel_spacing[1]} {slice_thickness}
DimSize = {img_width} {img_height} {total_slices}
ElementType = MET_UCHAR
ElementDataFile = {os.path.basename(raw_file_path)}
"""

    # 写入MHD文件
    with open(mhd_file_path, "w", encoding="utf-8") as f:
        f.write(mhd_content)
    print(f"Successfully generated MHD file: {mhd_file_path}")

调用方式

在你生成Raw文件之后,直接调用这个函数即可:

# 生成Raw文件的代码执行完后
generate_mhd("./all_in_one.raw", "./all_in_one.mhd", dicomPath)

整合后的完整代码

把两部分整合起来,就是可以直接运行的完整脚本了:

import dicom
import os
import numpy as np
import sys

dicomPath = "C:/DataLuna16pen/dcmdata/"

def generate_mhd(raw_file_path, mhd_file_path, dicom_root_path):
    first_dcm = None
    total_slices = 0
    for dirpath, _, filenames in os.walk(dicom_root_path):
        for fname in filenames:
            if fname.lower().endswith(".dcm"):
                total_slices += 1
                if not first_dcm:
                    first_dcm = dicom.read_file(os.path.join(dirpath, fname))
        if first_dcm:
            break

    if not first_dcm:
        print("Error: No DICOM files found!")
        return

    img_width = first_dcm.Columns
    img_height = first_dcm.Rows
    pixel_spacing = first_dcm.PixelSpacing
    slice_thickness = first_dcm.SliceThickness if hasattr(first_dcm, "SliceThickness") else pixel_spacing[0]

    mhd_content = f"""ObjectType = Image
NDims = 3
BinaryData = True
BinaryDataByteOrderMSB = False
CompressedData = False
TransformMatrix = 1 0 0 0 1 0 0 0 1
Offset = 0 0 0
CenterOfRotation = 0 0 0
ElementSpacing = {pixel_spacing[0]} {pixel_spacing[1]} {slice_thickness}
DimSize = {img_width} {img_height} {total_slices}
ElementType = MET_UCHAR
ElementDataFile = {os.path.basename(raw_file_path)}
"""

    with open(mhd_file_path, "w", encoding="utf-8") as f:
        f.write(mhd_content)
    print(f"MHD file generated: {mhd_file_path}")

# 处理DICOM并生成Raw文件
allInOne = b""
for dirName, subdirList, fileList in os.walk(dicomPath):
    i = 0
    for filename in fileList:
        i += 1
        if filename.lower().endswith(".dcm"):
            path = os.path.join(dirName, filename)
            try:
                dataset = dicom.read_file(path)
                # 像素值归一化与截断
                for n, val in enumerate(dataset.pixel_array.flat):
                    normalized_val = val / 60
                    dataset.pixel_array.flat[n] = max(0, normalized_val)
                # 转换为uint8字节数据
                dataset.PixelData = np.uint8(dataset.pixel_array).tobytes()
                allInOne += dataset.PixelData
                print(f"Processed slice: {filename} ({i})")
            except Exception as e:
                print(f"Failed to process {filename}: {str(e)}")

# 写入Raw文件
with open("./all_in_one.raw", "wb") as raw_file:
    raw_file.write(allInOne)
print("RAW file generated successfully!")

# 生成对应的MHD文件
generate_mhd("./all_in_one.raw", "./all_in_one.mhd", dicomPath)

内容的提问来源于stack exchange,提问作者黃本聰

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:50:37