Краткая версия: откуда берется 16-байтовое смещение при экспорте видеопотока MPEG-4 из файла DICOM с помощью Pydicom с помощью следующего кода? (И дополнительный вопрос: всегда ли это смещение на 16 байт?)
from pathlib import Path
import pydicom
in_dcm_filename: str = ...
out_mp4_filename: str = ...
ds = pydicom.dcmread(in_dcm_filename)
Path(out_mp4_filename).write_bytes(ds.PixelData[16:]) # 16-byte offset necessary
Для воспроизводимости можно использовать, например, этот файл DICOM , который я нашел в этом старом обсуждении в группах Google (предупреждение о содержании: видео показывает открытый мозг во время нейрохирургического вмешательства).
У меня есть несколько файлов DICOM, содержащих хирургические видеопотоки MPEG-4 (UID синтаксиса передачи 1.2.840.10008.1.2.4.102 – MPEG-4 AVC/H.264 High Profile/Level 4.1). Я хотел экспортировать видеопотоки из файлов DICOM для упрощения обработки последующих задач.
Немного погуглив, я нашел следующее обсуждение , предлагающее использование dcmdump
из DCMTK следующим образом (которое мне удалось воспроизвести):
dcmdump +P 7fe0,0010 <in_dcm_filename> +W <out_folder>
.<out_folder>
, mpeg4.dcm.0.raw
и mpeg4.dcm.1.raw
отбросьте первый, размер которого равен 0 байт, и сохраните второй (возможно, изменив его суффикс на .mp4
), который представляет собой обычный воспроизводимый видеофайл.Из того, что я увидел в команде dcmdump
, я пришел к выводу, что это просто необработанный дамп тега 7fe0,0010
(который является атрибутом пиксельных данных), поэтому я подумал, что смогу воспроизвести это с помощью Pydicom
. Моя первая попытка заключалась в использовании Path(out_mp4_filename).write_bytes(ds.PixelData)
(полную информацию см. в примере кода выше); однако в итоге у меня получился файл, который невозможно было воспроизвести. Затем я сравнил шестнадцатеричный дамп результата dcmdump
и результата Pydicom
:
$ hd ./dcmdump.mp4 | head
00000000 00 00 00 20 66 74 79 70 69 73 6f 6d 00 00 02 00 |... ftypisom....|
00000010 69 73 6f 6d 69 73 6f 32 61 76 63 31 6d 70 34 31 |isomiso2avc1mp41|
00000020 00 00 00 08 66 72 65 65 00 ce 97 1d 6d 64 61 74 |....free....mdat|
...
$ hd ./pydicom.mp4 | head
00000000 fe ff 00 e0 00 00 00 00 fe ff 00 e0 3e bc ce 00 |............>...|
00000010 00 00 00 20 66 74 79 70 69 73 6f 6d 00 00 02 00 |... ftypisom....|
00000020 69 73 6f 6d 69 73 6f 32 61 76 63 31 6d 70 34 31 |isomiso2avc1mp41|
...
При этом я заметил, что мой экспорт Pydicom
содержал 16 предшествующих дополнительных байтов. Как только я удалил их с помощью Path(out_mp4_filename).write_bytes(ds.PixelData[16:])
, я получил точно такой же воспроизводимый экспорт видео, как и с dcmdump
.
Итак, еще раз, мой вопрос: откуда берутся эти 16 дополнительных байтов, каково их значение и можно ли безопасно просто удалить их?
Причина, по которой вы видите эти байты, заключается в том, что данные пикселей инкапсулированы. Использование dcmdump ясно показывает это:
(7fe0,0010) OB (PixelSequence #=2) # u/l, 1 PixelData
(fffe,e000) pi (no value available) # 0, 1 Item
(fffe,e000) pi 00\00\00\20\66\74\79\70\69\73\6f\6d\00\00\02\00\69\73\6f\6d\69\73... # 13548606, 1 Item
(fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem
Если вы проверите удаляемые ведущие байты, вы увидите, что они содержат соответствующие теги-разделители, как показано в выводе дампа. Вы также можете увидеть, что там содержатся 2 элемента, первый из них пустой — это те, которые вы получаете с помощью dcmtk.
Чтобы получить инкапсулированное содержимое, вы можете использовать encaps.defragment_data
в pydicom 2.x, который возвращает все содержащиеся фрагменты в одном блоке данных (в pydicom 3 интерфейс изменится, чтобы выдавать по одному фрагменту за раз):
from pydicom import dcmread, encaps
ds = dcmread"test_720.dcm")
with open("test_720.mpeg4", "wb") as f:
f.write(encaps.defragment_data(ds.PixelData))
Обратите внимание, что в целом фрагменты являются частями многокадровых данных (в наиболее распространенном случае — по одному фрагменту на кадр), и вы можете обрабатывать их отдельно. В случае MPEG4 существует только один непрерывный поток данных с видеоданными, и правильным способом решения этой проблемы является объединение любых фрагментов, на которые он может быть разделен.
Правда, фрагменты срастаются, что во многих случаях может вообще не быть проблемой. Поправлю в ответе, было неряшливо...
Еще раз большое спасибо – и за ответ, и за разъяснения! Я не очень хорошо знаком с внутренним устройством DICOM, поэтому очень ценю ваше время и усилия.
Большое спасибо! Я не знал об инкапсуляции и фрагментах. Чтобы внести ясность в отношении вашего утверждения: «в вашем случае… один инкапсулированный фрагмент с видеоданными…»: разве в моем случае нет двух фрагментов (0-байтовый и соответствующий) и
defragment_data()
принимает заботиться о них, соединяя их? Другими словами: два элемента, содержащиеся в выводеdcmdump
, — что это, если не фрагменты?