From d394fba8d63c3a81495b42958e9abd42757d2495 Mon Sep 17 00:00:00 2001
From: Shivam Goyal <shivamgoyal1506@outlook.com>
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 <inttypes.h>
+
+#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

