From patchwork Mon Jun 24 15:48:13 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Shivam Goyal X-Patchwork-Id: 13689 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 34D204477D6 for ; Mon, 24 Jun 2019 18:49:27 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 09C6668A8E5; Mon, 24 Jun 2019 18:49:27 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail3.iitk.ac.in (mail3.iitk.ac.in [202.3.77.190]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id A0E3568A864 for ; Mon, 24 Jun 2019 18:49:18 +0300 (EEST) Received: from smtp.cc.iitk.ac.in (smtp.cc.iitk.ac.in [172.31.1.22]) (using TLSv1 with cipher ADH-CAMELLIA256-SHA (256/256 bits)) (No client certificate requested) by mail3.iitk.ac.in (Postfix) with ESMTPS id B5AD2100009F for ; Mon, 24 Jun 2019 21:19:15 +0530 (IST) Received: from [172.24.33.217] (unknown [172.24.33.217]) (using TLSv1 with cipher AES128-SHA (128/128 bits)) (No client certificate requested) (Authenticated sender: shivgo) by smtp.cc.iitk.ac.in (Postfix) with ESMTPSA id A668751 for ; Mon, 24 Jun 2019 21:19:15 +0530 (IST) From: Shivam To: ffmpeg-devel@ffmpeg.org Message-ID: <79072192-5b9d-5255-255f-24e289e0efb7@iitk.ac.in> Date: Mon, 24 Jun 2019 21:18:13 +0530 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.7.1 MIME-Version: 1.0 Content-Language: en-US Subject: [FFmpeg-devel] [PATCH] Add DICOM Support X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Hi!     The code is to add DICOM Support. The patch is only for uncompressed dicom files using explicit value representation. I would extend it, once i clarify some doubts.     As dicom image files contain lots of metadata about the patient. So, should i display that data while demuxing or should i ignore and only demux the image data ?. In the current patch, i have made an option "-metadata", which when used will print the data on the terminal while demuxing. Also, in the dicomdict.c ( which is only required for implicit dicom files, and for metadata), till now i have only specified ~200 data elements. but the dicom specification, specifies a total of ~4000 data elements (would do binary search if all the tags are needed ). So, is there a better way to specifiy that data?. (I mean if we ignore the metadata about the patient, then we only need to specify ~150 data elements for image data). I have uploaded some samples of dicom files with explicit value representation here (in case needed). https://drive.google.com/drive/folders/1V8HUNeX3EYiPLj_dcFt8C68tAh7C7v4X?usp=sharing Please comment, Thank you , Shivam Goyal From d394fba8d63c3a81495b42958e9abd42757d2495 Mon Sep 17 00:00:00 2001 From: Shivam Goyal Date: Mon, 24 Jun 2019 21:09:14 +0530 Subject: [PATCH] lavf: Add DICOM demuxer, lavc: Add DICOM decoder --- libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/avcodec.h | 1 + libavcodec/codec_desc.c | 7 + libavcodec/dicom.c | 94 ++++++++++++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/dicom.h | 83 +++++++++++ libavformat/dicomdec.c | 308 +++++++++++++++++++++++++++++++++++++++ libavformat/dicomdict.c | 280 +++++++++++++++++++++++++++++++++++ 10 files changed, 777 insertions(+) create mode 100644 libavcodec/dicom.c create mode 100644 libavformat/dicom.h create mode 100644 libavformat/dicomdec.c create mode 100644 libavformat/dicomdict.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index edccd73037..87ae3ea048 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -263,6 +263,7 @@ OBJS-$(CONFIG_DCA_DECODER) += dcadec.o dca.o dcadata.o dcahuff.o \ OBJS-$(CONFIG_DCA_ENCODER) += dcaenc.o dca.o dcadata.o dcahuff.o \ dcaadpcm.o OBJS-$(CONFIG_DDS_DECODER) += dds.o +OBJS-$(CONFIG_DICOM_DECODER) += dicom.o OBJS-$(CONFIG_DIRAC_DECODER) += diracdec.o dirac.o diracdsp.o diractab.o \ dirac_arith.o dirac_dwt.o dirac_vlc.o OBJS-$(CONFIG_DFA_DECODER) += dfa.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index d2f9a39ce5..59d7b83625 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -83,6 +83,7 @@ extern AVCodec ff_cscd_decoder; extern AVCodec ff_cyuv_decoder; extern AVCodec ff_dds_decoder; extern AVCodec ff_dfa_decoder; +extern AVCodec ff_dicom_decoder; extern AVCodec ff_dirac_decoder; extern AVCodec ff_dnxhd_encoder; extern AVCodec ff_dnxhd_decoder; diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 586bbbca4e..5d5e71cf10 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -410,6 +410,7 @@ enum AVCodecID { AV_CODEC_ID_SCREENPRESSO, AV_CODEC_ID_RSCC, AV_CODEC_ID_AVS2, + AV_CODEC_ID_DICOM, AV_CODEC_ID_Y41P = 0x8000, AV_CODEC_ID_AVRP, diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 4d033c20ff..edab71f815 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -1656,6 +1656,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("FITS (Flexible Image Transport System)"), .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, + { + .id = AV_CODEC_ID_DICOM, + .type = AVMEDIA_TYPE_VIDEO, + .name = "dicom", + .long_name = NULL_IF_CONFIG_SMALL("DICOM (Digital Imaging and Communications in Medicine)"), + .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_LOSSLESS, + }, { .id = AV_CODEC_ID_IMM4, .type = AVMEDIA_TYPE_VIDEO, diff --git a/libavcodec/dicom.c b/libavcodec/dicom.c new file mode 100644 index 0000000000..226f3fece3 --- /dev/null +++ b/libavcodec/dicom.c @@ -0,0 +1,94 @@ +/* + * DICOM decoder + * Copyright (c) 2019 Shivam Goyal + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "avcodec.h" +#include "bytestream.h" +#include "bmp.h" +#include "internal.h" + + +static void apply_window_level(short * vals, uint8_t *ptr, int window, int level, int bitmask, int size) +{ + int i, max, min; + short val; + + max = level + window / 2; + min = level - window / 2; + + for (i = 0; i < size; i++){ + val = vals[i]; + + if (val > max) { + ptr[i] = 255; + } else if (val < min) { + ptr[i] = 0; + } else { + ptr[i] = ((val & bitmask) - min) * 255 / (max - min); + } + } + return; + +} + +static int dicom_decode_frame(AVCodecContext *avctx, + void *data, int *got_frame, + AVPacket *avpkt) +{ + const uint8_t *buf = avpkt->data; + int buf_size = avpkt->size; + AVFrame *p = data; + short *vals; + int width; + int height; + uint8_t *ptr; + int ret, bitmask, window, level; + + width = avctx->width; + height = avctx->height; + window = avctx->profile; + level = avctx->level; + avctx->pix_fmt = AV_PIX_FMT_GRAY8; + if ((ret = ff_get_buffer(avctx, p, 0)) < 0) + return ret; + p->pict_type = AV_PICTURE_TYPE_I; + p->key_frame = 1; + + vals = (short *) buf; + bitmask = (1 << 16) - 1; + + ptr = p->data[0]; + + apply_window_level(vals, ptr, window, level, bitmask, width * height); + + *got_frame = 1; + return buf_size; +} + +AVCodec ff_dicom_decoder = { + .name = "dicom", + .long_name = NULL_IF_CONFIG_SMALL("DICOM (Digital Imaging and Communications in Medicine)"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_DICOM, + .decode = dicom_decode_frame, + .capabilities = AV_CODEC_CAP_DR1, +}; diff --git a/libavformat/Makefile b/libavformat/Makefile index a434b005a4..58ba6a4c36 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -150,6 +150,7 @@ OBJS-$(CONFIG_DAUD_MUXER) += daudenc.o OBJS-$(CONFIG_DCSTR_DEMUXER) += dcstr.o OBJS-$(CONFIG_DFA_DEMUXER) += dfa.o OBJS-$(CONFIG_DHAV_DEMUXER) += dhav.o +OBJS-$(CONFIG_DICOM_DEMUXER) += dicomdec.o dicomdict.o OBJS-$(CONFIG_DIRAC_DEMUXER) += diracdec.o rawdec.o OBJS-$(CONFIG_DIRAC_MUXER) += rawenc.o OBJS-$(CONFIG_DNXHD_DEMUXER) += dnxhddec.o rawdec.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index cd00834807..c5120ef1df 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -111,6 +111,7 @@ extern AVOutputFormat ff_daud_muxer; extern AVInputFormat ff_dcstr_demuxer; extern AVInputFormat ff_dfa_demuxer; extern AVInputFormat ff_dhav_demuxer; +extern AVInputFormat ff_dicom_demuxer; extern AVInputFormat ff_dirac_demuxer; extern AVOutputFormat ff_dirac_muxer; extern AVInputFormat ff_dnxhd_demuxer; diff --git a/libavformat/dicom.h b/libavformat/dicom.h new file mode 100644 index 0000000000..51ad99b2e8 --- /dev/null +++ b/libavformat/dicom.h @@ -0,0 +1,83 @@ +/* + * DICOM demuxer + * + * Copyright (c) 2019 Shivam Goyal + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#define DICOM_PREAMBLE_SIZE 128 +#define DICOM_PREFIX_SIZE 4 + +#define IMAGE_GR_NB 0x0028 +#define PIXEL_GR_NB 0x7FE0 +#define PIXELDATA_EL_NB 0x0010 +#define TRANSFER_SYNTEX_UID_GR_NB 0x0002 +#define TRANSFER_SYNTEX_UID_EL_NB 0x0010 +#define DEFAULT_WINDOW 1100 +#define DEFAULT_LEVEL 125 + +typedef enum { + AE = 0x4145, + AS = 0x4153, + AT = 0x4154, + CS = 0x4353, + DA = 0x4441, + DS = 0x4453, + DT = 0x4454, + FD = 0x4644, + FL = 0x464c, + IS = 0x4953, + LO = 0x4c4f, + LT = 0x4c54, + OB = 0x4f42, + OD = 0x4f44, + OF = 0x4f46, + OL = 0x4f4c, + OV = 0x4f56, + OW = 0x4f57, + PN = 0x504e, + SH = 0x5348, + SL = 0x534c, + SQ = 0x5351, + SS = 0x5353, + ST = 0x5354, + SV = 0x5356, + TM = 0x544d, + UC = 0x5543, + UI = 0x5549, + UL = 0x554c, + UN = 0x554e, + UR = 0x5552, + US = 0x5553, + UT = 0x5554, + UV = 0x5556 +} ValueRepresentation; + + +typedef struct DataElement{ + uint16_t GroupNumber; + uint16_t ElementNumber; + ValueRepresentation VR; + uint32_t VL; + void* bytes; + int index; // Index in dicom dictionary + char* desc; +} DataElement; + +int dicom_dict_find_elem_info (DataElement *de); diff --git a/libavformat/dicomdec.c b/libavformat/dicomdec.c new file mode 100644 index 0000000000..9ab560c2b1 --- /dev/null +++ b/libavformat/dicomdec.c @@ -0,0 +1,308 @@ +/* + * DICOM demuxer + * + * Copyright (c) 2019 Shivam Goyal + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "libavutil/dict.h" +#include "libavutil/opt.h" +#include "libavutil/bprint.h" +#include "avformat.h" +#include "internal.h" +#include "dicom.h" + + +typedef struct DICOMContext { + const AVClass *class; ///< Class for private options. + int big_endian; + + uint64_t nb_frames; + uint16_t bits_allocated; + uint16_t bits_stored; + uint16_t high_bit; + uint16_t rows; + uint16_t columns; + uint32_t nb_DEs; + int window; + int level; + int metadata; + char *transer_syntext_uid; +} DICOMContext; + + +static int dicom_probe(const AVProbeData *p) +{ + const uint8_t *d = p->buf; + + if (d[DICOM_PREAMBLE_SIZE] == 'D' && + d[DICOM_PREAMBLE_SIZE + 1] == 'I' && + d[DICOM_PREAMBLE_SIZE + 2] == 'C' && + d[DICOM_PREAMBLE_SIZE + 3] == 'M') { + return AVPROBE_SCORE_MAX; + } + return 0; +} + +static int set_imagegroup_data(AVFormatContext *s, AVStream* st, DataElement *de) +{ + DICOMContext *dicom = s->priv_data; + void *bytes = de->bytes; + int len = de->VL; + char *cbytes; + + if (de->GroupNumber != IMAGE_GR_NB) + return 0; + + switch (de->ElementNumber) { + case 0x0010: + dicom->rows = *(uint16_t *)bytes; + st->codecpar->height = dicom->rows; + break; + case 0x0011: + dicom->columns = *(uint16_t *)bytes; + st->codecpar->width = dicom->columns; + break; + case 0x0100: + dicom->bits_allocated = *(uint16_t *)bytes; + st->codecpar->bits_per_raw_sample = dicom->bits_allocated; + break; + case 0x0101: + dicom->bits_stored = *(uint16_t *)bytes; + st->codecpar->bits_per_coded_sample = dicom->bits_stored; + break; + case 0x0102: + dicom->high_bit = *(uint16_t *)bytes; + break; + case 0x0008: + dicom->nb_frames = *(uint32_t *)bytes; + break; + case 0x1050: + if (dicom->level == -1) { + cbytes = av_malloc(len + 1); + memcpy(cbytes, bytes, len); + cbytes[len] = 0; + st->codecpar->level = atoi(cbytes); + dicom->level = st->codecpar->level; + av_free(cbytes); + } + break; + case 0x1051: + if (dicom->window == -1) { + cbytes = av_malloc(len + 1); + memcpy(cbytes, bytes, len); + cbytes[len] = 0; + st->codecpar->profile = atoi(cbytes); + dicom->level = st->codecpar->profile; + av_free(cbytes); + } + break; + } + return 0; +} + + + +static int read_data_element_metainfo(AVFormatContext *s, DataElement *de) +{ + ValueRepresentation vr; + int ret; + + ret = avio_rl16(s->pb); + if (ret < 0) + return ret; + de->GroupNumber = ret; + + de->ElementNumber = avio_rl16(s->pb); + + vr = avio_rb16(s->pb); // Stored in Big Endian in dicom.h + de->VR = vr; + + switch (vr) { + case OB: + case OD: + case OF: + case OL: + case OV: + case OW: + case SQ: + case SV: + case UC: + case UR: + case UT: + case UN: + case UV: + avio_skip(s->pb, 2); // Padding always 0x0000 + de->VL = avio_rl32(s->pb); + if (de->VL % 2) + av_log(s, AV_LOG_WARNING,"Data Element Value length:%d can't be odd\n", de->VL); + break; + default: + de->VL = avio_rl16(s->pb); + if (de->VL % 2) + av_log(s, AV_LOG_WARNING,"Data Element Value length:%d can't be odd\n", de->VL); + break; + } + return de->VL; +} + +static int read_data_element_valuefield(AVFormatContext *s, DataElement *de) +{ + int ret; + uint32_t len = de->VL; + + de->bytes = av_malloc(len); + ret = avio_read(s->pb, de->bytes, len); + return ret; +} + +static int print_data_element_metadata(AVFormatContext *s, DataElement *de) { + av_log(s, AV_LOG_INFO,"(%04x, %04x)\tVR=%c%c\tVL=%d\t",de->GroupNumber, de->ElementNumber, de->VR>>8, de->VR - ((de->VR>>8)<<8), de->VL); + return 0; +} + +static int print_data_element_value(AVFormatContext *s, DataElement *de) { + ValueRepresentation vr = de->VR; + uint32_t len = de->VL; + void *data = de->bytes; + char *cdata; + + switch (vr) { + case AT: + if (len < 4) { + printf("[Invalid]\n"); + return 0; + } + cdata = data; + av_log(s, AV_LOG_INFO, "%04d %04d", cdata[1] << 8 + cdata[0], cdata[4] << 8 + cdata[3]); + break; + case FL: + av_log(s, AV_LOG_INFO, "%f\n", ((float *)data)[0]); + break; + default: + cdata = av_malloc(len + 1); + memcpy(cdata, data, len); + cdata[len] = 0; + av_log(s, AV_LOG_INFO, "%s\n", cdata); + av_free(cdata); + } + return 0; +} + +static int dicom_read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + int ret; + + ret = avio_skip(pb, DICOM_PREAMBLE_SIZE + DICOM_PREFIX_SIZE); + if (ret < 0) + return ret; + + s->ctx_flags |= AVFMTCTX_NOHEADER; + s->start_time = 0; + return 0; +} + +static int dicom_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + DICOMContext *dicom = s->priv_data; + int metadata = dicom->metadata; + AVStream *st; + DataElement *de; + int ret; + + if (s->nb_streams < 1) { + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codecpar->codec_id = AV_CODEC_ID_DICOM; + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + if (st->codecpar->profile != -1) { + st->codecpar->profile = dicom->window; + } + if (st->codecpar->level != -1) { + st->codecpar->level = dicom->level; + } + } else + st = s->streams[0]; + + for (;;) { + ret = avio_feof(s->pb); + if (ret) + return AVERROR_EOF; + + de = av_malloc(sizeof(DataElement)); + ret = read_data_element_metainfo(s,de); + if (ret < 0) + return ret; + + if (de->GroupNumber == IMAGE_GR_NB) { + ret = read_data_element_valuefield(s, de); + if (ret < 0) + return ret; + set_imagegroup_data(s, st, de); + } else if (de->GroupNumber == PIXEL_GR_NB && de->ElementNumber == PIXELDATA_EL_NB) { + if (av_new_packet(pkt, de->VL) < 0) + return AVERROR(ENOMEM); + pkt->pos = avio_tell(s->pb); + pkt->stream_index = 0; + pkt->size = de->VL; + ret = avio_read(s->pb, pkt->data, de->VL); + if (ret < 0) + av_packet_unref(pkt); + return ret; + } + else { + ret = read_data_element_valuefield(s, de); + if (ret < 0) + return ret; + if (metadata) { + print_data_element_metadata(s, de); + print_data_element_value(s,de); + } + } + av_free(de); + } + return AVERROR_EOF; +} + +static const AVOption options[] = { + { "window", "window", offsetof(DICOMContext, window), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 99999, AV_OPT_FLAG_DECODING_PARAM }, + { "level", "level", offsetof(DICOMContext, level), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 99999 , AV_OPT_FLAG_DECODING_PARAM }, + { "metadata", "Print metadata present in dicom file", offsetof(DICOMContext, metadata) , AV_OPT_TYPE_BOOL,{.i64 = 0}, 0,1, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass dicom_class = { + .class_name = "dicomdec", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_dicom_demuxer = { + .name = "dicom", + .long_name = NULL_IF_CONFIG_SMALL("DICOM (Digital Imaging and Communications in Medicine)"), + .priv_data_size = sizeof(DICOMContext), + .read_probe = dicom_probe, + .read_header = dicom_read_header, + .read_packet = dicom_read_packet, + .extensions = "dcm", + .priv_class = &dicom_class, +}; \ No newline at end of file diff --git a/libavformat/dicomdict.c b/libavformat/dicomdict.c new file mode 100644 index 0000000000..c70c03e8a9 --- /dev/null +++ b/libavformat/dicomdict.c @@ -0,0 +1,280 @@ +/* + * DICOM Dictionary + * + * Copyright (c) 2019 Shivam Goyal + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "dicom.h" + +typedef struct DICOMDictionary { + uint16_t GroupNumber; + uint16_t ElementNumber; + ValueRepresentation vr; + char *desc; +} DICOMDictionary; + +DICOMDictionary dicom_dictionary[] = { + {0x0002, 0x0000, UL, "File Meta Elements Group Len"}, + {0x0002, 0x0001, OB, "File Meta Information Version"}, + {0x0002, 0x0002, UI, "Media Storage SOP Class UID"}, + {0x0002, 0x0003, UI, "Media Storage SOP Inst UID"}, + {0x0002, 0x0010, UI, "Transfer Syntax UID"}, + {0x0002, 0x0012, UI, "Implementation Class UID"}, + {0x0002, 0x0013, SH, "Implementation Version Name"}, + {0x0002, 0x0016, AE, "Source Application Entity Title"}, + {0x0002, 0x0017, AE, "Sending Application Entity Title"}, + {0x0002, 0x0018, AE, "Receiving Application Entity Title"}, + {0x0002, 0x0100, UI, "Private Information Creator UID"}, + {0x0002, 0x0102, OB, "Private Information"}, + + + {0x0004, 0x1130, CS, "File-set ID"}, + {0x0004, 0x1141, CS, "File-set Descriptor File ID"}, + {0x0004, 0x1142, CS, "Specific Character Set of File-set Descriptor File"}, + {0x0004, 0x1200, UL, "Offset of the First Directory Record of the Root Directory Entity"}, + {0x0004, 0x1202, UL, "Offset of the Last Directory Record of the Root Directory Entity"}, + {0x0004, 0x1212, US, "File-set Consistency Flag"}, + {0x0004, 0x1220, SQ, "Directory Record Sequence"}, + {0x0004, 0x1400, UL, "Offset of the Next Directory Record"}, + {0x0004, 0x1410, US, "Record In-use Flag"}, + {0x0004, 0x1420, UL, "Offset of Referenced Lower-Level Directory Entity"}, + {0x0004, 0x1430, CS, "Directory Record Type"}, + {0x0004, 0x1432, UI, "Private Record UID"}, + {0x0004, 0x1500, CS, "Referenced File ID"}, + {0x0004, 0x1504, UL, "MRDR Directory Record Offset"}, + {0x0004, 0x1510, UI, "Referenced SOP Class UID in File"}, + {0x0004, 0x1511, UI, "Referenced SOP Instance UID in File"}, + {0x0004, 0x1512, UI, "Referenced Transfer Syntax UID in File"}, + {0x0004, 0x151A, UI, "Referenced Related General SOP Class UID in File"}, + {0x0004, 0x1600, UL, "Number of References"}, + + + {0x0008, 0x0001, UL, "Length to End"}, + {0x0008, 0x0005, CS, "Specific Character Set"}, + {0x0008, 0x0006, SQ, "Language Code Sequence"}, + {0x0008, 0x0008, CS, "Image Type"}, + {0x0008, 0x0010, SH, "Recognition Code"}, + {0x0008, 0x0012, DA, "Instance Creation Date"}, + {0x0008, 0x0013, TM, "Instance Creation Time"}, + {0x0008, 0x0014, UI, "Instance Creator UID"}, + {0x0008, 0x0015, DT, "Instance Create UID"}, + {0x0008, 0x0016, UI, "SOP Class UID"}, + {0x0008, 0x0018, UI, "SOP Instance UID"}, + {0x0008, 0x001A, UI, "Related General SOP Class UID"}, + {0x0008, 0x001B, UI, "Original Specialized SOP Class UID"}, + {0x0008, 0x0020, DA, "Study Date"}, + {0x0008, 0x0021, DA, "Series Date"}, + {0x0008, 0x0022, DA, "Acquisition Date"}, + {0x0008, 0x0023, DA, "Content Date"}, + {0x0008, 0x0024, DA, "Overlay Date"}, + {0x0008, 0x0025, DA, "Curve Date"}, + {0x0008, 0x002A, DT, "Acquisition DateTime"}, + {0x0008, 0x0030, TM, "Study Time"}, + {0x0008, 0x0031, TM, "Series Time"}, + {0x0008, 0x0032, TM, "Acquisition Time"}, + {0x0008, 0x0033, TM, "Content Time"}, + {0x0008, 0x0034, TM, "Overlay Time"}, + {0x0008, 0x0035, TM, "Curve Time"}, + {0x0008, 0x0040, US, "Data Set Type"}, + {0x0008, 0x0041, LO, "Data Set Subtype"}, + {0x0008, 0x0042, CS, "Nuclear Medicine Series Type"}, + {0x0008, 0x0050, SH, "Accession Number"}, + {0x0008, 0x0051, SQ, "Issuer of Accession Number Sequence"}, + {0x0008, 0x0052, CS, "Query/Retrieve Level"}, + {0x0008, 0x0053, CS, "Query/Retrieve View"}, + {0x0008, 0x0054, AE, "Retrieve AE Title"}, + {0x0008, 0x0055, AE, "Station AE Title"}, + {0x0008, 0x0056, CS, "Instance Availability"}, + {0x0008, 0x0058, UI, "Failed SOP Instance UID List"}, + {0x0008, 0x0060, CS, "Modality"}, + {0x0008, 0x0061, CS, "Modalities in Study"}, + {0x0008, 0x0062, UI, "SOP Classes in Study"}, + {0x0008, 0x0064, CS, "Conversion Type"}, + {0x0008, 0x0068, CS, "Presentation Intent Type"}, + {0x0008, 0x0070, LO, "Manufacturer"}, + {0x0008, 0x0080, LO, "Institution Name"}, + {0x0008, 0x0081, ST, "Institution Address"}, + {0x0008, 0x0082, SQ, "Institution Code Sequence"}, + {0x0008, 0x0090, PN, "Referring Physician's Name"}, + {0x0008, 0x0092, ST, "Referring Physician's Address"}, + {0x0008, 0x0094, SH, "Referring Physician's Telephone Numbers"}, + {0x0008, 0x0096, SQ, "Referring Physician Identification Sequence"}, + {0x0008, 0x009C, PN, "Consulting Physician's Name"}, + {0x0008, 0x009D, SQ, "Consulting Physician Identification Sequence"}, + {0x0008, 0x0100, SH, "Code Value"}, + {0x0008, 0x0101, LO, "Extended Code Value"}, + {0x0008, 0x0102, SH, "Coding Scheme Designator"}, + {0x0008, 0x0104, LO, "Code Meaning"}, + {0x0008, 0x0105, CS, "Mapping Resource"}, + {0x0008, 0x0106, DT, "Context Group Version"}, + {0x0008, 0x0107, DT, "Context Group Local Version"}, + {0x0008, 0x0108, LT, "Extended Code Meaning"}, + {0x0008, 0x010C, UI, "Coding Scheme UID"}, + {0x0008, 0x010D, UI, "Context Group Extension Creator UID"}, + {0x0008, 0x010F, CS, "Context Identifier"}, + {0x0008, 0x0110, SQ, "Coding Scheme Identification Sequence"}, + {0x0008, 0x0112, LO, "Coding Scheme Registry"}, + {0x0008, 0x0114, ST, "Coding Scheme External ID"}, + {0x0008, 0x0115, ST, "Coding Scheme Name"}, + {0x0008, 0x0116, ST, "Coding Scheme Responsible Organization"}, + {0x0008, 0x0117, UI, "Context UID"}, + {0x0008, 0x0118, UI, "Mapping Resource UID"}, + {0x0008, 0x0119, UC, "Long Code Value"}, + {0x0008, 0x0120, UR, "URN Code Value"}, + {0x0008, 0x0121, SQ, "Equivalent Code Sequence"}, + {0x0008, 0x0122, LO, "Mapping Resource Name"}, + {0x0008, 0x0123, SQ, "Context Group Identification Sequence"}, + {0x0008, 0x0124, SQ, "Mapping Resource Identification Sequence"}, + {0x0008, 0x0201, SH, "Timezone Offset From UTC"}, + {0x0008, 0x0300, SQ, "Private Data Element Characteristics Sequence"}, + {0x0008, 0x0301, US, "Private Group Reference"}, + {0x0008, 0x0302, LO, "Private Creator Reference"}, + {0x0008, 0x0303, CS, "Block Identifying Information Status"}, + {0x0008, 0x0304, US, "Nonidentifying PrivateElements"}, + {0x0008, 0x0305, SQ, "Deidentification ActionSequence"}, + {0x0008, 0x0306, US, "Identifying PrivateElements"}, + {0x0008, 0x0307, CS, "Deidentification Action"}, + {0x0008, 0x1000, AE, "Network ID"}, + {0x0008, 0x1010, SH, "Station Name"}, + {0x0008, 0x1030, LO, "Study Description"}, + {0x0008, 0x1032, SQ, "Procedure Code Sequence"}, + {0x0008, 0x103E, LO, "Series Description"}, + {0x0008, 0x103F, SQ, "Series Description CodeSequence"}, + {0x0008, 0x1040, LO, "Institutional Department Name"}, + {0x0008, 0x1048, PN, "Physician(s) of Record"}, + {0x0008, 0x1049, SQ, "Physician(s) of Record Identification Sequence"}, + {0x0008, 0x1050, PN, "Attending Physician's Name"}, + {0x0008, 0x1052, SQ, "Performing Physician Identification Sequence"}, + {0x0008, 0x1060, PN, "Name of Physician(s) Reading Study"}, + {0x0008, 0x1062, SQ, "Physician(s) ReadingStudy Identification Sequenc"}, + {0x0008, 0x1070, PN, "Operator's Name"}, + {0x0008, 0x1072, SQ, "Operator Identification Sequence"}, + {0x0008, 0x1080, LO, "Admitting Diagnosis Description"}, + {0x0008, 0x1084, SQ, "Admitting Diagnosis Code Sequence"}, + {0x0008, 0x1090, LO, "Manufacturer's Model Name"}, + {0x0008, 0x1100, SQ, "Referenced Results Sequence"}, + {0x0008, 0x1110, SQ, "Referenced Study Sequence"}, + {0x0008, 0x1111, SQ, "Referenced Study Component Sequence"}, + {0x0008, 0x1115, SQ, "Referenced Series Sequence"}, + {0x0008, 0x1120, SQ, "Referenced Patient Sequence"}, + {0x0008, 0x1125, SQ, "Referenced Visit Sequence"}, + {0x0008, 0x1130, SQ, "Referenced Overlay Sequence"}, + {0x0008, 0x1134, SQ, "Referenced Stereometric Instance Sequence"}, + {0x0008, 0x113A, SQ, "Referenced Waveform Sequence"}, + {0x0008, 0x1140, SQ, "Referenced Image Sequence"}, + {0x0008, 0x1145, SQ, "Referenced Curve Sequence"}, + {0x0008, 0x114A, SQ, "Referenced InstanceSequence"}, + {0x0008, 0x114B, SQ, "Referenced Real World Value Mapping InstanceSequence"}, + {0x0008, 0x1150, UI, "Referenced SOP Class UID"}, + {0x0008, 0x1155, UI, "Referenced SOP Instance UID"}, + {0x0008, 0x115A, UI, "SOP Classes Supported"}, + {0x0008, 0x1160, IS, "Referenced Frame Number"}, + {0x0008, 0x1161, UL, "Simple Frame List"}, + {0x0008, 0x1162, UL, "Calculated Frame List"}, + {0x0008, 0x1163, FD, "Time Range"}, + {0x0008, 0x1164, SQ, "Frame Extraction Sequence"}, + {0x0008, 0x1167, UI, "Multi-frame Source SOP Instance UID"}, + {0x0008, 0x1190, UR, "Retrieve URL"}, + {0x0008, 0x1195, UI, "Transaction UID"}, + {0x0008, 0x1196, US, "Warning Reason"}, + {0x0008, 0x1197, US, "Failure Reason"}, + {0x0008, 0x1198, SQ, "Failed SOP Sequence"}, + {0x0008, 0x1199, SQ, "Referenced SOP Sequence"}, + {0x0008, 0x119A, SQ, "Other Failures Sequence"}, + {0x0008, 0x1200, SQ, "Studies Containing OtherReferenced InstancesSequence"}, + {0x0008, 0x1250, SQ, "Related Series Sequence"}, + {0x0008, 0x2110, CS, "Lossy Image Compression(Retired)"}, + {0x0008, 0x2111, ST, "Derivation Description"}, + {0x0008, 0x2112, SQ, "Source Image Sequence"}, + {0x0008, 0x2120, SH, "Stage Name"}, + {0x0008, 0x2122, IS, "Stage Number"}, + {0x0008, 0x2124, IS, "Number of Stages"}, + {0x0008, 0x2127, SH, "View Name"}, + {0x0008, 0x2128, IS, "View Number"}, + {0x0008, 0x2129, IS, "Number of Event Timers"}, + {0x0008, 0x212A, IS, "Number of Views in Stage"}, + {0x0008, 0x2130, DS, "Event Elapsed Time(s)"}, + {0x0008, 0x2132, LO, "Event Timer Name(s)"}, + {0x0008, 0x2133, SQ, "Event Timer Sequence"}, + {0x0008, 0x2134, FD, "Event Time Offset"}, + {0x0008, 0x2135, SQ, "Event Code Sequence"}, + {0x0008, 0x2142, IS, "Start Trim"}, + {0x0008, 0x2143, IS, "Stop Trim"}, + {0x0008, 0x2144, IS, "Recommended Display Frame Rate"}, + {0x0008, 0x2200, CS, "Transducer Position"}, + {0x0008, 0x2204, CS, "Transducer Orientation"}, + {0x0008, 0x2208, CS, "Anatomic Structure"}, + {0x0008, 0x2218, SQ, "Anatomic RegionSequence"}, + {0x0008, 0x2220, SQ, "Anatomic Region ModifierSequence"}, + {0x0008, 0x2228, SQ, "Primary Anatomic Structure Sequence"}, + {0x0008, 0x2229, SQ, "Anatomic Structure, Spaceor Region Sequence"}, + {0x0008, 0x2230, SQ, "Primary Anatomic Structure ModifierSequence"}, + {0x0008, 0x2240, SQ, "Transducer Position Sequence"}, + {0x0008, 0x2242, SQ, "Transducer Position Modifier Sequence"}, + {0x0008, 0x2244, SQ, "Transducer Orientation Sequence"}, + {0x0008, 0x2246, SQ, "Transducer Orientation Modifier Sequence"}, + {0x0008, 0x2251, SQ, "Anatomic Structure SpaceOr Region Code Sequence(Trial)"}, + {0x0008, 0x2253, SQ, "Anatomic Portal Of Entrance Code Sequence(Trial)"}, + {0x0008, 0x2255, SQ, "Anatomic ApproachDirection Code Sequence(Trial)"}, + {0x0008, 0x2256, ST, "Anatomic Perspective Description (Trial)"}, + {0x0008, 0x2257, SQ, "Anatomic Perspective Code Sequence (Trial)"}, + {0x0008, 0x2258, ST, "Anatomic Location Of Examining InstrumentDescription (Trial)"}, + {0x0008, 0x2259, SQ, "Anatomic Location Of Examining InstrumentCode Sequence (Trial)"}, + {0x0008, 0x225A, SQ, "Anatomic Structure SpaceOr Region Modifier CodeSequence (Trial)"}, + {0x0008, 0x225C, SQ, "On Axis Background Anatomic Structure CodeSequence (Trial)"}, + {0x0008, 0x3001, SQ, "Alternate Representation Sequence"}, + {0x0008, 0x3010, UI, "Irradiation Event UID"}, + {0x0008, 0x3011, SQ, "Source Irradiation Event Sequence"}, + {0x0008, 0x2012, UI, "Radiopharmaceutical Administration Event UID"}, + {0x0008, 0x4000, LT, "Identifying Comments"}, + {0x0008, 0x9007, CS, "Frame Type"}, + {0x0008, 0x9092, SQ, "Referenced ImageEvidence Sequence"}, + {0x0008, 0x9121, SQ, "Referenced Raw DataSequence"}, + {0x0008, 0x9123, UI, "Creator-Version UID"}, + {0x0008, 0x9124, SQ, "Derivation ImageSequence"}, + {0x0008, 0x9154, SQ, "Source Image EvidenceSequence"}, + {0x0008, 0x9205, CS, "Pixel Presentation"}, + {0x0008, 0x9206, CS, "Volumetric Properties"}, + {0x0008, 0x9207, CS, "Volume Based Calculation Technique"}, + {0x0008, 0x9208, CS, "Complex Image Component"}, + {0x0008, 0x9209, CS, "Acquisition Contrast"}, + {0x0008, 0x9215, SQ, "Derivation Code Sequence"}, + {0x0008, 0x9237, SQ, "Referenced Presentation State Sequence"}, + {0x0008, 0x9410, SQ, "Referenced Other Plane Sequence"}, + {0x0008, 0x9458, SQ, "Frame Display Sequence"}, + {0x0008, 0x9459, FL, "Recommended DisplayFrame Rate in Float"}, + {0x0008, 0x9460, CS, "Skip Frame Range Flag"}, +}; + +int dicom_dict_find_elem_info(DataElement *de) { + int i, len; + + if (!de->GroupNumber || !de->ElementNumber) + return -2; + len = sizeof(dicom_dictionary) / sizeof(dicom_dictionary[0]); + for (int i = 0; i < len; i++) { + if (de->GroupNumber == dicom_dictionary[i].GroupNumber + && de->ElementNumber == dicom_dictionary[i].ElementNumber) { + de->VR = dicom_dictionary[i].vr; + return 0; + } + } + return -1; +} \ No newline at end of file -- 2.22.0