Làm cách nào để tạo tập dữ liệu DICOM nén JPEG bằng pydicom?


14

Tôi đang cố gắng tạo một hình ảnh DICOM nén JPEG bằng pydicom . Một tài liệu nguồn hay về hình ảnh DICOM đầy màu sắc có thể được tìm thấy ở đây , nhưng chủ yếu là lý thuyết và C ++. Trong ví dụ mã bên dưới, tôi tạo một dấu chấm lửng màu xanh nhạt bên trong output-raw.dcm(không nén) trông giống như thế này:

Hình ảnh mẫu 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

Cuối cùng, tôi đang cố gắng tạo DICOM nén: Tôi đã thử thiết lập các cú pháp chuyển, nén khác nhau bằng PIL, nhưng không gặp may. Tôi tin rằng tệp DICOM được tạo bị hỏng. Nếu tôi chuyển đổi tệp DICOM thô thành JPEG được nén bằng công cụ gdcm:

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

Bằng cách thực hiện dcmdumptrên tệp được chuyển đổi này, chúng ta có thể thấy một cấu trúc thú vị mà tôi không biết cách tái tạo bằng 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

Tôi đã cố gắng sử dụng mô-đun đóng gói của pydicom , nhưng tôi nghĩ rằng nó chủ yếu để đọc dữ liệu, không phải viết. Bất cứ ai khác có bất kỳ ý tưởng làm thế nào để đối phó với vấn đề này, làm thế nào để tạo / mã hóa những PixelSequences này ? Rất thích tạo các DICOM nén JPEG bằng Python đơn giản mà không cần chạy các công cụ bên ngoài.


Bạn có thể đọc ảnh nén JPEG qua PyDicom không?
norok2

Vâng tất nhiên tôi có thể giải nén và đọc nó. Tất nhiên, bạn cần thêm một vài thư viện được cài đặt, đây là các kết hợp có thể có: pydicom.github.io/pydicom/ sóng / image_data_handlers.html
mseimys

Có phải trường hợp sử dụng này được giải quyết? Tôi muốn xem một số tài liệu về nó bản thân mình.
Steven Hart

Câu trả lời:


4

DICOM yêu cầu dữ liệu Pixel được nén được đóng gói (đặc biệt là xem các bảng). Khi bạn có dữ liệu hình ảnh nén của mình, bạn có thể sử dụng phương thức encaps.encapsulation () để tạo bytesphù hợp để sử dụng với Dữ liệu Pixel :

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)

Điều này hoạt động, nhưng chỉ cho jpeg khung đơn. Bất cứ ai cũng biết làm thế nào để mã hóa một jpeg đa khung?
Steven Hart

Mỗi khung cần được mã hóa riêng biệt và sau đó tất cả các khung được gói gọnencapsulate([frame1, frame2, ...])
Scaramallion

1

Đang thử giải pháp từ @scaramallion, với nhiều chi tiết hơn sẽ hoạt động:

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.