Inserting a Waveform Group Into a Data Set Example for C++ 6.0 and later
/*****************************************************************/
/* This is a comprehensive sample, which shows how to insert a */
/* waveform group with one ECG channel into a Data Set. */
/* The main function is InsertECGWaveform; the rest of the */
/* functions are helping functions. */
/*****************************************************************/
#pragma warning(disable: 4786)
// Functions Prototypes
BOOL InsertECGChannel(ILEADDicomDSPtr& spDataSet,
IDicomWaveformGroupPtr& spWaveformGroup,
SAFEARRAY* psaSamples);
BOOL SetChannelSourceAndSensitivity(ILEADDicomDSPtr& spDataSet,
IDicomWaveformChannelPtr& spWaveformChannel);
BOOL SetChannelAnnotations(IDicomWaveformChannelPtr& spWaveformChannel);
// The main function that creates the waveform group and adds to the Data Set
// Note: The function assumes that psaSamples is a safearray of short (VT_I2) values.
BOOL InsertECGWaveform(ILEADDicomDSPtr& spDataSet, SAFEARRAY* psaSamples)
{
if (!psaSamples)
{
return FALSE;
}
// Our new waveform group
IDicomWaveformGroupPtr spECGWaveformGroup(__uuidof(DicomWaveformGroup));
spECGWaveformGroup->EnableMethodErrors = VARIANT_FALSE;
// Reset the waveform group, we don't really need to call this!
spECGWaveformGroup->Reset ();
// Set the Waveform Sample Interpretation
spECGWaveformGroup->SampleInterpretation = WAVEFORM_SAMPLE_INTERPRETATION_SS;
ULONG uNumberOfSamples = psaSamples->rgsabound->cElements;
// Set the Number of Waveform Samples. You can obtain this number by accessing
// the NumberOfSamplesPerChannel property.
if (spECGWaveformGroup->SetNumberOfSamplesPerChannel (uNumberOfSamples) != DICOM_SUCCESS)
{
return FALSE;
}
// Set the Sampling Frequency
spECGWaveformGroup->SamplingFrequency = 240.0;
// No Multiplex Group Time Offset. When the value is defined, it can be set using the
// MultiplexGroupTimeOffset property.
spECGWaveformGroup->ValueDefined [MULTIPLEX_GROUP_TIME_OFFSET] = VARIANT_FALSE;
// No Trigger Time Offset. When the value is defined, it can be set using the
// TriggerTimeOffset property.
spECGWaveformGroup->ValueDefined [TRIGGER_TIME_OFFSET] = VARIANT_FALSE;
// No Trigger Sample Position. When the value is defined, it can be set using the
// TriggerSamplePosition property.
spECGWaveformGroup->ValueDefined [TRIGGER_SAMPLE_POSITION] = VARIANT_FALSE;
// Waveform Originality is ORIGINAL
spECGWaveformGroup->WaveformOriginality = WAVEFORM_ORIGINALITY_ORIGINAL;
// Set the Multiplex Group Label
spECGWaveformGroup->ValueDefined [MULTIPLEX_GROUP_LABEL] = VARIANT_TRUE;
spECGWaveformGroup->MultiplexGroupLabel = "SCPECG Waveform";
// Set the Waveform Padding Value
spECGWaveformGroup->ValueDefined [WAVEFORM_PADDING_VALUE] = VARIANT_TRUE;
spECGWaveformGroup->WaveformPaddingValue = 32768;
if (!InsertECGChannel(spDataSet, spECGWaveformGroup, psaSamples))
{
return FALSE;
}
// Delete any waveform groups that already exist in the Data Set
while (spDataSet->GetWaveformGroupCount() > 0)
{
spDataSet->DeleteWaveformGroup (0, 0);
}
// Insert the new waveform group into the Data Set
if (spDataSet->AddWaveformGroup (spECGWaveformGroup, 0, ELEMENT_INDEX_MAX) != DICOM_SUCCESS)
{
return FALSE;
}
return TRUE;
}
// Adds an ECG channel to the group
BOOL InsertECGChannel(ILEADDicomDSPtr& spDataSet,
IDicomWaveformGroupPtr& spWaveformGroup,
SAFEARRAY* psaSamples)
{
IDicomWaveformChannelPtr spECGWaveformChannel;
// Add a channel to the group
spECGWaveformChannel = spWaveformGroup->Channels->Add();
VARIANT varSamples;
varSamples.vt = VT_ARRAY | VT_I2;
varSamples.parray = psaSamples;
// Set the samples of the channel
if (spECGWaveformChannel->SetChannelSamples (varSamples) < 0)
{
return FALSE;
}
// Set the Channel Source and Sensitivity
if (!SetChannelSourceAndSensitivity(spDataSet, spECGWaveformChannel))
{
return FALSE;
}
// Set the Channel Status
spECGWaveformChannel->ValueDefined [CHANNEL_STATUS] = VARIANT_TRUE;
spECGWaveformChannel->ChannelStatus = CHANNEL_STATUS_OK;
// Set the Channel Time Skew
spECGWaveformChannel->ValueDefined [CHANNEL_TIME_SKEW] = VARIANT_TRUE;
spECGWaveformChannel->ChannelTimeSkew = 0.0;
// Set the Waveform Channel Number
spECGWaveformChannel->ValueDefined [WAVEFORM_CHANNEL_NUMBER] = VARIANT_TRUE;
spECGWaveformChannel->WaveformChannelNumber = 0;
// Set the Channel Label
spECGWaveformChannel->ValueDefined [CHANNEL_LABEL] = VARIANT_TRUE;
spECGWaveformChannel->ChannelLabel = "First Channel";
// No Channel Offset. When the value is defined, it can be set using the ChannelOffset
// property
spECGWaveformChannel->ValueDefined [CHANNEL_OFFSET] = VARIANT_FALSE;
// Set the Filter Low Frequency
spECGWaveformChannel->ValueDefined [FILTER_LOW_FREQUENCY] = VARIANT_TRUE;
spECGWaveformChannel->FilterLowFrequency = 0.05;
// Set Filter High Frequency
spECGWaveformChannel->ValueDefined [FILTER_HIGH_FREQUENCY] = VARIANT_TRUE;
spECGWaveformChannel->FilterHighFrequency = 100.0;
// Set the Channel Minimum Value
spECGWaveformChannel->ValueDefined [CHANNEL_MINIMUM_VALUE] = VARIANT_TRUE;
spECGWaveformChannel->ChannelMinimumValue = -386;
// Set the Channel Maximum Value
spECGWaveformChannel->ValueDefined [CHANNEL_MAXIMUM_VALUE] = VARIANT_TRUE;
spECGWaveformChannel->ChannelMaximumValue = 1264;
// When the Notch Filter Frequency and Bandwidth are defined, they can be set using the
// NotchFilterFrequency and NotchFilterBandwidth properties.
// Last, but not least, set the channel annotations!
return SetChannelAnnotations(spECGWaveformChannel);
}
#define CID_3001 "CID 3001"
#define CID_3082 "CID 3082"
// Sets the Channel Source and Sensitivity
BOOL SetChannelSourceAndSensitivity(ILEADDicomDSPtr& spDataSet,
IDicomWaveformChannelPtr& spWaveformChannel)
{
// We will use the Context Group Table
IDicomCodedConceptPtr spCodedConcept;
spCodedConcept = spDataSet->CurrentCodedConcept;
// -------------------- Channel Source --------------------
// Load the ECG Leads Context Group
spDataSet->LoadContextGroup(CID_3001);
if (!spDataSet->FindContextGroup(CID_3001))
{
return FALSE;
}
// 5.6.3-9-1 is Lead I (Einthoven)
if (!spDataSet->FindCodedConcept("SCPECG", "5.6.3-9-1"))
{
return FALSE;
}
IDicomCodeSequenceItemPtr spChannelSource;
spChannelSource = spWaveformChannel->ChannelSource;
// Set the Channel Source
spChannelSource->CodingSchemeDesignator = spCodedConcept->CodingSchemeDesignator;
spChannelSource->CodingSchemeVersion = spCodedConcept->CodingSchemeVersion;
spChannelSource->CodeValue = spCodedConcept->CodeValue;
spChannelSource->CodeMeaning = spCodedConcept->CodeMeaning;
// -------------------- Channel Sensitivity --------------------
spWaveformChannel->ValueDefined [CHANNEL_SENSITIVITY] = VARIANT_TRUE;
// The Channel Sensitivity
spWaveformChannel->ChannelSensitivity= 0.00122;
// Load the Cardiology Units of Measurement Context Group
spDataSet->LoadContextGroup(CID_3082);
if (!spDataSet->FindContextGroup(CID_3082))
{
return FALSE;
}
if (!spDataSet->FindCodedConcept("UCUM", "mV"))
{
return FALSE;
}
IDicomCodeSequenceItemPtr spChannelSensitivityUnits;
spChannelSensitivityUnits = spWaveformChannel->ChannelSensitivityUnits;
// The Channel Sensitivity Units
spChannelSensitivityUnits->CodingSchemeDesignator = spCodedConcept->CodingSchemeDesignator;
spChannelSensitivityUnits->CodingSchemeVersion = spCodedConcept->CodingSchemeVersion;
spChannelSensitivityUnits->CodeValue = spCodedConcept->CodeValue;
spChannelSensitivityUnits->CodeMeaning = spCodedConcept->CodeMeaning;
// The Channel Sensitivity Correction Factor
spWaveformChannel->ChannelSensitivityCF = 1.0;
// The Channel Baseline
spWaveformChannel->ChannelBaseline = 0.0;
return TRUE;
}
// Adds annotations for the channel
BOOL SetChannelAnnotations(IDicomWaveformChannelPtr& spWaveformChannel)
{
// Delete any existing channel annotations
while (spWaveformChannel->Annotations->Count> 0)
{
spWaveformChannel->Annotations->Remove (0);
}
IDicomWaveformAnnotationPtr spECGWaveformAnnotation;
// Add an annotation object
spECGWaveformAnnotation = spWaveformChannel->Annotations->Add();
// Our annotation is defined by a Coded Name/Numeric Measurement pair
spECGWaveformAnnotation->ValueType = TYPE_CODED_NAME_AND_NUMERIC_VALUE;
IDicomCodeSequenceItemPtr spCodeSequenceItem;
// The Coded Name. Note: Instead of setting these values directly, you can also use
// the Context Group Table; the Context Groups defined by the DICOM Content Mapping
// Resource (DCMR) can be loaded into this table. Take a look at the function
// SetChannelSourceAndSensitivity above for an example.
spCodeSequenceItem = spECGWaveformAnnotation->CodedName;
spCodeSequenceItem->CodingSchemeDesignator = "LN";
spCodeSequenceItem->CodingSchemeVersion = "19971101";
spCodeSequenceItem->CodeValue = "8867-4";
spCodeSequenceItem->CodeMeaning = "Heart rate";
// The Numeric Value
spECGWaveformAnnotation->NumericValueCount = 1;
spECGWaveformAnnotation->NumericValue[0] = 69.0;
// The Measurement Units. Refer to the note given when the Coded Name was set.
spECGWaveformAnnotation->ValueDefined[MEASUREMENT_UNITS] = VARIANT_TRUE;
spCodeSequenceItem = spECGWaveformAnnotation->MeasurementUnits;
spCodeSequenceItem->CodingSchemeDesignator = "UCUM";
spCodeSequenceItem->CodingSchemeVersion = "1.4";
spCodeSequenceItem->CodeValue = "{H.B.}/min";
spCodeSequenceItem->CodeMeaning = "Heart beat per minute";
return TRUE;
}