Bug 144454 - Loading and storing DICOM again looses important information
Retain dicom tags using parasites. Patch by Paul Epperson.
This commit is contained in:
@ -28,6 +28,7 @@ Global data follows no strict rules.
|
||||
"tiff" : The standard GIMP TIFF plugin
|
||||
"jpeg" : The standard GIMP JPEG plugin
|
||||
"png" : The standard GIMP PNG plugin
|
||||
"dcm" : The standard GIMP DICOM plugin
|
||||
"gimp" : For common and standard parasites
|
||||
|
||||
------------------------------------------------------------------
|
||||
@ -215,6 +216,13 @@ Global data follows no strict rules.
|
||||
from GtkPageSetup. The content is identical to what is stored in
|
||||
~/.gimp-2.x/print-page-setup.
|
||||
|
||||
"dcm/XXXX-XXXX-AA" (IMAGE, PERSISTANT)
|
||||
These parasites are stored by the Dicom plug-in and hold the DICOM
|
||||
element information for that image. The format is raw binary data
|
||||
as read from the original image.
|
||||
where: XXXX is a 4-digit ascii encoded hexadecimal number
|
||||
AA is a two character ascii value representing the Dicom
|
||||
element's Value Represenation (VR)
|
||||
|
||||
------------------------------------------------------------------
|
||||
*** KNOWN LAYER/DRAWABLE PARASITES:
|
||||
|
@ -84,16 +84,9 @@ static void add_tag_pointer (GByteArray *group_stream,
|
||||
const gchar *value_rep,
|
||||
const guint8 *data,
|
||||
gint length);
|
||||
static void add_tag_string (GByteArray *group_stream,
|
||||
gint group,
|
||||
gint element,
|
||||
const gchar *value_rep,
|
||||
const gchar *s);
|
||||
static void add_tag_int (GByteArray *group_stream,
|
||||
gint group,
|
||||
gint element,
|
||||
const gchar *value_rep,
|
||||
gint value);
|
||||
static GSList * dicom_add_tags (FILE *DICOM,
|
||||
GByteArray *group_stream,
|
||||
GSList *elements);
|
||||
static gboolean write_group_to_file (FILE *DICOM,
|
||||
gint group,
|
||||
GByteArray *group_stream);
|
||||
@ -295,6 +288,23 @@ run (const gchar *name,
|
||||
values[0].data.d_status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* add_parasites_to_image:
|
||||
* @data: pointer to a GimpParasite to be attached to the image specified by @user_data.
|
||||
* @user_data: pointer to the image_ID to which parasite @data should be added.
|
||||
*
|
||||
* Attaches parasite to image and also frees that parasite
|
||||
**/
|
||||
static void
|
||||
add_parasites_to_image (gpointer data,
|
||||
gpointer user_data)
|
||||
{
|
||||
GimpParasite *parasite = (GimpParasite *)data;
|
||||
gint32 *image_ID = (gint32 *)user_data;
|
||||
gimp_image_parasite_attach (*image_ID, parasite);
|
||||
gimp_parasite_free (parasite);
|
||||
}
|
||||
|
||||
static gint32
|
||||
load_image (const gchar *filename,
|
||||
GError **error)
|
||||
@ -303,6 +313,7 @@ load_image (const gchar *filename,
|
||||
gint32 volatile image_ID = -1;
|
||||
gint32 layer_ID;
|
||||
GimpDrawable *drawable;
|
||||
GSList *elements = NULL;
|
||||
FILE *DICOM;
|
||||
gchar buf[500]; /* buffer for random things like scanning */
|
||||
DicomInfo *dicominfo;
|
||||
@ -314,6 +325,7 @@ load_image (const gchar *filename,
|
||||
gint high_bit = 0;
|
||||
guint8 *pix_buf = NULL;
|
||||
gboolean is_signed = FALSE;
|
||||
guint8 in_sequence = 0;
|
||||
|
||||
/* open the file */
|
||||
DICOM = g_fopen (filename, "rb");
|
||||
@ -433,7 +445,16 @@ load_image (const gchar *filename,
|
||||
|
||||
/* Sequence of items - just ignore the delimiters... */
|
||||
if (element_length == 0xffffffff)
|
||||
continue;
|
||||
{
|
||||
in_sequence = 1;
|
||||
continue;
|
||||
}
|
||||
/* End of Sequence tag */
|
||||
if (tag == 0xFFFEE0DD)
|
||||
{
|
||||
in_sequence = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Sequence of items item tag... Ignore as well */
|
||||
if (tag == 0xFFFEE000)
|
||||
@ -454,6 +475,12 @@ load_image (const gchar *filename,
|
||||
value = g_new0 (guint8, element_length + 4);
|
||||
fread (value, 1, element_length, DICOM);
|
||||
|
||||
/* ignore everything inside of a sequence */
|
||||
if (in_sequence)
|
||||
{
|
||||
g_free (value);
|
||||
continue;
|
||||
}
|
||||
/* Some special casts that are used below */
|
||||
ctx_ul = *(guint32 *) value;
|
||||
ctx_us = *(guint16 *) value;
|
||||
@ -511,6 +538,25 @@ load_image (const gchar *filename,
|
||||
}
|
||||
else
|
||||
{
|
||||
/* save this element to a parasite for later writing */
|
||||
GimpParasite *parasite;
|
||||
gchar pname[255];
|
||||
/* all elements are retrievable using gimp_parasite_list() */
|
||||
g_snprintf(pname,sizeof(pname),"dcm/%04x-%04x-%s",group_word,element_word,value_rep);
|
||||
if ((parasite = gimp_parasite_new (pname,
|
||||
GIMP_PARASITE_PERSISTENT,
|
||||
element_length, value)))
|
||||
{
|
||||
/*
|
||||
* at this point, the image has not yet been created, so image_ID is not valid.
|
||||
* keep the parasite around until we're able to attach it.
|
||||
*/
|
||||
/* gimp_image_parasite_attach (image_ID, parasite);*/
|
||||
/* gimp_parasite_free (parasite); */
|
||||
/* add to our list of parasites to be added (prepending for speed. we'll reverse it later) */
|
||||
elements = g_slist_prepend (elements,parasite);
|
||||
}
|
||||
|
||||
g_free (value);
|
||||
}
|
||||
}
|
||||
@ -571,6 +617,14 @@ load_image (const gchar *filename,
|
||||
|
||||
dicom_loader (pix_buf, dicominfo, &pixel_rgn);
|
||||
|
||||
if (elements)
|
||||
{
|
||||
/* flip the parasites back around into the order they were created (read from the file) */
|
||||
elements = g_slist_reverse (elements);
|
||||
/* and add each one to the image */
|
||||
g_slist_foreach (elements,add_parasites_to_image,(gpointer)&image_ID);
|
||||
g_slist_free (elements);
|
||||
}
|
||||
/* free the structures */
|
||||
g_free (pix_buf);
|
||||
g_free (dicominfo);
|
||||
@ -578,8 +632,9 @@ load_image (const gchar *filename,
|
||||
/* close the file */
|
||||
fclose (DICOM);
|
||||
|
||||
/* Tell GIMP to display the image. */
|
||||
gimp_drawable_flush (drawable);
|
||||
/* Tell GIMP to display the image and detach. */
|
||||
/* gimp_drawable_flush (drawable); -- implicitely done via gimp_drawable_detach() */
|
||||
gimp_drawable_detach (drawable);
|
||||
|
||||
return image_ID;
|
||||
}
|
||||
@ -737,6 +792,446 @@ toggle_endian2 (guint16 *buf16,
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct _DicomElement {
|
||||
guint16 group_word;
|
||||
guint16 element_word;
|
||||
gchar value_rep[3];
|
||||
guint32 element_length;
|
||||
guint8 *value;
|
||||
gint free;
|
||||
} DICOMELEMENT;
|
||||
|
||||
/**
|
||||
* dicom_add_element:
|
||||
* @elements: head of a GSList containing DICOMELEMENT structures.
|
||||
* @group_word: Dicom Element group number for the tag to be added to @elements.
|
||||
* @element_word: Dicom Element element number for the tag to be added to @elements.
|
||||
* @value_rep: a string representing the Dicom VR for the new element.
|
||||
* @value: a pointer to an integer containing the value for the element to be created.
|
||||
*
|
||||
* Creates a DICOMELEMENT object and inserts it into @elements.
|
||||
*
|
||||
* Return value: the new head of @elements
|
||||
**/
|
||||
static GSList *
|
||||
dicom_add_element (GSList *elements,
|
||||
guint16 group_word,
|
||||
guint16 element_word,
|
||||
gchar *value_rep,
|
||||
guint32 element_length,
|
||||
guint8 *value,
|
||||
gint copy)
|
||||
{
|
||||
DICOMELEMENT *element = g_new0 (DICOMELEMENT, 1);
|
||||
if (element)
|
||||
{
|
||||
element->free = 0;
|
||||
if (copy)
|
||||
{
|
||||
guint8 *v = g_new (guint8,element_length);
|
||||
if (v)
|
||||
{
|
||||
memcpy (v,value,element_length);
|
||||
value = v;
|
||||
element->free = 1;
|
||||
}
|
||||
}
|
||||
element->group_word = group_word;
|
||||
element->element_word = element_word;
|
||||
strncpy (element->value_rep,value_rep,sizeof (element->value_rep));
|
||||
element->element_length = element_length;
|
||||
element->value = value;
|
||||
elements = g_slist_prepend (elements,element);
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* dicom_add_element_int:
|
||||
* @elements: head of a GSList containing DICOMELEMENT structures.
|
||||
* @group_word: Dicom Element group number for the tag to be added to @elements.
|
||||
* @element_word: Dicom Element element number for the tag to be added to @elements.
|
||||
* @value_rep: a string representing the Dicom VR for the new element.
|
||||
* @value: a pointer to an integer containing the value for the element to be created.
|
||||
*
|
||||
* Creates a DICOMELEMENT object from the passed integer pointer and adds it to @elements.
|
||||
* Note: value should be the address of a guint16 for @value_rep==%US or guint32 for other values of @value_rep
|
||||
*
|
||||
* Return value: the new head of @elements
|
||||
*/
|
||||
static GSList *
|
||||
dicom_add_element_int (GSList *elements,
|
||||
guint16 group_word,
|
||||
guint16 element_word,
|
||||
gchar *value_rep,
|
||||
guint8 *value)
|
||||
{
|
||||
guint32 len;
|
||||
|
||||
if (strcmp (value_rep, "US") == 0)
|
||||
len = 2;
|
||||
else
|
||||
len = 4;
|
||||
|
||||
return dicom_add_element (elements,group_word,element_word,value_rep,len,value,0);
|
||||
}
|
||||
|
||||
/**
|
||||
* dicom_element_done:
|
||||
* @data: pointer to a DICOMELEMENT structure which is to be destroyed.
|
||||
* @user_data: unused.
|
||||
*
|
||||
* Destroys the DICOMELEMENT passed as @data
|
||||
**/
|
||||
static void
|
||||
dicom_element_done (gpointer data,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
DICOMELEMENT *e = (DICOMELEMENT *)data;
|
||||
if (e->free)
|
||||
{
|
||||
g_free (e->value);
|
||||
}
|
||||
g_free (data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dicom_elements_destroy:
|
||||
* @elements: head of a GSList containing DICOMELEMENT structures.
|
||||
*
|
||||
* Destroys the list of DICOMELEMENTs
|
||||
**/
|
||||
static void
|
||||
dicom_elements_destroy (GSList *elements)
|
||||
{
|
||||
if (elements)
|
||||
{
|
||||
g_slist_foreach (elements,dicom_element_done,NULL);
|
||||
g_slist_free (elements);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dicom_destroy_element:
|
||||
* @elements: head of a GSList containing DICOMELEMENT structures.
|
||||
* @ele: a DICOMELEMENT structure to be removed from @elements
|
||||
*
|
||||
* Removes the specified DICOMELEMENT from @elements and Destroys it
|
||||
*
|
||||
* Return value: the new head of @elements
|
||||
**/
|
||||
static GSList *
|
||||
dicom_destroy_element (GSList *elements,
|
||||
DICOMELEMENT *ele)
|
||||
{
|
||||
if (ele)
|
||||
{
|
||||
elements = g_slist_remove_all (elements,ele);
|
||||
if (ele->free)
|
||||
{
|
||||
g_free (ele->value);
|
||||
}
|
||||
g_free (ele);
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* dicom_elements_compare:
|
||||
* @a: pointer to a DICOMELEMENT structure.
|
||||
* @b: pointer to a DICOMELEMENT structure.
|
||||
*
|
||||
* Determines the equality of @a and @b as strcmp
|
||||
*
|
||||
* Return value: an integer indicating the equality of @a and @b.
|
||||
**/
|
||||
static gint
|
||||
dicom_elements_compare (gconstpointer a,
|
||||
gconstpointer b)
|
||||
{
|
||||
DICOMELEMENT *e1 = (DICOMELEMENT *)a;
|
||||
DICOMELEMENT *e2 = (DICOMELEMENT *)b;
|
||||
|
||||
if (e1->group_word == e2->group_word)
|
||||
{
|
||||
if (e1->element_word == e2->element_word)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (e1->element_word > e2->element_word)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (e1->group_word < e2->group_word)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* dicom_element_find_by_num:
|
||||
* @head: head of a GSList containing DICOMELEMENT structures.
|
||||
* @group_word: Dicom Element group number for the tag to be found.
|
||||
* @element_word: Dicom Element element number for the tag to be found.
|
||||
*
|
||||
* Retrieves the specified DICOMELEMENT from @head, if available.
|
||||
*
|
||||
* Return value: a DICOMELEMENT matching the specified group,element, or NULL if the specified element was not found.
|
||||
**/
|
||||
static DICOMELEMENT *
|
||||
dicom_element_find_by_num (GSList *head,
|
||||
guint16 group_word,
|
||||
guint16 element_word)
|
||||
{
|
||||
DICOMELEMENT data = {group_word,element_word,"",0,NULL};
|
||||
GSList *ele = g_slist_find_custom (head,&data,dicom_elements_compare);
|
||||
return ele?ele->data:NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* dicom_get_elements_list:
|
||||
* @image_ID: the image_ID from which to read parasites in order to retrieve the dicom elements
|
||||
*
|
||||
* Reads all DICOMELEMENTs from the specified image's parasites.
|
||||
*
|
||||
* Return value: a GSList of all known dicom elements
|
||||
**/
|
||||
static GSList *
|
||||
dicom_get_elements_list (gint32 image_ID)
|
||||
{
|
||||
GSList *elements = NULL;
|
||||
GimpParasite *parasite;
|
||||
gchar **parasites = NULL;
|
||||
gint count = 0;
|
||||
|
||||
gimp_image_parasite_list (image_ID,&count,¶sites);
|
||||
if (parasites && count > 0)
|
||||
{
|
||||
gint i;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (strncmp (parasites[i],"dcm",3) == 0)
|
||||
{
|
||||
parasite = gimp_image_parasite_find (image_ID,parasites[i]);
|
||||
if (parasite)
|
||||
{
|
||||
gchar buf[1024];
|
||||
gchar *ptr1;
|
||||
gchar *ptr2;
|
||||
gchar value_rep[3]="";
|
||||
guint16 group_word=0;
|
||||
guint16 element_word=0;
|
||||
|
||||
/* sacrificial buffer */
|
||||
strncpy (buf,parasites[i],sizeof (buf));
|
||||
|
||||
/* buf should now hold a string of the form dcm/XXXX-XXXX-AA
|
||||
* where XXXX are Hex values for group and element respectively
|
||||
* AA is the Value Representation of the element
|
||||
*
|
||||
* start off by jumping over the dcm/ to the first Hex blob
|
||||
*/
|
||||
ptr1 = strchr (buf,'/');
|
||||
if (ptr1)
|
||||
{
|
||||
gchar t[15];
|
||||
ptr1++;
|
||||
ptr2 = strchr (ptr1,'-');
|
||||
if (ptr2)
|
||||
{
|
||||
*ptr2 = '\0';
|
||||
}
|
||||
g_snprintf (t,sizeof (t),"0x%s",ptr1);
|
||||
group_word = (guint16)g_ascii_strtoull (t,NULL,16);
|
||||
ptr1 = ptr2+1;
|
||||
}
|
||||
/* now get the second Hex blob */
|
||||
if (ptr1)
|
||||
{
|
||||
gchar t[15];
|
||||
ptr2 = strchr (ptr1,'-');
|
||||
if (ptr2)
|
||||
{
|
||||
*ptr2 = '\0';
|
||||
}
|
||||
g_snprintf (t,sizeof (t),"0x%s",ptr1);
|
||||
element_word = (guint16)g_ascii_strtoull (t,NULL,16);
|
||||
ptr1 = ptr2+1;
|
||||
}
|
||||
/* and lastly, the VR */
|
||||
if (ptr1)
|
||||
{
|
||||
strncpy (value_rep,ptr1,sizeof (value_rep));
|
||||
}
|
||||
/*
|
||||
* If all went according to plan, we should be able to add this element
|
||||
*/
|
||||
if (group_word > 0 && element_word > 0)
|
||||
{
|
||||
/* create a copy of the parasite's data so we can drop the const qualifier */
|
||||
guint32 len = gimp_parasite_data_size (parasite);
|
||||
if (len > 0)
|
||||
{
|
||||
guint8 *val = g_new0 (guint8,len);
|
||||
if (val)
|
||||
{
|
||||
memcpy (val,gimp_parasite_data (parasite),len);
|
||||
/* and add the dicom element, asking to have it's value copied for later garbage collection */
|
||||
elements = dicom_add_element (elements,group_word,element_word,value_rep,len,val,1);
|
||||
g_free (val);
|
||||
}
|
||||
}
|
||||
}
|
||||
gimp_parasite_free (parasite);
|
||||
}
|
||||
}
|
||||
/* make sure we free each individual parasite name, in addition to the array of names */
|
||||
g_free (parasites[i]);
|
||||
}
|
||||
}
|
||||
/* cleanup the array of names */
|
||||
if (parasites)
|
||||
{
|
||||
g_free (parasites);
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* dicom_remove_gimp_specified_elements:
|
||||
* @elements: GSList to remove elements from
|
||||
* @samples_per_pixel: samples per pixel of the image to be written.
|
||||
* if set to %3 the planar configuration for color images
|
||||
* will also be removed from @elements
|
||||
*
|
||||
* Removes certain DICOMELEMENTs from the elements list which are specific to the output of this plugin.
|
||||
*
|
||||
* Return value: the new head of @elements
|
||||
**/
|
||||
static GSList *
|
||||
dicom_remove_gimp_specified_elements (GSList *elements,
|
||||
gint samples_per_pixel)
|
||||
{
|
||||
DICOMELEMENT remove[] = {
|
||||
/* Image presentation group */
|
||||
/* Samples per pixel */
|
||||
{0x0028, 0x0002, "", 0, NULL},
|
||||
/* Photometric interpretation */
|
||||
{0x0028, 0x0004, "", 0, NULL},
|
||||
/* rows */
|
||||
{0x0028, 0x0010, "", 0, NULL},
|
||||
/* columns */
|
||||
{0x0028, 0x0011, "", 0, NULL},
|
||||
/* Bits allocated */
|
||||
{0x0028, 0x0100, "", 0, NULL},
|
||||
/* Bits Stored */
|
||||
{0x0028, 0x0101, "", 0, NULL},
|
||||
/* High bit */
|
||||
{0x0028, 0x0102, "", 0, NULL},
|
||||
/* Pixel representation */
|
||||
{0x0028, 0x0103, "", 0, NULL},
|
||||
|
||||
{0,0,"",0,NULL}
|
||||
};
|
||||
DICOMELEMENT *ele;
|
||||
gint i;
|
||||
|
||||
/*
|
||||
* Remove all Dicom elements which will be set as part of the writing of the new file
|
||||
*/
|
||||
for (i=0; remove[i].group_word > 0;i++)
|
||||
{
|
||||
if ((ele = dicom_element_find_by_num (elements,remove[i].group_word,remove[i].element_word)))
|
||||
{
|
||||
elements = dicom_destroy_element (elements,ele);
|
||||
}
|
||||
}
|
||||
/* special case - allow this to be overwritten if necessary */
|
||||
if (samples_per_pixel == 3)
|
||||
{
|
||||
/* Planar configuration for color images */
|
||||
if ((ele = dicom_element_find_by_num (elements,0x0028,0x0006)))
|
||||
{
|
||||
elements = dicom_destroy_element (elements,ele);
|
||||
}
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* dicom_ensure_required_elements_present:
|
||||
* @elements: GSList to remove elements from
|
||||
* @today_string: string containing today's date in DICOM format. This is used to default any required Dicom elements of date type to today's date.
|
||||
*
|
||||
* Defaults DICOMELEMENTs to the values set by previous version of this plugin, but only if they do not already exist.
|
||||
*
|
||||
* Return value: the new head of @elements
|
||||
**/
|
||||
static GSList *
|
||||
dicom_ensure_required_elements_present (GSList *elements,
|
||||
gchar *today_string)
|
||||
{
|
||||
DICOMELEMENT defaults[] = {
|
||||
/* Meta element group */
|
||||
/* 0002,0001 - File Meta Information Version */
|
||||
{0x0002,0x0001,"OB",2,(guint8 *)"\0\1"},
|
||||
/* 0002,0010 - Transfer syntax uid */
|
||||
{0x0002,0x0001,"UI",strlen ("1.2.840.10008.1.2.1"),(guint8 *)"1.2.840.10008.1.2.1"},
|
||||
/* 0002,0013 - Implementation version name */
|
||||
{0x0002,0x0013,"SH",strlen ("Gimp Dicom Plugin 1.0"),(guint8 *)"Gimp Dicom Plugin 1.0"},
|
||||
/* Identifying group */
|
||||
/* Study date */
|
||||
{0x0008,0x0020,"DA",strlen (today_string),(guint8 *)today_string},
|
||||
/* Series date */
|
||||
{0x0008,0x0021,"DA",strlen (today_string),(guint8 *)today_string},
|
||||
/* Acquisition date */
|
||||
{0x0008,0x0022,"DA",strlen (today_string),(guint8 *)today_string},
|
||||
/* Content Date */
|
||||
{0x0008,0x0023,"DA",strlen (today_string),(guint8 *)today_string},
|
||||
/* Modality - I have to add something.. */
|
||||
{0x0008,0x0060,"CS",strlen ("MR"),(guint8 *)"MR"},
|
||||
/* Patient group */
|
||||
/* Patient name */
|
||||
{0x0010, 0x0010, "PN", strlen ("DOE^WILBER"), (guint8 *)"DOE^WILBER"},
|
||||
/* Patient ID */
|
||||
{0x0010, 0x0020, "CS", strlen ("314159265"), (guint8 *)"314159265"},
|
||||
/* Patient Birth date */
|
||||
{0x0010, 0x0030, "DA", strlen (today_string), (guint8 *)today_string},
|
||||
/* Patient sex */
|
||||
{0x0010, 0x0040, "CS", strlen (""), (guint8 *)"" /* unknown */},
|
||||
/* Relationship group */
|
||||
/* Instance number */
|
||||
{0x0020, 0x0013, "IS", strlen ("1"), (guint8 *)"1"},
|
||||
|
||||
{0,0,"",0,NULL}
|
||||
};
|
||||
gint i;
|
||||
|
||||
/*
|
||||
* Make sure that all of the default elements have a value
|
||||
*/
|
||||
for (i=0; defaults[i].group_word > 0;i++)
|
||||
{
|
||||
if (dicom_element_find_by_num (elements,defaults[i].group_word,defaults[i].element_word) == NULL)
|
||||
{
|
||||
elements = dicom_add_element (elements,defaults[i].group_word,defaults[i].element_word,
|
||||
defaults[i].value_rep,defaults[i].element_length,defaults[i].value,0);
|
||||
}
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
/* save_image() saves an image in the dicom format. The DICOM format
|
||||
* requires a lot of tags to be set. Some of them have real uses, others
|
||||
* must just be filled with dummy values.
|
||||
@ -752,12 +1247,17 @@ save_image (const gchar *filename,
|
||||
GimpDrawable *drawable;
|
||||
GimpPixelRgn pixel_rgn;
|
||||
GByteArray *group_stream;
|
||||
GSList *elements = NULL;
|
||||
gint group;
|
||||
GDate *date;
|
||||
gchar today_string[16];
|
||||
gchar *photometric_interp;
|
||||
gint samples_per_pixel;
|
||||
gboolean retval = TRUE;
|
||||
guint16 zero = 0;
|
||||
guint16 seven = 7;
|
||||
guint16 eight = 8;
|
||||
guchar *src = NULL;
|
||||
|
||||
drawable_type = gimp_drawable_type (drawable_ID);
|
||||
drawable = gimp_drawable_get (drawable_ID);
|
||||
@ -814,101 +1314,129 @@ save_image (const gchar *filename,
|
||||
|
||||
group_stream = g_byte_array_new ();
|
||||
|
||||
/* Meta element group */
|
||||
group = 0x0002;
|
||||
/* 0002,0001 - File Meta Information Version */
|
||||
add_tag_pointer (group_stream, group, 0x0001, "OB",
|
||||
(const guint8 *) "\0\1", 2);
|
||||
/* 0002,0010 - Transfer syntax uid */
|
||||
add_tag_string (group_stream, group, 0x0010, "UI", "1.2.840.10008.1.2.1");
|
||||
/* 0002,0013 - Implementation version name */
|
||||
add_tag_string (group_stream, group, 0x0013, "SH", "Gimp Dicom Plugin 1.0");
|
||||
write_group_to_file (DICOM, group, group_stream);
|
||||
elements = dicom_get_elements_list (image_ID);
|
||||
if (0/*replaceElementsList*/)
|
||||
{
|
||||
/* to do */
|
||||
}
|
||||
else if (1/*insist_on_basic_elements*/)
|
||||
{
|
||||
elements = dicom_ensure_required_elements_present (elements,today_string);
|
||||
}
|
||||
|
||||
/* Identifying group */
|
||||
group = 0x0008;
|
||||
/* Study date */
|
||||
add_tag_string (group_stream, group, 0x0020, "DA", today_string);
|
||||
/* Series date */
|
||||
add_tag_string (group_stream, group, 0x0021, "DA", today_string);
|
||||
/* Acquisition date */
|
||||
add_tag_string (group_stream, group, 0x0022, "DA", today_string);
|
||||
/* Content Date */
|
||||
add_tag_string (group_stream, group, 0x0023, "DA", today_string);
|
||||
/* Modality - I have to add something.. */
|
||||
add_tag_string (group_stream, group, 0x0060, "CS", "MR");
|
||||
write_group_to_file (DICOM, group, group_stream);
|
||||
|
||||
/* Patient group */
|
||||
group = 0x0010;
|
||||
/* Patient name */
|
||||
add_tag_string (group_stream, group, 0x0010, "PN", "Wilber Doe");
|
||||
/* Patient ID */
|
||||
add_tag_string (group_stream, group, 0x0020, "CS", "314159265");
|
||||
/* Patient Birth date */
|
||||
add_tag_string (group_stream, group, 0x0030, "DA", today_string);
|
||||
/* Patient sex */
|
||||
add_tag_string (group_stream, group, 0x0040, "CS", "" /* unknown */);
|
||||
write_group_to_file (DICOM, group, group_stream);
|
||||
|
||||
/* Relationship group */
|
||||
group = 0x0020;
|
||||
/* Instance number */
|
||||
add_tag_string (group_stream, group, 0x0013, "IS", "1");
|
||||
write_group_to_file (DICOM, group, group_stream);
|
||||
/*
|
||||
* Set value of custom elements
|
||||
*/
|
||||
elements = dicom_remove_gimp_specified_elements (elements,samples_per_pixel);
|
||||
|
||||
/* Image presentation group */
|
||||
group = 0x0028;
|
||||
/* Samples per pixel */
|
||||
add_tag_int (group_stream, group, 0x0002, "US", samples_per_pixel);
|
||||
elements = dicom_add_element_int (elements,group,0x0002,"US",(guint8 *)&samples_per_pixel);
|
||||
/* Photometric interpretation */
|
||||
add_tag_string (group_stream, group, 0x0004, "CS", photometric_interp);
|
||||
elements = dicom_add_element (elements,group,0x0004,"CS",strlen (photometric_interp),(guint8 *)photometric_interp,0);
|
||||
/* Planar configuration for color images */
|
||||
if (samples_per_pixel == 3)
|
||||
add_tag_int (group_stream, group, 0x0006, "US", 0);
|
||||
elements = dicom_add_element_int (elements,group,0x0006,"US",(guint8 *)&zero);
|
||||
/* rows */
|
||||
add_tag_int (group_stream, group, 0x0010, "US", drawable->height);
|
||||
elements = dicom_add_element_int (elements, group, 0x0010, "US", (guint8 *)&(drawable->height));
|
||||
/* columns */
|
||||
add_tag_int (group_stream, group, 0x0011, "US", drawable->width);
|
||||
elements = dicom_add_element_int (elements, group, 0x0011, "US", (guint8 *)&(drawable->width));
|
||||
/* Bits allocated */
|
||||
add_tag_int (group_stream, group, 0x0100, "US", 8);
|
||||
elements = dicom_add_element_int (elements, group, 0x0100, "US", (guint8 *)&eight);
|
||||
/* Bits Stored */
|
||||
add_tag_int (group_stream, group, 0x0101, "US", 8);
|
||||
elements = dicom_add_element_int (elements, group, 0x0101, "US", (guint8 *)&eight);
|
||||
/* High bit */
|
||||
add_tag_int (group_stream, group, 0x0102, "US", 7);
|
||||
elements = dicom_add_element_int (elements, group, 0x0102, "US", (guint8 *)&seven);
|
||||
/* Pixel representation */
|
||||
add_tag_int (group_stream, group, 0x0103, "US", 0);
|
||||
write_group_to_file (DICOM, group, group_stream);
|
||||
elements = dicom_add_element_int (elements, group, 0x0103, "US", (guint8 *)&zero);
|
||||
|
||||
/* Pixel data */
|
||||
group = 0x7fe0;
|
||||
{
|
||||
guchar *src;
|
||||
src = g_new (guchar,
|
||||
drawable->height * drawable->width * samples_per_pixel);
|
||||
if (src)
|
||||
{
|
||||
gimp_pixel_rgn_init (&pixel_rgn, drawable,
|
||||
0, 0, drawable->width, drawable->height,
|
||||
FALSE, FALSE);
|
||||
gimp_pixel_rgn_get_rect (&pixel_rgn,
|
||||
src, 0, 0, drawable->width, drawable->height);
|
||||
elements = dicom_add_element (elements,group,0x0010,"OW",
|
||||
drawable->width * drawable->height * samples_per_pixel,
|
||||
(guint8 *)src,0);
|
||||
|
||||
src = g_new (guchar,
|
||||
drawable->height * drawable->width * samples_per_pixel);
|
||||
elements = dicom_add_tags (DICOM,group_stream,elements);
|
||||
|
||||
gimp_pixel_rgn_init (&pixel_rgn, drawable,
|
||||
0, 0, drawable->width, drawable->height,
|
||||
FALSE, FALSE);
|
||||
gimp_pixel_rgn_get_rect (&pixel_rgn,
|
||||
src, 0, 0, drawable->width, drawable->height);
|
||||
add_tag_pointer (group_stream, group, 0x0010, "OW", (guint8 *) src,
|
||||
drawable->width * drawable->height * samples_per_pixel);
|
||||
|
||||
g_free (src);
|
||||
}
|
||||
|
||||
retval = write_group_to_file (DICOM, group, group_stream);
|
||||
g_free (src);
|
||||
}
|
||||
else
|
||||
{
|
||||
retval = FALSE;
|
||||
}
|
||||
|
||||
fclose (DICOM);
|
||||
|
||||
dicom_elements_destroy (elements);
|
||||
g_byte_array_free (group_stream, TRUE);
|
||||
gimp_drawable_detach (drawable);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* dicom_print_tags:
|
||||
* @data: pointer to a DICOMELEMENT structure which is to be written to file
|
||||
* @user_data: structure containing state information and output parameters
|
||||
*
|
||||
* Writes the specified DICOMELEMENT to @user_data's group_stream member.
|
||||
* Between groups, flushes the group_stream to @user_data's DICOM member.
|
||||
*/
|
||||
static void
|
||||
dicom_print_tags(gpointer data,
|
||||
gpointer user_data)
|
||||
{
|
||||
struct {
|
||||
FILE *DICOM;
|
||||
GByteArray *group_stream;
|
||||
gint last_group;
|
||||
} *d = user_data;
|
||||
DICOMELEMENT *e = (DICOMELEMENT *)data;
|
||||
if (d->last_group >= 0 && e->group_word != d->last_group)
|
||||
{
|
||||
write_group_to_file (d->DICOM, d->last_group, d->group_stream);
|
||||
}
|
||||
|
||||
add_tag_pointer (d->group_stream,e->group_word,e->element_word,e->value_rep,e->value,e->element_length);
|
||||
d->last_group = e->group_word;
|
||||
}
|
||||
|
||||
/**
|
||||
* dicom_add_tags:
|
||||
* @DICOM: File pointer to which @elements should be written.
|
||||
* @group_stream: byte array used for staging Dicom Element groups before flushing them to disk.
|
||||
* @elements: GSList container the Dicom Element elements from
|
||||
*
|
||||
* Writes all Dicom tags in @elements to the file @DICOM
|
||||
*
|
||||
* Return value: the new head of @elements
|
||||
**/
|
||||
static GSList *
|
||||
dicom_add_tags(FILE *DICOM,
|
||||
GByteArray *group_stream,
|
||||
GSList *elements)
|
||||
{
|
||||
struct {
|
||||
FILE *DICOM;
|
||||
GByteArray *group_stream;
|
||||
gint last_group;
|
||||
} data = {DICOM,group_stream,-1};
|
||||
elements = g_slist_sort (elements,dicom_elements_compare);
|
||||
g_slist_foreach (elements,dicom_print_tags,&data);
|
||||
/* make sure that the final group is written to the file */
|
||||
write_group_to_file (data.DICOM, data.last_group, data.group_stream);
|
||||
return elements;
|
||||
}
|
||||
/* add_tag_pointer () adds to the group_stream one single value with its
|
||||
* corresponding value_rep. Note that we use "explicit VR".
|
||||
*/
|
||||
@ -986,37 +1514,6 @@ add_tag_pointer (GByteArray *group_stream,
|
||||
}
|
||||
}
|
||||
|
||||
/* Convenience function for adding a string to the dicom stream */
|
||||
static void
|
||||
add_tag_string (GByteArray *group_stream,
|
||||
gint group,
|
||||
gint element,
|
||||
const gchar *value_rep,
|
||||
const gchar *s)
|
||||
{
|
||||
add_tag_pointer (group_stream,
|
||||
group, element, value_rep, (const guint8 *) s, strlen (s));
|
||||
}
|
||||
|
||||
/* Convenience function for adding an integer to the dicom stream */
|
||||
static void
|
||||
add_tag_int (GByteArray *group_stream,
|
||||
gint group,
|
||||
gint element,
|
||||
const gchar *value_rep,
|
||||
gint value)
|
||||
{
|
||||
gint len;
|
||||
|
||||
if (strcmp (value_rep, "US") == 0)
|
||||
len = 2;
|
||||
else
|
||||
len = 4;
|
||||
|
||||
add_tag_pointer (group_stream,
|
||||
group, element, value_rep, (const guint8 *) &value, len);
|
||||
}
|
||||
|
||||
/* Once a group has been built it has to be wrapped with a meta-group
|
||||
* tag before it is written to the DICOM file. This is done by
|
||||
* write_group_to_file.
|
||||
|
Reference in New Issue
Block a user