Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mutiple Qs]: convert .jpg to .dcm, no dicom header, could't not find declaration module ''dcmjs",... #333

Closed
chenkenkone opened this issue Dec 22, 2022 · 5 comments

Comments

@chenkenkone
Copy link

chenkenkone commented Dec 22, 2022

Having problem in converting .jpg to .dcm. I've read about Convert jpeg file ( .jpeg ) to dicom file ( .dcm ) #85 and try it out. However there are few things I can't solve.

Q1: For the first one, I can't figure it out how this generate.js convert .jpg file to .dcm file? Isn't it just create a object and make it as the configuration dataset? And the store in buffer? There is no source or output file directory infomation in metioned article or code, that's why I got confused.

Q2: So, I tried this(most simply compy from the generate.js, I need to successfully create dicom file first and then to match my business logics later), the concept is concat the Dataset()Buffer and Image(.jpg)Buffer together to create a file, not sure is it a right way.

    // Get picture's pixle data
    var jpeg = require('jpeg-js');
    var jpegData = fs.readFileSync('./upload/image/4/fa50f460-732f-11ed-ae7d-cfd6aeaf2db8.jpg');
    var rawImageData = jpeg.decode(jpegData, {useTArray: true}); // return as Uint8Array
    console.log("rawImageData:", rawImageData);
    /*
    rawImageData: {
    width: 640,
    height: 480,
    exifBuffer: undefined,
    data: Uint8Array(1228800) [
        106, 109, 104, 255, 104, 107, 102, 255, 103, 106, 101, 255,
        104, 107, 102, 255, 105, 108, 101, 255, 103, 106,  99, 255,
        102, 105,  98, 255, 102, 105,  98, 255, 105, 108, 101, 255,
        106, 109, 102, 255, 107, 110, 103, 255, 106, 109, 102, 255,
        104, 108,  98, 255, 103, 107,  97, 255, 106, 110, 100, 255,
        108, 112, 102, 255, 107, 112, 106, 255, 106, 111, 105, 255,
        105, 110, 104, 255, 105, 110, 104, 255, 105, 110, 104, 255,
        106, 111, 105, 255, 107, 112, 106, 255, 107, 112, 106, 255,
        107, 112, 106, 255,
        ... 1228700 more items
    ] // typed array
    } 
    */
    const jsonDataset = `{
        "AccessionNumber": "",
        "AcquisitionMatrix": [ 256, 0, 0, 192 ],
        "AcquisitionNumber": "8",
        "BitsAllocated": 16,
        "BitsStored": 16,
        "Columns": ${rawImageData.width},
        "ContentDate": "20020628",
        "ContentTime": "164936.0",
        "ContrastBolusAgent": "",
        "DeviceSerialNumber": "4168496325",
        "EchoTime": "6",
        "EchoTrainLength": "1",
        "FlipAngle": "3",
        "FrameOfReferenceUID": "2.16.840.1.113662.4.4168496325.1025305873.7118351817185979330",
        "HighBit": 15,
        "ImageOrientationPatient": [ "0.00000e+00", "1.00000e+00", "-0.00000e+00", "-0.00000e+00", "0.00000e+00", "-1.00000e+00" ],
        "ImagePositionPatient": [ "6.454490e+01", "-1.339286e+02", "1.167857e+02" ],
        "ImageType": [ "ORIGINAL", "PRIMARY", "OTHER" ],
        "ImagesInAcquisition": "130",
        "ImagingFrequency": "6.37165e+01",
        "InstanceNumber": "18",
        "InstitutionName": "UCI Medical Center",
        "MRAcquisitionType": "",
        "MagneticFieldStrength": "1.5",
        "Manufacturer": "Marconi Medical Systems, Inc.",
        "ManufacturerModelName": "Eclipse 1.5T",
        "Modality": "MR",
        "NumberOfAverages": "1",
        "NumberOfTemporalPositions": "1",
        "OperatorsName": "SSRT/^^^^",
        "PatientAge": "039Y",
        "PatientBirthDate": "19000000",
        "PatientID": "0000000",
        "PatientName": "Zzzzzz^Yyyyy^^^",
        "PatientPosition": "HFS",
        "PatientSex": "M",
        "PatientWeight": "99.773300",
        "PercentPhaseFieldOfView": "91.4062",
        "PhotometricInterpretation": "MONOCHROME2",
        "PixelRepresentation": 1,
        "PixelSpacing": [ "1.000000e+00", "1.000000e+00" ],
        "PositionReferenceIndicator": "BRAIN",
        "ProtocolName": "SAG/RF-FAST/VOL/FLIP 3",
        "ReceiveCoilName": "HEAD",
        "ReferencedImageSequence": {
            "ReferencedSOPClassUID": "1.2.840.10008.5.1.4.1.1.4",
            "ReferencedSOPInstanceUID": "2.16.840.1.113662.4.4168496325.1025306066.995213515550827262",
            "_vrMap": {}
        },
        "ReferringPhysicianName": "",
        "RepetitionTime": "20",
        "Rows": ${rawImageData.height},
        "SOPClassUID": "1.2.840.10008.5.1.4.1.1.4",
        "SOPInstanceUID": "2.16.840.1.113662.4.4168496325.1025307679.1207625440068523197",
        "SamplesPerPixel": 1,
        "ScanOptions": "",
        "ScanningSequence": "GR",
        "SequenceVariant": [
            "SS",
            "SP"
        ],
        "SeriesDate": "20020628",
        "SeriesDescription": "SAG/RF-FAST/VOL/FLIP 3",
        "SeriesInstanceUID": "2.16.840.1.113662.4.4168496325.1025307678.3494705239865384161",
        "SeriesNumber": "5",
        "SeriesTime": "164118.0",
        "SliceLocation": "64.544899",
        "SliceThickness": "1.300000e+00",
        "SoftwareVersions": "VIA2.0E.003",
        "SpecificCharacterSet": "ISO_IR 192",
        "StationName": "ba187_ws",
        "StudyDate": "20020628",
        "StudyDescription": "BRAIN",
        "StudyID": "14024",
        "StudyInstanceUID": "2.16.840.1.113662.4.4168496325.1025305873.7118351817185979330",
        "StudyTime": "160956.0",
        "TemporalPositionIdentifier": "1",
        "TemporalResolution": "458660",
        "WindowCenter": "28",
        "WindowWidth": "20",
        "_meta": {
            "FileMetaInformationVersion": {
                "Value": [
                    {
                        "0": 0,
                        "1": 1
                    }
                ],
                "vr": "OB"
            },
            "ImplementationClassUID": {
                "Value": [
                    "1.2.840.113819.7.1.1997.1.0"
                ],
                "vr": "UI"
            },
            "ImplementationVersionName": {
                "Value": [
                    "SENSORSYSTEMS1.0"
                ],
                "vr": "SH"
            },
            "MediaStorageSOPClassUID": {
                "Value": [
                    "1.2.840.10008.5.1.4.1.1.4"
                ],
                "vr": "UI"
            },
            "MediaStorageSOPInstanceUID": {
                "Value": [
                    "2.16.840.1.113662.4.4168496325.1025307679.1207625440068523197"
                ],
                "vr": "UI"
            },
            "TransferSyntaxUID": {
                "Value": [
                    "1.2.840.10008.1.2"
                ],
                "vr": "UI"
            }
        },
        "_vrMap": {
            "PixelData": "OW"
        }
    }`;
    const dataset = JSON.parse(jsonDataset);
    
    pixelData = new Int16Array(dataset.Rows * dataset.Columns);
    pixelData.fill(100);
    dataset.PixelData = pixelData.buffer;
    
    const dicomDict = dcmjs.data.datasetToDict(dataset);
    const buffer = Buffer.from(dicomDict.write());

    const dcmBuffer = Buffer.concat([rawImageData.data, buffer]);

    appendFile("./upload/image/4/fa50f460-732f-11ed-ae7d-cfd6aeaf2db8.dcm", dcmBuffer, (err) => {
        if (err) throw err;
        console.log('The "data to append" was appended to file!');
    });

I can create a .dcm file successfully. But using ezDICOM open the .dcm I got LoadData: Error reading image., using DICOM_Parser_Rubo I got No DICOM Header detected.
So I tried to find something for this issue, I found this #315

dcmjs/src/DicomDict.js

Lines 20 to 24 in 914b3dc

write(writeOptions = { allowInvalidVRLength: false }) {
var metaSyntax = EXPLICIT_LITTLE_ENDIAN;
var fileStream = new WriteBufferStream(4096, true);
fileStream.writeUint8Repeat(0, 128);
fileStream.writeAsciiString("DICM");

So dcmjs will add the header. But I don't understand why I got NO DICOM HEADER detected. And did the "Preamble" exist in the header already too?

Q3: I got this notice. However, when I altered it to const dcmjs = require("dcmjs/build/dcmjs"); the notice was gone. I don't know which would be correct.
截圖 2022-12-22 下午6 10 45

Q4: Why use Int16Array? and Why .fill(100)

    pixelData = new Int16Array(dataset.Rows * dataset.Columns);
    pixelData.fill(100);
    dataset.PixelData = pixelData.buffer;

Thank you for reply.

@pieper
Copy link
Collaborator

pieper commented Dec 22, 2022

It looks like you are on the right track. As mentioned in the thread you linked, the generate.js example creates just a dummy file with nonsense data (an image with the value 100 at every pixel using the fill(100) command. Instead you need to put the real jpg image you read using the library into the PixelData element. Also you. need to adjust the other header tags to match, like you did with the Rows and Columns. My suggestion from #85 is still what I would suggest: generate a valid Secondary Capture image using another tool and use it as the template for creating a new dicom dataset in dcmjs with your header values and pixel data. Please post if you come up with a complete solution or better yet add a pull request with working code in the form of a software test.

@coderider007
Copy link

coderider007 commented Mar 21, 2023

I am able to generate DICOM file from Jpeg image using below code:

`
import dcmjs from 'dcmjs';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const jpeg = require('jpeg-js');

 async createDicomFromImageAndAddToZip(zipUpdated: any, fileData: any, caseData: CaseData): Promise<void> {
    logger.info('update patient details, Uuids and image pixel data of Dicom generated from X-Ray image');

    // Get dicom template data
    const dataset = JSON.parse(CommonConstants.DICOM_META_TEMPLATE);

    // Get json data as dcmjs dicom dictionary
    const dicomDict = dcmjs.data.datasetToDict(dataset);

    // Get jpeg image pixels
    const rawImageData = jpeg.decode(fileData, { formatAsRGBA: false });

    // 1. Set Pixel data with var type Other Bytes
    dicomDict.dict['7FE00010'] = {
        vr: 'OB',
        Value: [rawImageData.data.buffer]
    }

    // Parse the byte array to get a DataSet object that has the parsed contents
    const parsedDataSet = dcmjs.data.DicomMetaDictionary.naturalizeDataset(dicomDict.dict);

    // 2. Set image data rows and columns
    parsedDataSet.Columns = rawImageData.width;
    parsedDataSet.Rows = rawImageData.height;


    // 3. Alter StudyInstanceUID 
    const oldStudyInstanceUID = parsedDataSet.StudyInstanceUID;
    const oldStudyInstanceUIDArray = oldStudyInstanceUID.split('.');
    oldStudyInstanceUIDArray[oldStudyInstanceUIDArray.length - 1] = caseData.id.toString();
    oldStudyInstanceUIDArray[oldStudyInstanceUIDArray.length - 2] = this.getRndInteger(1000, 10000)
    const newStudyInstanceUID = oldStudyInstanceUIDArray.join('.');
    parsedDataSet.StudyInstanceUID = newStudyInstanceUID;

    // 4. Alter SeriesInstanceUID 
    const oldSeriesInstanceUID = parsedDataSet.SeriesInstanceUID;
    const oldSeriesInstanceUIDArray = oldSeriesInstanceUID.split('.');
    oldSeriesInstanceUIDArray[oldSeriesInstanceUIDArray.length - 1] = caseData.id.toString();
    oldSeriesInstanceUIDArray[oldSeriesInstanceUIDArray.length - 2] = this.getRndInteger(10000, 100000)
    const newSeriesInstanceUID = oldSeriesInstanceUIDArray.join('.');
    parsedDataSet.SeriesInstanceUID = newSeriesInstanceUID;

    // 5. Alter SOPInstanceUID 
    const oldInstanceUID = parsedDataSet.SOPInstanceUID;
    const oldInstanceUIDArray = oldInstanceUID.split('.');
    oldInstanceUIDArray[oldInstanceUIDArray.length - 1] = caseData.id.toString();
    oldInstanceUIDArray[oldInstanceUIDArray.length - 2] = this.getRndInteger(100000, 1000000)
    const newInstanceUID = oldInstanceUIDArray.join('.');
    parsedDataSet.SOPInstanceUID = newInstanceUID;

    // 6. Update Patient Details
    // Patient ID
    parsedDataSet.PatientID = caseData.patient.mrNumber ? `${caseData.patient.mrNumber}` : `${caseData.patient.id}`;
    // Patient Name
    parsedDataSet.PatientName = `${caseData.patient.firstName} ${caseData.patient.lastName}`;
    // Patient Gender
    parsedDataSet.PatientSex = caseData.patient.gender == 'male' ? 'M' : (caseData.patient.gender == 'female' ? 'F' : 'O');
    // Patient DoB
    parsedDataSet.PatientBirthDate = utils.getDateAsDicomMetaDate(caseData.patient.dob as any);
    // Patient Age
    parsedDataSet.PatientAge = this.calculateAge(new Date(caseData.patient.dob));

    // Set updated content back to dicom dict
    dicomDict.dict = dcmjs.data.DicomMetaDictionary.denaturalizeDataset(parsedDataSet);
    const updatedDicomArrayBuffer = dicomDict.write();
    const updatedDicomBuffer = Buffer.from(updatedDicomArrayBuffer);

    // add altered dicom to zip
    zipUpdated.addFile('image-' + this.getRndInteger(100, 10000), updatedDicomBuffer);
}

`

static readonly DICOM_META_TEMPLATE = { "AccessionNumber": "", "AcquisitionMatrix": [ 256, 0, 0, 192 ], "AcquisitionNumber": "8", "BitsAllocated": "8", "BitsStored": "8", "Columns": "256", "ContentDate": "20020628", "ContentTime": "164936.0", "ContrastBolusAgent": "", "DeviceSerialNumber": "4168496325", "EchoTime": "6", "EchoTrainLength": "1", "FlipAngle": "3", "FrameOfReferenceUID": "2.16.840.1.113662.4.4168496325.1025305873.7118351817185979330", "HighBit": "7", "ImageComments": "Picture of X-ray", "ImageOrientationPatient": [ "0.00000e+00", "1.00000e+00", "-0.00000e+00", "-0.00000e+00", "0.00000e+00", "-1.00000e+00" ], "ImagePositionPatient": [ "6.454490e+01", "-1.339286e+02", "1.167857e+02" ], "ImageType": [ "ORIGINAL", "PRIMARY", "OTHER" ], "ImagesInAcquisition": "1", "ImagingFrequency": "6.37165e+01", "InstanceNumber": "1", "InstitutionName": "MyOrganization", "LossyImageCompression": "00", "MRAcquisitionType": "", "MagneticFieldStrength": "1.5", "Manufacturer": "MyOrganization", "ManufacturerModelName": "MyOrganization 1.5T", "Modality": "DX", "NumberOfAverages": "1", "NumberOfFrames": "1", "NumberOfTemporalPositions": "1", "OperatorsName": "NA", "PatientAge": "023Y", "PatientBirthDate": "20000101", "PatientID": "0000000", "PatientName": "NA", "PatientPosition": "HFS", "PatientSex": "O", "PatientWeight": "99.773300", "PercentPhaseFieldOfView": "91.4062", "PhotometricInterpretation": "RGB", "PixelRepresentation": 0, "PixelSpacing": [ "0.1171875", "0.1171875" ], "PlanarConfiguration": "0", "PositionReferenceIndicator": "BRAIN", "ProtocolName": "SAG/RF-FAST/VOL/FLIP 3", "ReceiveCoilName": "HEAD", "ReferencedImageSequence": { "ReferencedSOPClassUID": "1.2.840.10008.5.1.4.1.1.4", "ReferencedSOPInstanceUID": "2.16.840.1.113662.4.4168496325.1025306066.995213515550827262", "_vrMap": {} }, "ReferringPhysicianName": "", "RepetitionTime": "20", "Rows": 256, "SOPClassUID": "1.2.840.10008.5.1.4.1.1.4", "SOPInstanceUID": "2.16.840.1.113662.4.4168496325.1025307679.1207625440068523197", "SamplesPerPixel": 3, "ScanOptions": "", "ScanningSequence": "GR", "SequenceVariant": [ "SS", "SP" ], "SeriesDate": "20020628", "SeriesDescription": "L Spine", "SeriesInstanceUID": "2.16.840.1.113662.4.4168496325.1025307678.3494705239865384161", "SeriesNumber": "1", "SeriesTime": "164118.0", "SliceLocation": "64.544899", "SliceThickness": "1.300000e+00", "SoftwareVersions": "VIA2.0E.003", "SpecificCharacterSet": "ISO_IR 100", "StationName": "ba187_ws", "StudyDate": "20230101", "StudyDescription": "SPINE", "StudyID": "14024", "StudyInstanceUID": "2.16.840.1.113662.4.4168496325.1025305873.7118351817185979330", "StudyTime": "160956.0", "TemporalPositionIdentifier": "1", "TemporalResolution": "458660", "_meta": { "FileMetaInformationVersion": { "Value": [ { "0": 0, "1": 1 } ], "vr": "OB" }, "ImplementationClassUID": { "Value": [ "1.2.840.113819.7.1.1997.1.0" ], "vr": "UI" }, "ImplementationVersionName": { "Value": [ "SENSORSYSTEMS1.0" ], "vr": "SH" }, "MediaStorageSOPClassUID": { "Value": [ "1.2.840.10008.5.1.4.1.1.4" ], "vr": "UI" }, "MediaStorageSOPInstanceUID": { "Value": [ "2.16.840.1.113662.4.4168496325.1025307679.1207625440068523197" ], "vr": "UI" }, "TransferSyntaxUID": { "Value": [ "1.2.840.10008.1.2" ], "vr": "UI" } }, "_vrMap": { "PixelData": "OB" } };

@pieper
Copy link
Collaborator

pieper commented Mar 21, 2023

Excellent - thanks for sharing it. If you have a chance, it would be great if you could convert this into a test so that others can easily discover and learn from it, and so we can confirm that this keeps working in future versions.

@pieper pieper closed this as completed Mar 21, 2023
@coderider007
Copy link

coderider007 commented Mar 21, 2023

Sure Steve @pieper, I'll do it.

Looks like there is an issue when setting dataset.PixelData using Tag name. The var type is not set correctly.

As a workaround I used

dicomDict.dict['7FE00010'] = {
vr: 'OB',
Value: [rawImageData.data.buffer]
}

to set PixelData

@pieper
Copy link
Collaborator

pieper commented Mar 21, 2023

Sure Steve @pieper, I'll do it.

That's great 👍

Looks like there is an issue when setting dataset.PixelData using Tag name. The var type is not set correctly.

If you think that's an error in the way PixelData is handled generally maybe it's fixable. I don't think generating datasets from scratch like this has gotten a lot of testing compared to read or read/modify/write.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants