วิธีสร้าง JPEG ชุดข้อมูล DICOM ที่ถูกบีบอัดโดยใช้ pydicom?


14

ฉันพยายามที่จะสร้างภาพ DICOM JPEG ที่ใช้pydicom แหล่งข้อมูลที่ดีเกี่ยวกับภาพ DICOM ที่มีสีสันสามารถพบได้ที่นี่แต่ส่วนใหญ่เป็นทฤษฎีและ C ++ ในตัวอย่างรหัสด้านล่างฉันสร้างจุดไข่ปลาสีน้ำเงินภายในoutput-raw.dcm(ไม่บีบอัด) ซึ่งดูดีเช่นนี้:

ตัวอย่างรูปภาพ DICOM

import io
from PIL import Image, ImageDraw
from pydicom.dataset import Dataset
from pydicom.uid import generate_uid, JPEGExtended
from pydicom._storage_sopclass_uids import SecondaryCaptureImageStorage

WIDTH = 100
HEIGHT = 100


def ensure_even(stream):
    # Very important for some viewers
    if len(stream) % 2:
        return stream + b"\x00"
    return stream


def bob_ross_magic():
    image = Image.new("RGB", (WIDTH, HEIGHT), color="red")
    draw = ImageDraw.Draw(image)
    draw.rectangle([10, 10, 90, 90], fill="black")
    draw.ellipse([30, 20, 70, 80], fill="cyan")
    draw.text((11, 11), "Hello", fill=(255, 255, 0))
    return image


ds = Dataset()
ds.is_little_endian = True
ds.is_implicit_VR = True
ds.SOPClassUID = SecondaryCaptureImageStorage
ds.SOPInstanceUID = generate_uid()
ds.fix_meta_info()
ds.Modality = "OT"
ds.SamplesPerPixel = 3
ds.BitsAllocated = 8
ds.BitsStored = 8
ds.HighBit = 7
ds.PixelRepresentation = 0
ds.PhotometricInterpretation = "RGB"
ds.Rows = HEIGHT
ds.Columns = WIDTH

image = bob_ross_magic()
ds.PixelData = ensure_even(image.tobytes())

image.save("output.png")
ds.save_as("output-raw.dcm", write_like_original=False)  # File is OK

#
# Create compressed image
#
output = io.BytesIO()
image.save(output, format="JPEG")

ds.PixelData = ensure_even(output.getvalue())
ds.PhotometricInterpretation = "YBR_FULL_422"
ds.file_meta.TransferSyntaxUID = JPEGExtended

ds.save_as("output-jpeg.dcm", write_like_original=False)  # File is corrupt

ในตอนท้ายฉันพยายามสร้าง DICOM ที่ถูกบีบอัด: ฉันพยายามตั้งค่าการถ่ายโอนรูปแบบต่าง ๆ , การบีบอัดด้วย PIL แต่ไม่มีโชค ฉันเชื่อว่าไฟล์ DICOM ที่สร้างขึ้นเสียหาย ถ้าฉันจะแปลงไฟล์ DICOM ดิบเป็น JPEG ที่บีบอัดด้วย gdcm-tools:

$ gdcmconv -J output-raw.dcm output-jpeg.dcm

ด้วยการทำไฟล์ที่แปลงแล้วdcmdumpนี้เราสามารถเห็นโครงสร้างที่น่าสนใจซึ่งฉันไม่รู้วิธีการสร้างซ้ำโดยใช้ pydicom:

$ dcmdump output-jpeg.dcm

# Dicom-File-Format

# Dicom-Meta-Information-Header
# Used TransferSyntax: Little Endian Explicit
(0002,0000) UL 240                                      #   4, 1 FileMetaInformationGroupLength
(0002,0001) OB 00\01                                    #   2, 1 FileMetaInformationVersion
(0002,0002) UI =SecondaryCaptureImageStorage            #  26, 1 MediaStorageSOPClassUID
(0002,0003) UI [1.2.826.0.1.3680043.8.498.57577581978474188964358168197934098358] #  64, 1 MediaStorageSOPInstanceUID
(0002,0010) UI =JPEGLossless:Non-hierarchical-1stOrderPrediction #  22, 1 TransferSyntaxUID
(0002,0012) UI [1.2.826.0.1.3680043.2.1143.107.104.103.115.2.8.4] #  48, 1 ImplementationClassUID
(0002,0013) SH [GDCM 2.8.4]                             #  10, 1 ImplementationVersionName
(0002,0016) AE [gdcmconv]                               #   8, 1 SourceApplicationEntityTitle

# Dicom-Data-Set
# Used TransferSyntax: JPEG Lossless, Non-hierarchical, 1st Order Prediction
...
... ### How to do the magic below?
...
(7fe0,0010) OB (PixelSequence #=2)                      # u/l, 1 PixelData
  (fffe,e000) pi (no value available)                     #   0, 1 Item
  (fffe,e000) pi ff\d8\ff\ee\00\0e\41\64\6f\62\65\00\64\00\00\00\00\00\ff\c3\00\11... # 4492, 1 Item
(fffe,e0dd) na (SequenceDelimitationItem)               #   0, 0 SequenceDelimitationItem

ฉันพยายามใช้โมดูล encapsของpydicomแต่ฉันคิดว่าส่วนใหญ่สำหรับการอ่านข้อมูลไม่ใช่การเขียน ใครบ้างมีความคิดวิธีจัดการกับปัญหานี้วิธีการสร้าง / เข้ารหัสเหล่านี้PixelSequenceหรือไม่ ชอบที่จะสร้าง JPEG บีบอัด DICOMs ใน Python ธรรมดาโดยไม่ต้องใช้เครื่องมือภายนอก


คุณสามารถอ่านไฟล์ภาพ JPEG ที่ถูกบีบอัดผ่าน PyDicom ได้หรือไม่?
norok2

ใช่แน่นอนฉันสามารถขยายและอ่านมันได้ แน่นอนว่าคุณต้องติดตั้งไลบรารีเพิ่มเติมสองสามตัวต่อไปนี้เป็นชุดค่าผสมที่เป็นไปได้: pydicom.github.io/pydicom/stable/image_data_handlers.html
mseimys

กรณีการใช้งานนี้ได้รับการแก้ไขทุกครั้งหรือไม่ ฉันชอบที่จะดูเอกสารเกี่ยวกับมันด้วยตัวเอง
Steven Hart

คำตอบ:


4

DICOM ต้องการข้อมูลพิกเซลที่บีบอัดต้องถูกห่อหุ้ม (ดูตารางโดยเฉพาะ) เมื่อคุณมีข้อมูลภาพที่ถูกบีบอัดแล้วคุณสามารถใช้วิธีencaps.encapsulate ()เพื่อสร้างbytesที่เหมาะสมสำหรับใช้กับPixel Data :

from pydicom.encaps import encapsulate

# encapsulate() requires a list of bytes, one item per frame
ds.PixelData = encapsulate([ensure_even(output.getvalue())])
# Need to set this flag to indicate the Pixel Data is compressed
ds['PixelData'].is_undefined_length = True
ds.PhotometricInterpretation = "YBR_FULL_422"
ds.file_meta.TransferSyntaxUID = JPEGExtended

ds.save_as("output-jpeg.dcm", write_like_original=False)

ใช้งานได้ แต่ใช้กับ jpeg แบบเฟรมเดียวเท่านั้น ใครรู้วิธีเข้ารหัส jpeg multiframe?
สตีเวนฮาร์ต

แต่ละเฟรมจะต้องมีการเข้ารหัสแยกต่างหากจากนั้นเฟรมทั้งหมดจะถูกห่อหุ้มด้วยencapsulate([frame1, frame2, ...])
scaramallion

1

ลองใช้วิธีแก้ปัญหาจาก @scaramallion โดยมีรายละเอียดเพิ่มเติมให้ดู:

import numpy as np
from PIL import Image
import io

# set some parameters
num_frames = 4
img_size = 10

# Create a fake RGB dataset
random_image_array = (np.random.random((num_frames, img_size, img_size, 3))*255).astype('uint8')
# Convert to PIL
imlist = []
for i in range(num_frames):   # convert the multiframe image into RGB of single frames (Required for compression)
    imlist.append(Image.fromarray(tmp))

# Save the multipage tiff with jpeg compression
f = io.BytesIO()
        imlist[0].save(f, format='tiff', append_images=imlist[1:], save_all=True, compression='jpeg')
# The BytesIO object cursor is at the end of the object, so I need to tell it to go back to the front
f.seek(0)
img = Image.open(f)

# Get each one of the frames converted to even numbered bytes
img_byte_list = []
for i in range(num_frames):
    try:
        img.seek(i)
        with io.BytesIO() as output:
            img.save(output, format='jpeg')
            img_byte_list.append(output.getvalue())
    except EOFError:
         # Not enough frames in img
         break

ds.PixelData = encapsulate([x for x in img_byte_list])
ds['PixelData'].is_undefined_length = True
ds.is_implicit_VR = False
ds.LossyImageCompression = '01'
ds.LossyImageCompressionRatio = 10 # default jpeg
ds.LossyImageCompressionMethod = 'ISO_10918_1'
ds.file_meta.TransferSyntaxUID = '1.2.840.10008.1.2.4.51'

ds.save_as("output-jpeg.dcm", write_like_original=False)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.