dicomdcm4che

Dcm4che delete study from local archive (dicomdir)


Rephrasing my original post after some research in dcm4che3 and dicom protocol.

I am using dcm4che3 toolkit to build an application that is essentially going to be a simple Image Archive able to forward studies on demand to other modalities.The tool also implements a s-store scu service to query itself but also other modalities.

A requirement is to able and "delete" periodically studies from this local archive.

I am also new to the dicom protocol and dcm4che, so i am trying to understand the logic of the underlying dicomdir (used by dcm4che-tool-dcmqrscp) as also any available services or methods for deleting studies.

So my questions are the following:

Are there any other ways (or part of protocol) for deleting in consistent way a study (permanently from dicomdir records but also from filesystem if possibly) from the dicomdir?

The code i use to delete the study (using similar approach with -d option in dicomdir) is:

public void deleteDicomDir(String studyDir) throws IOException {

        DcmDir1 main = new DcmDir1();

        long start = System.currentTimeMillis();
        LOG.info("$$$ Trying to delete dicomdir: " + studyDir);
        main.open(new File("C:\\dicomrouter_dev\\dcmrouter_root\\dicomdir"));
        main.removeReferenceTo(new File(studyDir));
        main.close();
        long end = System.currentTimeMillis();
        System.out.println();
        System.out.println(MessageFormat.format(
            rb.getString("deleted"),
            num, main.getFile(), (end - start)));
}

And what actually does the "removeReferenceTo()" method is calling at the end DicomDirWritter method:

public synchronized boolean deleteRecord(Attributes rec)
            throws IOException {
        if (rec.getInt(Tag.RecordInUseFlag, 0) == INACTIVE)
            return false; // already disabled

        for (Attributes lowerRec = readLowerDirectoryRecord(rec);
                lowerRec != null; 
                lowerRec = readNextDirectoryRecord(lowerRec))
            deleteRecord(lowerRec);

        rec.setInt(Tag.RecordInUseFlag, VR.US, INACTIVE);
        markAsDirty(rec);
        return true;
}

Thanks for your time and i am looking forward for any info that will declare this

Actually after some research on dicom protocol as also dcm4che toolkit i am able to delete a study and synchonize my DICOMDIR and remove studies(or files) with Record In-use Flag = 0 in 3 steps:

//delete records referring DICOM files specified by <directory:studyDir> arguments from existing directory file <dicomdir> by setting its Record In-use Flag = 0
public void deleteDicomDir(String studyDir) {        
        String dicomdir = "C:\\dicomrouter_dev\\dcmrouter_root\\DICOMDIR";
        DcmDir1 main = new DcmDir1();
        long start = System.currentTimeMillis();
        try {
            LOG.info("$$$ Trying to delete dicomdir: " + studyDir);
            main.open(new File(dicomdir));
            int num = 0;               
            main.removeReferenceTo(new File(studyDir));
            main.close();
            long end = System.currentTimeMillis();
            System.out.println();
            System.out.println(MessageFormat.format(
                rb.getString("deleted"),
                num, main.getFile(), (end - start)));
        } catch (IOException ex) {
            java.util.logging.Logger.getLogger(DcmDir1.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
                main.close();
        }        
}


//purge records without file references from directory file <dicomdir> by setting its Record In-use Flag = 0
public void purgeDicomDir() {
        String dicomdir = "C:\\dicomrouter_dev\\dcmrouter_root\\DICOMDIR";
        DcmDir1 main = new DcmDir1();
        long start = System.currentTimeMillis();
        try {
            main.open(new File(dicomdir));
            int num = main.purge();
            main.close();
            long end = System.currentTimeMillis();
            System.out.println(MessageFormat.format(
                rb.getString("purged"),
                num, main.getFile(), (end - start)));
        } catch (IOException ex) {
            java.util.logging.Logger.getLogger(DcmDir1.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
                main.close();
        }  
}

//compact existing directory file <dicomdir> by removing records with Record In-use Flag != 0
public void compactDicomDir() {        
        String fpath = "C:\\dicomrouter_dev\\dcmrouter_root\\DICOMDIR";
        File f = new File(fpath);
        File bak = new File(fpath + "~");        
        DcmDir1 main = new DcmDir1();
        long start = System.currentTimeMillis();        
        try {
            LOG.info("$$$ Trying to compact dicomdir: " + fpath);
            main.compact(f, bak);
            long end = System.currentTimeMillis();
            System.out.println(MessageFormat.format(
                rb.getString("compacted"),
                f, bak.length(), f.length(), (end - start)));
        } catch (IOException ex) {
            java.util.logging.Logger.getLogger(DcmDir1.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
                main.close();
        }
}

Then i can also safely delete the files from the disk.


Solution

  • I have already explained this to you in comments (now deleted). I have also pointed you to this question which discusses same thing. As I said in comments, this is more about protocol than toolkit.

    First thing, when you say "Delete a study", I assume you actually want to delete the DICOM files from the hard disk with their references in other (DICOMDIR or database etc) locations.

    There is no way to do this in DICOM.

    N-DELETE command in DICOM is not for this purpose. As explained in this answer, you may attempt Imaging Object Change Management IHE Integration Profile. I never used that; so sorry, I cannot comment on that further.

    In the context of dicom protocol is it valid the term: "Delete a study"?

    As I said above; no.

    As i can see the "dcm4che-tool-dcmdir" when deleting a study (using the tool), what happens actually is to "delete records referring DICOM files specified by file.. or directory.. arguments from existing directory file by setting its Record In-use Flag = 0" so the files remain on the filesystem.

    This seems natural. The command should just modify the DICOMDIR to remove the file. It should not delete the physical file from hard disk.

    Even more if i try and delete a study in this way (-d option in tool-dicomdir) when i query the archive with my c-find scu, i can find the study. So even if the record in dicomdir is marked as Inactive if i perform a c-find query to the archive i can still fetch it.

    I do not know what is the source of your C-FIND-RESPONSE. If it is DICOMDIR itself, most probably the modifications you did to it in earlier step are not saved. Save the modifications and that should work. But remember, this still will not delete the file from physical storage. You are just removing reference of it.

    I am not expert in the toolkit you are working with. But, with following code in your question:

    main.open(new File("C:\\dicomrouter_dev\\dcmrouter_root\\dicomdir"));
    main.removeReferenceTo(new File(studyDir));
    //Check if you need to save the changes here before closing
    main.close();
    

    it seems you are opening DICOMDIR -- removing the directory reference from it -- closing DICOMDIR. I do not know the implementation of removeReferenceTo method; may be it is from github. If it does not save the changes as well, you have to save the changes explicitly. May be you need to use DicomOutputStream class to do it. I found following sample code here:

    FileOutputStream fos = new FileOutputStream(fileOutput +".dcm");
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    DicomOutputStream dos = new DicomOutputStream(bos);
    dos.writeDicomFile(dio);
    dos.close();
    

    If i try and delete study files manually from the filesystem i guess that the dicomdir becomes corrupted.

    Yes; that's right. To avoid this, also modify DICOMDIR accordingly. Both, physical storage and DICOMDIR should be in consistent state.

    Proposal - 1:

    1. Decide the event/action (C-STORE-SUCCESS sent to calling application may be) on which the deletion should trigger. You always know in your application that the event/action is triggered.
    2. Delete the file(s) manually or through some code outside DICOM.
    3. Update the underlying references (DICOMDIR, Database etc) to reflect this changes (the -d command as you said in question).
    4. Save the changes. Just in case you are missing this step.
    5. Verify if changes are reflected in references. Run the query on database to check the records. Load DICOMDIR in some DICOM Loader/Dump application and see if relevant entries are gone.
    6. Attempt to query again to see how it goes.

    Proposal - 2:

    Using DICOM Commands, implement the custom behavior. Modify the behavior of DICOM command (N-DELETE may be) to do your additional actions. This is only possible if you have access to source code and willing to change it. I personally do not recommend this because: