--[[
    This script identifies the image properties of magnetic resonance (MR) images.
    Specific properties extracted are:
        echo type, 
        based on image type tag, series description tag or echo time, repetition time 
        and phase angle values
        image description, based on series description or on the concatenation of 
        echo type, "ORIGINAL", view, laterality and contrast

     Cristian Bonciu,    July-September 2004
	-	CREATED and implemented the functionality from pseudo-code prototypes provided by Allan 
	Allan Noordvyk,     January 2005
     -	Added Pulse Sequence property support
     Allan Noordvyk			14 MAR 2005
     -	Added Diffusion Coefficient property and code to set it.
     -	Added check for "T-1" and "T-2" as aliases for T1 and T2.
     Allan Noordvyk      		04 MAY 2005
     -	Added Diffusion Coefficient handling for GE.
     Allan Noordvyk			17 JUL 2005
     -	CRDB00033847: Added support for /T/, /C/, /S/, view abbreviations used by some 
		Philips/Marconi MRs
     -	Added Image Comments to tags consulted when looking for echo type.
     Allan Noordvyk			14 SEP 2005
     -	Removed ExtractPhaseMR function.   If we find a specific device that we can trust relative 
		acquistion number to be phase, we should recreate as new routine based on ExtractPhaseCT 
		in ExtractCT.  Until then no phase splitting for MR.
     Allan Noordvyk			07 OCT 2005
     -	The changes below are related to one or more of the following STRQs:
          - STRQ1572 Sequencing: Improper sequencing for MIP images
          -STRQ1563 Sequencing concerns for SIEMENS MR MAGNETOM VISION VB33G 
          - STRQ1560 Sequencing for Siemens MR MAGNETOM EXPERT VB33G 
          - STRQ1561 Sequencing concerns for Siemens MR MAGNETOM IMPACT VB33G 
          - STRQ1562 Sequencing concerns for SIEMENS MR MAGNETOM OPEN viva VB33G
          and other issues observed in situ at Gwinnett Medical
     -	Added interpretation of "/DIF" and "DIFFUSION" substrings in series description as 
		indications of diffusion even when specificc diffusion values not indicated elsewehere in header.
     -	Added "FLA" as abbreviation of FLAIR
     -	Echo Type now fails to empty value rather than triplet of TE, TR, FA values.
     -	Added Time Echo, Time Repetition, and Flip Angle properties which are not treated as common for DP,
		but could be used manually if needed.
     -	Allowed substring matching (rather than whole words) for T1, T2, and 4+ character lenght pulse 
		sequence names.
     -	Added last ditch guessing at echo type based on TR range if more sophisticated classification has failed.
     Vladan Petrovic			21 OCT 2005
     -	Added HIDDEN_PULSE_SEQ_INFO_TAG; it has been observed that some MR vendors hide 
		Pulse Sequence information in the this tag (0019/109e element).
     Allan Noordvyk			25 OCT 2005
     -	Added "DW" as another keyword indicating a diffusion weighted series (observed at Elkhart
		with GE MR device).
     Mahesh Nagarajan		30 OCT 2005
	- 	Added detection of "DIFF" as keyword for Diffusion weighted series for  images from GE 
		(observed at Prince William Hospital, VA)
     Mahesh Nagarajan		10 NOV 2005
	-	Switched the order of searching tags for pulse sequence info; now look in ACQ_SCAN_SEQ_TAG 
		first and ACQ_PROTOCOL_TAG next. The reason for the switch is that ACQ_PROTOCOL_TAG
		is given its value manually and therefore less reliable.
     Allan Noordvyk			17 NOV 2005
     -	Added support for detecting GR in ACQ Scanning Sequence <0018,0020> as alias for 
          GRE pulse sequence.
     -	Fixed substring matching for long pulse sequence names.
     Allan Noordvyk			21 NOV 2005
     -	Added VIBE (aka FAME) pulse sequence
     Mahesh Nagarajan		17 JAN 2005
     -	Added new property Inversion Time (TI) from DICOM tag (0018,0082)
     -	Added "FMPIR" as alias for "STIR" on request from Shields MRI (Boston, MA)
     Mahesh Nagarajan		18 JAN 2006
     -	During the acquisition of a MIP series, the MIPS and the original axial data are often a part 
		of the same series; and both get classified as image type "MIP". If (0008,0008) Image Type
		has the value "ORIGINAL" and the McKesson image type says "MIP" and the view is "Axial";
		these images are re-classified as "ORIGINAL".
     Mahesh Nagarajan		03 FEB 2006        
     -	The above change backfired for radial MIPS acquired about the horizontal axis and image type 
		"ORIGINAL"; the fix is now modified to reset the value of the image typeproperty only if the
          series description doesnt have "MIPSeries" in it.
     Mahesh Nagarajan		16 FEB 2006
     -	The routine for extracting diffusion coefficient has a statement at the end reporting that
          no diffusion coefficient was found for the sequence; however the variable used here is NIL if
		the sequence in question does not have the tag (0008, 0070) Device Manufacturer. Lua being 
		what it is, this causes the entire script to fail. This is now corrected.
     Mahesh Nagarajan		24 FEB 2006
     -	On request by PACS Admin at Mountain View, diffusion sequences for that site are not pushed
		behind other original image sequences on the thumbnail bar. 
     Mahesh Nagarajan		02 MAR 2006
     -	Modified the check for re-setting the value of Axial "MIP" images to Original; this is not 
		done if MIP is found in (0008, 0008) ID Image type
     Mahesh Nagarajan		03 MAR 2006
     -	CRDB00038038: Another form of phase identification is added through a new property called 
		"Phase Name". The  property currently takes four values - (1) Before (2) Arterial (3) Capillary 
		(4) Venous; though as other string valued properties this will be subject to a lot of changes 
		and syntax variations.
     Mahesh Nagarajan		22 MAR 2006
     -	It is useful for radiologists to be able to use the axis of the MIP as a matching criteria to
		ensure that the MIP sequences go into specific view-ports.  For this purpose , two properties
		are created for all MIP MR images - "X-Axis Orientation" & "Y-Axis Orientation"; the former
		with the first three elements of (0020, 0037) REL Image Orientation and the latter with the
		last three. If these attributes are common through the sequence, the sequence gets assigned 
		the appropriate axis as a property value. 
     Mahesh Nagarajan		12 MAY 2006
     -	Pulled the plug on ExtractImageTypeMR(); this function is made obselete by the fact primary 
		sorting is now on image number     
     Mahesh Nagarajan		17 MAY 2006
     -	Bringing back ExtractPhaseMR(); this tag will populate phase with whatever value exists in 
		(0020, 0100) REL Temporal Position Identifier. This will occur only if the value in (0020, 0105)
		REL Number of Temporal Positions is less than 6. The reason behind this is that some sites acquire
		upto 400 temporal positions and which clutters up the thumbnail bar.
     Mahesh Nagarajan		06 JUN 2006
     -	Removing diffusion coefficient ion for MR studies performed at Spectrum.	
     Cartik Sharma			28 DEC 2006
     -	Added function call ExtractSuppressionType() which splits the series based on whether images are
		fat suppressed or  water suppressed based on image type.
	Mahesh Nagarajan		01 FEB 2007
	-	Site customized code will be removed and re-located to ExtractMR.site files -> starting with 11.5,
		unless extraction	routines are fundamentally flawed, all change requests will be incorporated into a site file.
	Cartik Sharma			28 FEB 2007
	-	Corrected the echo type extraction routine to ignore positive matches from partial string searches
		for common echo type values
	Mahesh Nagarajan		30 MAR 2007
	-	Corrected the extraction routines for diffusion coefficients from Siemens acquired images; b500 and b1000
		were mistakenly identified as b5 & b1 respectively...
	-	Added enhanced diff. coefficient extraction routines for GE acquired images using private tag information
		obtained from the vendor
	Mahesh Nagarajan		16 MAY 2007
	-	Modified the process of return phase property values; using the function "NewPropWithValue" automatically converts
		the returned values to string types; this causes problems while sorting on phase property values. The "NewPropWithValue" 
		function call is replaced by a more stringent process of setting this property with the value in the proper value representation
	Mahesh Nagarajan		22 MAY 2007
	-	After talks with Hitachi, added extraction of phase info from images acquired from Hitachi Echelon devices; this info is
		found in the DICOM tag (0020, 0012) REL Acquisition Number
	Mahesh Nagarajan		30 MAY 2007
	-	 A discussion with Philips reveals that the restricting splits to 6 or less temporal positions may not capture all cases that 
		require splitting; a radiologist may perform 300 temporal positions i.e. the number stored in (0020, 0105); but he may actually 
		send only 3 of these to PACS. For now, we have introduced an exception for diffusion sequences from Philips devices so that
		these are slit based on the temporal position identifier.
	Mahesh Nagarajan		27 SEP 2007
	-	Added code to ExtractThumbnailGroupMR() to ensure that only diffusion images with type "Original" are assigned
		different category numbers; this ensures that diffusion screen saves do not get special treatment
	Mahesh Nagarajan		29 NOV 2007
	-	Modified ExtractDescriptionMR() function to ignore "View" values while building the series description of MIP
		series; this ensures uniform descriptions for such series
	- 	Changed the order in which the extract functions are executed; this ensures that the "pulse sequence" property is
		extracted before it is used in the description.
	-	Added nil-check for a tag being used in BestMatchInAttr() function call in ExtractPhaseMR()		
	Mahesh Nagarajan		07 FEB 2008
	-	Adding back site-specific code snippets; after a long discussion with Cristian B., it is understood that this code has to stay
		in the code until proper site files have been installed. Such code is added back but within "dollar sign" demarcations to
		ensure easy removal at a later date.	
     Mahesh Nagarajan		12 FEB 2008		  
	-	Site hack for Christus St Francis Cabrini to ensure that sequences titled "New Series" are not split but remain at 
		the end of the thumbnail bar
    Mahesh Nagarajan			27 MAR 2008		  
	- 	Continuing with the lucrative business of site hacks; Sacre-Coeur Hospital in Montreal requests disabling the 
		diffusion coefficient extraction
    Mahesh Nagarajan			10 APR 2008		  
	-	More site hacks for Christus St Francis Cabrini to disable diffusion coefficient extraction ; this ensures that the
		various diffusion sequences are presented in a single series
    Mahesh Nagarajan		11 APR 2008		
	- Changing the property type of PHASE_PROP from int to string; this is specifically done for Dr. Mendel of Caritas 
	  to help him use this property in his hanging protocols
    Shudong Liu			December 17, 2008
	- Cutomized for site St. Lukes Episcopal Hospital to not split images by (0020, 0100) REL Temporal
	  Position Identifier. (REI10756)
    Shudong Liu			December 22, 2008
	- Cutomized for site St. Lukes Outpatient Imaging to not split images by (0020, 0100) REL Temporal
	  Position Identifier. (REI11142)		  
    James Zhang - 17 September 2009
      - Customized for site Spectrum Health to place special series to the end of thumbnail bar,
        as per REI 15576(CRDB00079739).
--]]

-- Enable tracing in this module for debugging (comment out for production version)
--AliDSTraceLevel = 1

-- ////////////////////////////////////////////////////////////////////////////
-- string definitions 
-- ////////////////////////////////////////////////////////////////////////////

-- Echo type values
        T1_VAL	= "T1"
        T2_VAL	= "T2"
        PD_VAL	= "PD"
        T_DASH_1_VAL = "T-1"
        T_DASH_2_VAL = "T-2"
 
-- Pulse sequence values

      THREEDUF_VAL = "3DUF"
      CINE_PS_VAL = "Cine"
      DWEPI_VAL = "DWEPI"
      EPI_VAL = "EPI"
      FLAIR_VAL = "FLAIR"
      FIR_VAL = "FIR"
      FSE_VAL = "FSE"
      GMR_VAL = "GMR"
      GRE_VAL = "GRE"
      GSE_VAL = "GSE"
      IPAT_VAL = "IPAT"
      IR_VAL = "IR"
      MSE_VAL = "MSE"
      MT_VAL = "MT"
      MTC_VAL = "MTC"
      MYO_PERF_VAL = "MP-C"
      SE_VAL = "SE"
      SEBB_C_VAL = "SEBB-C"
      SEBB_NF_VAL = "SEBB-NF"
      SPGR_VAL = "SPGR"
      SPIR_VAL = "SPIR"
      SSFP_VAL = "SSFP"
      SSFSE_VAL = "SSFSE"
      SSFSE_IR_VAL = "SSFSE-IR"
      STIR_VAL = "STIR"
      TOF_GRE_VAL = "TOF-GRE"
      TOF_SPGR_VAL = "TOF-SPGR"
      UFGRE_VAL = "UFGRE"
      VENC_VAL = "VENC"
      VIA_C_VAL = "VIA-C"
      VIBE_VAL = "VIBE"
      
-- Diffusion coefficient values

      B0_VAL = "b0"
      B1_VAL = "b1"
      B2_VAL = "b2"
      B3_VAL = "b3"
      B4_VAL = "b4"
      B5_VAL = "b5"
      B6_VAL = "b6"
      BN_VAL = "bn"
	  B500_VAL = "b500"
	  B1000_VAL="b1000"
      
      -- Common abbreviations for diffusion in series descriptions
      DIFF_VAL = "DIFF"            
      SLASH_DIF_VAL = "/DIF"       
      DIFFUSION_VAL = "DIFFUSION"
      DWI_VAL = "DWI"  
      DW_VAL = "DW"
      ZERO_VAL = "ZERO"
      
	--Image type values
	IMAGE_FAT_VAL = "IMAGE SUBTRACT"
	IMAGE_WATER_VAL = "IMAGE ADDITION"
	
function RegisterProperties()
-- ////////////////////////////////////////////////////////////////////////////
--- This function is called automatically at load of script to register any
--- modality specific properties. 
--- All property IDs for MR are in the (3711,1301)- (3711,13FF) range
-- ////////////////////////////////////////////////////////////////////////////
-- /////////////////////////////////////////////////////////////////////////////

    local valList  -- used to build concatenated standard value lists

    -- PULSE_SEQ_PROP indicates the special pulse sequence (if any) employed to acquire the MR image.
    --
    -- Standard Values:	        FSE, GRE, FLAIR, etc.
    -- Usage in DP:	        Common
    -- Related DICOM Tags:	Series Description <0008, 103e> 

    valList = THREEDUF_VAL .. "," .. CINE_PS_VAL .. ",".. DWEPI_VAL .. "," .. EPI_VAL .. "," .. FLAIR_VAL .. "," .. 
              FIR_VAL .. "," .. FSE_VAL .. "," .. GMR_VAL .. "," .. GRE_VAL .. "," .. GSE_VAL  .. "," .. 
              IPAT_VAL .. "," .. IR_VAL .. "," .. MSE_VAL .. "," .. MT_VAL .. "," .. MTC_VAL .. "," .. 
              MYO_PERF_VAL .. "," .. SE_VAL .. "," .. SEBB_C_VAL .. "," .. SEBB_NF_VAL .. "," .. 
              SPGR_VAL .. "," .. SPIR_VAL .. "," .. SSFP_VAL .. "," .. SSFSE_VAL .. "," .. SSFSE_IR_VAL .. "," .. 
              STIR_VAL .. "," .. TOF_GRE_VAL .. "," .. TOF_SPGR_VAL .. "," .. 
              UFGRE_VAL .. "," .. VENC_VAL .. "," .. VIA_C_VAL .. "," .. VIBE_VAL
               
    Register("PULSE_SEQ_PROP", "1301", "string", valList, "Pulse Sequence",{1})
 
    -- DIFF_COEF_PROP indicates the diffusion coefficient (if any) employed to acquire the MR image.
    --
    -- Standard Values:	        b0, b1, b2, b3, b4, b5
    -- Usage in DP:	        Common
    -- Related DICOM Tags:	ACQ Sequence Name <0018,0024> 

    valList = B0_VAL .. "," .. B500_VAL .. "," .. B1000_VAL .. "," .. B1_VAL .. "," .. B2_VAL .. "," .. B3_VAL .. "," 
				.. B4_VAL .. "," .. B5_VAL .. "," .. BN_VAL
               
    Register("DIFF_COEF_PROP", "1302", "string", valList, "Diffusion Coef.",{1})
  
    -- Time Echo indicates the TE (if any) employed to acquire the MR image.
    --
    -- Standard Values:	        Positive Numeric
    -- Usage in DP:	        Rare
    -- Related DICOM Tags:	TE 

    Register("ECHO_TIME_PROP", "1303", "string", "", "TE" )
    
    -- Time Repetition indicates the TR (if any) employed to acquire the MR image.
    --
    -- Standard Values:	        Positive Numeric
    -- Usage in DP:	        Rare
    -- Related DICOM Tags:	TR 

    Register("REPETITION_TIME_PROP", "1304", "string", "", "TR" )
    
    -- Flip Angle indicates the FA (if any) employed to acquire the MR image.
    --
    -- Standard Values:	        Numeric 0-360
    -- Usage in DP:	        Rare
    -- Related DICOM Tags:	FA 

    Register("FLIP_ANGLE_PROP", "1305", "string", "", "FA" )
    
    -- Inversion Time indicates the TI (if any) employed to acquire the MR image.
    --
    -- Standard Values:	        
    -- Usage in DP:	        Rare
    -- Related DICOM Tags:	TI
    
    Register("INV_TIME_PROP", "1306", "string", "", "TI" )

    -- X-Axis Orientation contains the first three values of (0020, 0037) REL Image Orientation
    --
    -- Standard Values:	        
    -- Usage in DP:	        Rare i.e. NO CHANCE IN HELL!
    -- Related DICOM Tags:	(0020, 0037) REL Image Orientation
    
--[[
	MaxN: This portion of code was written to use functionality of IP that is not yet 
	implemented. There is no timeline on when this will be available and the functionality 
	itself is not considered critical. This is commented out to  prevent compiling and
	execution of unnecessary code.

	Register("XAXIS_ORIENT_PROP", "1307", "double", "", "X-Axis Orientation" , {0})
    
    -- Y-Axis Orientation contains the last three values of (0020, 0037) REL Image Orientation
    --
    -- Standard Values:	        
    -- Usage in DP:	        Rare i.e. NO CHANCE IN HELL!
    -- Related DICOM Tags:	(0020, 0037) REL Image Orientation
    
    Register("YAXIS_ORIENT_PROP", "1308", "double", "", "Y-Axis Orientation" , {0})
    
	-- MIP Axis contains the value of the axis about which the MIP images are acquired
    --
    -- Standard Values: Vertical, Horizontal  
    -- Usage in DP:	        Possible
    -- Related DICOM Tags:	(3711, 1307) X-Axis Orientation & (3711, 1308) Y-Axis Orientation
    
    Register("MIP_AXIS_PROP", "1309", "string", "", "MIP Axis" , {1})
--]]
    
    -- Image type value contains the value corresponding to fat suppression or water supression 
    --
    --Standard Values: FAT, WATER
    -- Related DICOM Tags: 	(0008,0008) Image Modality type 
    -- For example: Original
    --				Primary 
    --					Other
    --						Image Subtraction
    Register("IMAGE_SUPPR_PROP","1310", "string", "Fat, Water","Supression type")
        
end

-- ////////////////////////////////////////////////////////////////////////////    
-- definitions of the DICOM elements IDs used in this file 
-- ////////////////////////////////////////////////////////////////////////////    
function DeclareTags()  
	REPETITION_TIME_TAG = maketag("0018","0080")
	ECHO_TIME_TAG = maketag("0018","0081")
    INV_TIME_TAG = maketag("0018","0082")
	FLIP_ANGLE_TAG = maketag("0018","1314")
	RCV_COIL_TAG = maketag( "0018", "1215" )
	ACQ_SCAN_SEQ_TAG = maketag( "0018", "0020" )
	ACQ_SEQ_NAME_TAG = maketag( "0018", "0024" )
	HIDDEN_PULSE_SEQ_INFO_TAG = maketag("0019", "109e")
	GE_PRIV_IMAGE_OPTIONS_TAG = maketag( "0043", "1039" )
	GE_PRIV_DIFF_TYPE_TAG = maketag( "0021", "105a" )
	GE_PRIV_VAS_COLL_TAG = maketag( "0043", "1030" )
end

-- ////////////////////////////////////////////////////////////////////////////
-- MR property extraction functions 
-- ////////////////////////////////////////////////////////////////////////////

-- ////////////////////////////////////////////////////////////////////////////
function ExtractViewMR()
--
-- This function extends the ExtractView function of ExtractCommon.tls
-- to cover some additional possibilities relevant to specific MR devices.
-- If a valid view was already found by ExtractView, then this function
-- does nothing.  it only consults the additiona DiCOM header elements
-- if the ExtractView function came up empty.
--
-- ////////////////////////////////////////////////////////////////////////////

  -- If the common function was already successful finding a good view value...
    local viewProp = GetAttr( VIEW_PROP )
    if (viewProp) then

        TraceC( "Common script found view... skipping special MR logic.\n" )
        return viewProp;
        -- ==============================================

    end
    
    TraceC( "No good view found by common script... using special MR logic.\n" ) 
    
    -- Define some special search strings known to be used for some Philips, Marconi,
    -- and other MR devices
    local SLASH_AX = "/T/"   -- T for Transverse AKA Axial
    local SLASH_COR = "/C/"  -- C for Coronal
    local SLASH_SAG = "/S/"  -- S for Sagittal

    local matchStr = nil		-- Matched string value.
    
    -- List of additional tags we will examine for view information...
    local searchTags = { SERIES_DESC_TAG  }
    
    -- For each tag that could be checked...
    for index,tag in ipairs(searchTags) do 
    
        TraceC( "Checking tag " .. index .. ": " .. tag .. "\n" )
    
    	-- if attribute exists for given tag in this image...
        local testAttr = GetAttr(tag)
        if (testAttr) then
            -- Look for substring matches (including abbreviations)
            -- Note that we don't use word matching here since /C/ etc. contain non-alphanum delimeters
            matchStr = BestMatchInAttr( testAttr, SLASH_AX, SLASH_COR, SLASH_SAG )
                
            -- If we got a match...
            if (matchStr) then
                TraceC( "Found view string: " .. matchStr .. "\n" )
                break         -- stop searching
            end
        end
    end

    -- If nothing found above...
    if (not matchStr) then
        TraceC( "No view information found in common tags.\n" )
    	return nil	-- fail
	-- =======================================================
    end

    -- Remap any abbreviations/misspellings into standard unabbreviated values
    if (matchStr == SLASH_SAG) then
            matchStr = SAGITTAL_VAL
    elseif (matchStr == SLASH_COR) then
            matchStr = CORONAL_VAL
    elseif (matchStr == SLASH_AX) then
            matchStr = AXIAL_VAL
    end

    -- Return the new attribute.
    return NewPropWithValue( VIEW_PROP, matchStr )
    -- ===============================================    
end

function ExtractEcho()
-- ////////////////////////////////////////////////////////////////////////////
-- This function attempts to determine the type of echo represented by the
-- image.  The function first tries to find one of the target substrings
-- "T1", "T2", "PD", in certain key fields.  If a match is found, then
-- that is taken as the echo type.  However, if we don't find such a
-- match, an attempt is made to use heuristics based on the TE, TR, and
-- Flip Angle values of the image.
--
-- Regardless of the above, the Time Echo, Time Repetition, and Flip Angle properties
-- are also set as found by this routine.
--
-- ////////////////////////////////////////////////////////////////////////////

    TraceC( "Extracting echo type, TE, TR, TI and FA...\n" );
    
    -- Get raw values as local varaiables in case they are needed later
    local te = ValueForTag( ECHO_TIME_TAG )           -- raw echo time 
    local tr = ValueForTag( REPETITION_TIME_TAG )     -- raw repetition time 
    local fa = ValueForTag(FLIP_ANGLE_TAG)     -- raw flip angle
    local ti = ValueForTag(INV_TIME_TAG)     -- raw inversion time
    
    -- Create properties for any non-nil results
    NewPropWithValue( ECHO_TIME_PROP, te );
    NewPropWithValue( REPETITION_TIME_PROP, tr );
    NewPropWithValue( FLIP_ANGLE_PROP, fa );
    NewPropWithValue( INV_TIME_PROP, ti );
     
    local matchStr = nil		-- Matched string value.
    
    -- List of tags we will examine for view information...
    local searchTags = { IMAGE_TYPE_TAG, SERIES_DESC_TAG, ACQ_DEV_PROCESS_TAG,
                         SCAN_OPTIONS_TAG, ACQ_PROTOCOL_TAG, IMAGE_COMMENTS_TAG }
    
    -- For each tag that could be checked...
    for index,tag in ipairs(searchTags) do 
    
        TraceC( "Checking tag " .. index .. ": " .. tag .. "\n" )
    
    	-- if attribute exists for given tag in this image...
        local testAttr = GetAttr(tag)
        if (testAttr) then
        
            -- Look for whole word matches of the standard numeric echo names.
            matchStr = BestWordMatchInAttr( testAttr, T1_VAL, T2_VAL, PD_VAL )
            
			-- If we got a match...
            if (matchStr) then
                TraceC( "Found echo string: " .. matchStr .. "\n" )
                return NewPropWithValue( ECHO_TYPE_PROP, matchStr )
                -- ===================================================
            end
            
            -- If this is the series description...
            if (index == 2) then
            
               TraceC( "Checking for dash variant in " .. index .. ": " .. tag .. "\n" )
            
              -- Look for substring matches of using the unsual dash form that some vendors use
              -- only in series description
              matchStr = BestMatchInAttr( testAttr, T_DASH_1_VAL, T_DASH_2_VAL )
            
               -- If we got a match...
              if (matchStr) then
 
                  TraceC( "Found echo string: " .. matchStr .. "\n" )
 
                  -- Convert from dash form to standard one.
                  if (matchStr == T_DASH_1_VAL) then
                    matchStr = T1_VAL
                  else
                    matchStr = T2_VAL
                  end
                  
                  return NewPropWithValue( ECHO_TYPE_PROP, matchStr )
                  -- ===================================================
              end
            end
        end
    end
    
    -- If we are here we must start looking at TE, TR, and Flip Angle
    TraceC( "None of the usual tags provide echo info... Analyzing TE/TR/FA.\n" );

    -- if neither Time Echo or Time Repetition is unavailable...
    if (not te or not tr) then
        return nil; -- give up
        -- ==================================
    end

    TraceC( "TE=" .. te .. ", TR=" .. tr .. "\n" )
    
    local te_short -- Will be True if TE is considered short
    local tr_short -- Will True if TR is considered short
    
    -- If no flip angle information...
    if (fa == nil) then
    
        TraceC( "No flip angle information...\n" )
    
        -- If the echo time is less than 30 milliseconds 
        -- (N.B. 10-25 ms is considered normal for short)...
        if (te <= 30) then 
              te_short = true -- Consider it to be short.
        else
              te_short = false -- Consider it to be long.
        end

	-- If the repetition time is less than 1000 milliseconds
	-- (N.B. 100-900 ms is considered normal for short)
	if ( tr <= 1000 ) then 
              tr_short = true -- Consider it to be short
	else
              tr_short = false -- Consider it to be short
	end
	
        -- If both TE and TR are short...
        if (te_short == true and tr_short == true ) then
              TraceC( "TE and TR short -> T1\n" )
              return NewPropWithValue(ECHO_TYPE_PROP, T1_VAL)
              -- =========================================
        end

        -- If both TE and TR are long...
        if (te_short == false and tr_short == false) then
              TraceC( "TE and TR long -> T2\n" )
              return NewPropWithValue(ECHO_TYPE_PROP, T2_VAL)
              -- =========================================
        end
 
        -- If echo time is short but not the repetition time... 
        if (te_short == true and tr_short == false ) then 
              -- Treat as Proton Density echo
              TraceC( "TE short and TR long -> PD\n" )
              return NewPropWithValue(ECHO_TYPE_PROP, PD_VAL)
              -- =========================================
        end
        
        -- echo time is long and the repetition time is short -- so unclassifiable this way.

        
    else  -- can factor flip angle into equation
        
        TraceC( "Flip Angle=" .. fa .. "\n" )
          
	-- If the flip angle is less than 25 degrees 
        -- (N.B. 5-20 degrees is considered normal for small and 70-80 degrees large in gradient scans )
        local fa_small
        if (fa <= 25.0) then
            fa_small = true -- Consider it to be short.
        else
            fa_small = false
        end
		
	-- If the echo time is less than 12 milliseconds 
	-- (N.B. 5-10 ms is considered normal for short and 15-25 ms long in gradient scans)
	if (te <= 12) then
            te_short = true	-- Consider it to be short.
        else
            te_short = false
        end
		
        -- If the repeat time is less than 70.0 milliseconds
        -- (N.B. 30-60 ms is considered normal for short and 100 ms long in gradient scans).
        if (tr <= 70) then
            tr_short = true	-- Consider it to be short.
        else
            tr_short = false
        end
		
        -- If both TE and TR are short and flip angle large...
        if (te_short == true and tr_short == true and fa_small == false) then
            TraceC( "TE and TR short, FA large -> T1\n" )
            return NewPropWithValue( ECHO_TYPE_PROP, T1_VAL)
            -- =========================================
        end
          
        -- If both TE and TR are long and flip angle is small...
        if (te_short == false and tr_short == false and fa_small == true) then 
            TraceC( "TE and TR long, FA small -> T2\n" )
            return NewPropWithValue( ECHO_TYPE_PROP, T2_VAL)
            -- =========================================
        end
          
        -- If TE is short, TR is long, and flip angle is small...
	if (te_short == true and tr_short == false and fa_small == true) then 
              -- treat it as Proton Density echo
            TraceC( "TE short, TR long, FA small -> PD\n" )
            return NewPropWithValue( ECHO_TYPE_PROP, PD_VAL)
             -- =========================================
        end
		
        -- Unclassifiable
 
    end
    
    -- Sophisticated classification didn't work so let's just try crude TR ranges
    -- N.B. May need to guard against IMG and Stealth for the below, since
    -- these shifts the ranges higher.
    
    
    -- If in typical lower range
    if (tr >= 300 and tr <= 999) then
            -- treat it as T1
            TraceC( "TR in typical lower range -> T1\n" )
            return NewPropWithValue( ECHO_TYPE_PROP, T1_VAL)
             -- =========================================
    end
    
    -- If in typical higher range...
    if (tr >= 3000 and tr <= 9999) then
            -- treat it as T2
            TraceC( "TR in typical higher range -> T2\n" )
            return NewPropWithValue( ECHO_TYPE_PROP, T2_VAL)
             -- =========================================
    end
    
    -- If we are here then we couldn't determine echo type at all.
    return nil
  
end

function ExtractBodyRegionMR()
-- ////////////////////////////////////////////////////////////////////////////
--
-- If the ExtractBodyRegion function in ExtractCommon came up empty, then
-- this function will look in some MR specific additional tags for hints as
-- to the body region being examined.
--
-- ////////////////////////////////////////////////////////////////////////////


    -- If the common function was already successful finding a body region value...
    local bodyRegionProp = GetAttr( BODY_REGION_PROP )
    if (bodyRegionProp) then
        TraceC( "Common script found body region... skipping special MR logic.\n" )
        return bodyRegionProp;
        --==============================================
    end
    
    TraceC( "No body region found by common script... using special MR logic.\n" )  


    local coilAttr = GetAttr(RCV_COIL_TAG) -- DICOM ACQ Receiving Coil
    
    -- If there is no coil attribute...
    if (not coilAttr) then
        return nil  -- give up
    end
  
    local sString;
  
    -- See if it contains a recognizable body part name.
    sString = BestMatchForBodyRegion(coilAttr)
        
    -- If no match or emtpy string..
    if (not sString or string.len(sString) == 0) then
        return nil  -- fail
        -- ===================================
    end
        
    -- If we are here then we found a match above.
    local bodyRegionProp = AttrValue:new()
    bodyRegionProp:AppendVal(sString)
    bodyRegionProp:Dump("body region prop")
    SetAttr(BODY_REGION_PROP, bodyRegionProp)
    return bodyRegionProp
    
end	

-- ////////////////////////////////////////////////////////////////////////////
function ExtractDescriptionMR() -- call it always after extractEcho()
-- ////////////////////////////////////////////////////////////////////////////


    -- If the common function was already successful finding a good description value...
    local seriesDescProp = GetAttr( SERIES_DESC_PROP )
    if (seriesDescProp) then
        -- If it isn't just the skipping no-info placeholder...
        if (seriesDescProp:Get(1) ~= "?") then
            TraceC( "Common script found description... skipping special MR logic.\n" )
            return seriesDescProp;
            --==============================================
        end
    end
    
    TraceC( "No good description found by common script... using special MR logic.\n" ) 

    -- Try to build a description based on key components.
    local seriesDescStr
    if (ValueForTag(IMAGE_TYPE_PROP) == "MIP") then
		-- Dont include View for MIPs as this changes through the series
		seriesDescStr = ValueForTag(ECHO_TYPE_PROP)
	else
		seriesDescStr = SmartAppendWords( ValueForTag(ECHO_TYPE_PROP), " ", ValueForTag(VIEW_PROP) )
	end
    seriesDescStr = SmartAppendWords( seriesDescStr, " ", ValueForTag(LATERALITY_PROP) )
    seriesDescStr = SmartAppendWords( seriesDescStr, " ", ValueForTag(BODY_REGION_PROP) )
    seriesDescStr = SmartAppendWords( seriesDescStr, " ", ValueForTag(PULSE_SEQ_PROP) )
     
    -- If we have an image type other than boring old "Original"...
    local imageTypeStr = ValueForTag(IMAGE_TYPE_PROP) 
    if (imageTypeStr and imageTypeStr ~= "Original" and imageTypeStr ~= "ORIGINAL") then 
        -- Append non-boring image type to custom series description
        seriesDescStr = SmartAppendWords( seriesDescStr, " ", imageTypeStr )
    end
    
    -- If we have contrast...
    if (ValueForTag(CONTRAST_PROP) == "Yes") then 
        -- Append indication that contrast is present to custom series description
        seriesDescStr = SmartAppendWords( seriesDescStr, " ", "+C")
    end
    
    -- If we had no success above...
    if (not seriesDescStr or string.len(seriesDescStr) == 0) then
        seriesDescStr = "?"
    end
    
    return NewPropWithValue( SERIES_DESC_PROP, seriesDescStr )
    
end	

function ExtractPulseSeq()
-- ////////////////////////////////////////////////////////////////////////////
-- This function attempts to determine the pulse sequence (if any) used to
-- create the image.
-- ////////////////////////////////////////////////////////////////////////////

    TraceC( "Extracting pulse sequence...\n" );
      
    -- Set up array of arrays variable to hold aliases for each type of pulse sequence.
    -- First value of each array is the standard property value and the rest are the aliases.
    -- Order arrays with shortest substring aliases ones at the end (to avoid less precise early matching).
    -- For example SE needs to follow FSE, since SE is a substring of FSE.
    local aliases  = { { THREEDUF_VAL,  "3D Ultra Fast", "MP Rage", "3DFGRE", "3D Fast SPGR", "3D TFE", "3D TOF-SPGR" },
                       { CINE_PS_VAL,   "CINE FLASH", "FASTCARD" },
                       { DWEPI_VAL,     "Diffusion-Weighted Echo Planar Imaging" },
                       { IPAT_VAL,      "Parallel Imaging", "ASSET", "SENSE" },
                       { MYO_PERF_VAL,  "Myocardial Perfusion - Cardiac", "2D TurboFLASH", "SS TrueFisp sat or Ir", "gre-epi", 
                                        "FGR-ET multiphase", "Multislice perfusion", "Saturation TFE/EPI", "B-FFE", "TFE" },
                       { SEBB_C_VAL,    "Spin Echo Black Blood - Cardiac", 
                                        "dark-blood prepared TSE", "HASTE", "FLASH", "Double IR FSE", "FRE-XL", 
                                        "FSE-XL with Blood suppression", "Black Blood pre pulse" },
                       { SEBB_NF_VAL,   "Spin Echo Black Blood - Null Fat",
                                        "TRIM", "Triple IR FSE", "FSE-XL IR with Blood suppression", 
                                        "Black Blood pre pulse plus SPIR" },
                       { SPIR_VAL,      "Spectral Inversion Recovery" },
                       { SSFP_VAL,      "Half Fourier Single Shot Turbo Fast Spin Echo", 
                                        "Steady State Free Process", "TrueFISP", "FIESTA", "SARGE", "Balanced-FFE", "CE Fast" },
                       { TOF_GRE_VAL,   "Time of Flight - Gradient Echo" },
                       { TOF_SPGR_VAL,  "Time of Flight - Spoiled Gradient Echo" },
                       { SSFSE_IR_VAL,  "Half Fourier Single Shot Turbo Fast Spin Echo - Inversion Recovery", "TrueIR/FISP" },
                       { SSFSE_VAL,     "HASTE", "Single shot TSE", "EXPRESS" },
                       { STIR_VAL,      "Short Tau IR", "Short Tau Inversion Recovery", "FMPIR" },
                       { UFGRE_VAL,     "Ultra Fast Gradient Echo", "Turbo Flash", "Fast GRE", 
                                        "Fast SPGR", "Turbo Field Echo", "TFE", "RF Fast" },
                       { VENC_VAL,      "Velocity Encoding", "Phase Contrast", "PC Velocity", "Quantitative Flow" },
                       { VIA_C_VAL,     "Viability Imaging - Cardiac", 
                                        "Segmented 2D TurboFLASH", "Myocardial Delayed enhancement (MDE) IR Prep Gated FGRE", 
                                        "Delayed Hyper-enhancement IR TFE" },
                       { VIBE_VAL,      "Volume Interpolated Breathhold Exam", 
                                        "FAME", "Fast Acquisition with Multiphase Efgre3D" },
                       { SPGR_VAL,      "Spoiled Gradient Echo", "FLASH", "T1FFE", "T1fast", "NOSE" },
                       { GMR_VAL,       "Gradient Motion Rephasing" },
                       { MSE_VAL,       "Multiple Spin Echo" },
                       { GRE_VAL,       "Gradient Echo", "GRASS", "GE", "Fast Field Echo", "FFE", "Field Echo", "FAST", "GR" },
                       { GSE_VAL,       "Gradient and Spin Echo", "Turbo GSE", "TGSE", "GRASE", "Grase" },
                       { FLAIR_VAL,     "Fluid-Attenuated Inversion Recovery", "FLA" },
                       { FIR_VAL,       "Fast Inversion Recovery", "Fast IR" },
                       { EPI_VAL,       "Echo Planar Imaging" },
                       { MTC_VAL,       "Magnetization Transfer Contrast", "MTC-SE" },
                       { MT_VAL,        "Magnetization Transfer", "MT-SE" },
                       { IR_VAL,        "Inversion Recovery", "TurboIR", "TIR", "MPIR", "IR-TFE", "IR-TSE" },
                       { FSE_VAL,       "Fast Spin Echo", "RARE", 
                                        "Rapid Acquisition Relaxation Enhancement", "Turbo Spin Echo", "TSE", "Turbo Spin Echo" }, 
                       { SE_VAL,        "Spin Echo" } 
                     }

    
    local matchStr = nil		-- Matched string value.
    local aliasWords
    local i, j, index, tag
    
    -- List of tags we will examine for view information...
    local searchTags = { SERIES_DESC_TAG, ACQ_DEV_PROCESS_TAG, ACQ_SCAN_SEQ_TAG,
				ACQ_PROTOCOL_TAG, HIDDEN_PULSE_SEQ_INFO_TAG }
    
    -- For each tag that could be checked...
    for index,tag in ipairs(searchTags) do 
      
      -- For each possible property value...
      for i,aliasWords in ipairs(aliases) do
      
          local testVal = aliasWords[1] 
    
          TraceC( "Checking tag " .. index .. ": " .. tag .. " for aliases of [" .. testVal .. "]\n" )
    
          -- if attribute exists for given tag in this image...
          local testAttr = GetAttr(tag)
          if (testAttr) then
              for j,word in ipairs(aliasWords) do
              
                  -- If the word is less than four letters long...
                  if (string.len(word) < 4) then

                      -- Look only for full word matches for this alias 
                      -- (to avoid false positives 
                      matchStr = BestWordMatchInAttr( testAttr, word ) 
                      
                  else
              
                      -- Look for substring matches for this alias
                      matchStr = BestMatchInAttr( testAttr, word )
                  end
                
                  -- If we got a match...
                  if (matchStr) then
                      TraceC( "Found pulse sequence string [" .. matchStr .. 
                              "] indicating value [" .. testVal .. "]\n" )
                      return NewPropWithValue( PULSE_SEQ_PROP, testVal )
                      -- ===================================================
                  end
              end
          end
      end
    end
    
    TraceC( "No pulse sequence alias was found.\n" )
    
end

-- ////////////////////////////////////////////////////////////////////////////
function ExtractPhaseMR()
-- ////////////////////////////////////////////////////////////////////////////
	
	--$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
	SPECTRUM_VAL = "Spectrum"
	BUTTERWORTH_VAL = "Butterworth"  -- Alternate for Spectrum
	WEST_MICH_VAL = "West Mich"  -- Alternate for Spectrum
	BLODGETT_VAL = "Blodgett" -- Alternate for Spectrum
  	MR_SLMT_VAL = "MR SLMT" --  Alternate for St. Lukes Episcopal Hospital 
	ST_LUKES_OUTPATIENT_VAL = "St Lukes Outpatient Imaging" 
   
    local SiteName = GetAttr( INSTITUTION_NAME_TAG )
	
	-- Generic Site Customization: All sites that require scouts up front on the thumbnail bar
    if ( SiteName ~= nil and BestWordMatchInAttr(SiteName, SPECTRUM_VAL, BUTTERWORTH_VAL, 
													WEST_MICH_VAL, BLODGETT_VAL,
													MR_SLMT_VAL, ST_LUKES_OUTPATIENT_VAL) ) then
		return nil
    end
	--$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
	
	-- Checking for images from Hitachi Echelon; phase information in these images is indicated in 
	-- (0020, 0012) REL Acquisition Number
    if ( ValueForTag(DEVICE_MANUFACTURER_TAG) == "Hitachi Medical Corporation" and 
        ValueForTag(DEVICE_MODEL_TAG) == "ECHELON") then
	
		TraceC( "Recognized HITACHI device (ECHELON) - phase set by relative acquistion number.\n" )
        return NewPropFromTag( PHASE_PROP, ACQ_NUMBER_TAG)

	end
    
	local tempPosId = ValueForTag( TEMP_POSITION_IDENTIFIER_TAG )
    if (tempPosId == nil) then
		--No need to proceed further as (0020, 0100) does not exist
		return nil
	end
    
	-- Retrieve number of temporal positions. If the number of temporal positions is greater than 6,
	-- then no phase value is returned to ensure that the thumbnail bar is not unmanageable
    local numTemporalPosTag = GetAttr(NUM_TEMP_POS_TAG)
    local useTempPositionID = true
    
	if (numTemporalPosTag) then
    
        local tagValue = ValueForTag(NUM_TEMP_POS_TAG)
        if ( tagValue > 6) then
            useTempPositionID = false
        end
		
    end
	
	------------------------------------------------------------------------------------------------------
	-- MaxN: A discussion with Philips revelas that the above check is not accurate; a radiologists  
	-- may perform 300 temporal positions - this is the number stored in (0020, 0105); but he may 
	-- actually send only 3 of these to PACS. Our check above would prevent us from splitting this
	-- even though we should. For now, we have introduced an exception for diffusion sequences from
	-- Philips devices.
	local  seriesDescTag= GetAttr(SERIES_DESC_TAG) 
	DW_VAL = "DW/SSh"
	
	if ((ValueForTag(DEVICE_MANUFACTURER_TAG) == "Philips Medical Systems") and 
		(seriesDescTag~=nil and BestMatchInAttr(seriesDescTag, DW_VAL)) ) then 
		useTempPositionID = true
	end
	------------------------------------------------------------------------------------------------------	
    
	if (useTempPositionID) then
		TraceC( "Phase set by temporal position identifier.\n" )
		return NewPropWithValue( PHASE_PROP, tempPosId)
	end
	
	-- If we are here, then we could not use (0020, 0100) for phase
	return nil
end

-- ////////////////////////////////////////////////////////////////////////////
function ExtractPhaseNameMR()
-- ////////////////////////////////////////////////////////////////////////////

	local matchStr = nil
	local seriesDescTag = GetAttr(SERIES_DESC_TAG)
	
	if (seriesDescTag) then
	
		matchStr = BestMatchInAttr( seriesDescTag, BEFORE_VAL, ARTERIAL_VAL,
							CAPILLARY_VAL, VENOUS_VAL )
	end
	
	return NewPropWithValue( PHASE_NAME_PROP, matchStr)	
end
 
-- ////////////////////////////////////////////////////////////////////////////
function ExtractSuppressionType()
--////////////////////////////////////////////////////////////////////////////

	--ErrorC(" Set image type based on appropriate modality.\n", "..." )
	local matchStr = nil
	local imageTypeVal = ValueForTag( IMAGE_TYPE_TAG ) 	 
 
	-- Compare to see image value tag exists and matches to "IMAGE ADDITION" or 
	-- "Image SUBTRACT" 
	TraceC( "Checking tag...... \n")
	 
	 local testAttr = GetAttr(IMAGE_TYPE_TAG)
	 
	 if (testAttr) then  
		matchStr = BestWordMatchInAttr( testAttr, IMAGE_FAT_VAL, IMAGE_WATER_VAL)
	
		if (matchStr == IMAGE_FAT_VAL)  then 
			TraceC("Found fat suppressed image", "..." ) 
			matchStr="Fat" 
	       elseif (matchStr == IMAGE_WATER_VAL) then 
			TraceC("Found water suppressed image", "..." )	 
			matchStr="Water" 
		end
	end	
	 
	return NewPropWithValue( IMAGE_SUPPR_PROP, matchStr ) 
end

function ExtractDiffCoef()
-- ////////////////////////////////////////////////////////////////////////////

	--$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
	-- MaxN: Some sites do not want sequences split on diffusion coefficients; such
	-- sites are handled here with the "dreaded" site-specific code
	SPECTRUM_VAL = "Spectrum"
	BUTTERWORTH_VAL = "Butterworth"  -- Alternate for Spectrum
	WEST_MICH_VAL = "West Mich"  -- Alternate for Spectrum
	BLODGETT_VAL = "Blodgett" -- Alternate for Spectrum
	MTRL_SACRE_COEUR_VAL = "HOPITAL DU SACRE-COEUR DE MONTREAL"
	CHRISTUS_VAL = "Christus St Francis Cabrini"
	
	local SiteName = GetAttr( INSTITUTION_NAME_TAG )
	
	if ( SiteName~=nil and BestWordMatchInAttr(SiteName, SPECTRUM_VAL, BUTTERWORTH_VAL, 
																WEST_MICH_VAL, BLODGETT_VAL, MTRL_SACRE_COEUR_VAL,
																CHRISTUS_VAL) ) then
		return nil
	end
	--$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

    -- There is no DICOM standard for diffusion coefficient encoding at this time,
    -- so we use some vendor specific knowledge to ferret it out.
    local manuStr = ValueForTag( DEVICE_MANUFACTURER_TAG )
    
    -- Get series description attribute for keyword searches
    local seriesDescAttr = GetAttr( SERIES_DESC_TAG )   
    
    -- If there is no series description...
    if (seriesDescAttr == nil) then
        -- none of the tests below will be successful
        return nil
    end

    -- If the manufacturer is Siemens...
    if (manuStr == "SIEMENS") then
    
           TraceC( "Looking for Siemens specific diffusion coefficient encoding\n" )
    
           -- Look for an optional sequence name tag
           local acqAttr = GetAttr( ACQ_SEQ_NAME_TAG )
              
           -- If we found the tag...
           if (acqAttr ~= nil) then
              
              -- look for a standard value formatted value b#
              local dcStr = BestMatchInAttr( acqAttr, B0_VAL, B500_VAL, B1000_VAL, 
															B1_VAL, B2_VAL, B3_VAL, B4_VAL, B5_VAL, B6_VAL )
              
              -- If we recognized a standard value...
              if (dcStr ~= nil) then
                
                TraceC( "Diffusion coefficient found [" .. dcStr .. "]\n" )

                -- Adopt it
                return NewPropWithValue( DIFF_COEF_PROP, dcStr )
                -- ===================================================   
              end
              
           end
    
    -- If the manufacturer is General Electric...
    elseif (manuStr == "GE MEDICAL SYSTEMS") then
    
        -- if the series description contains the substring "DWI" or "DW"...
        if (seriesDescAttr ~= nil and BestMatchInAttr( seriesDescAttr, DWI_VAL, DW_VAL, DIFF_VAL ) ) then
  
            TraceC( "Looking for GE specific diffusion coefficient encoding\n" )
    
            -- Get the first value of the GE private image options tag
            local bValueStr = ValueForTag( GE_PRIV_IMAGE_OPTIONS_TAG )
			local diffType = ValueForTag(GE_PRIV_DIFF_TYPE_TAG)
			local vasCollValue = ValueForTag(GE_PRIV_VAS_COLL_TAG)
			
            -- If we found a value...
            if (bValueStr ~= nil) then
              
                local dcStr
                
                -- If the b-value is zero...
                if (bValueStr == 0 or vasCollValue == 14) then
                
                  -- It is the first coefficent (images are T2 weighted with no diffusion )
                  dcStr = B0_VAL
                  
                elseif ((diffType > 0) and (vasCollValue ~= 14) and (bValueStr > 0)) then
				
					-- a diffusion image has been identified
					dcStr =  SmartAppendWords("b",nil,string.format('%s', bValueStr)) 
					
				else  -- no known b-value
                
                  -- It is one of the later coefficient, but not known which one for GE.
                  dcStr = BN_VAL
                  
                end
              
                TraceC( "Diffusion coefficient found [" .. dcStr .. "]\n" )

                -- Adopt it
                return NewPropWithValue( DIFF_COEF_PROP, dcStr )
                -- ===================================================   
                
              end
              
        end

    end
    
    -- if the series description contains "ZERO" or "B0" keyword substring...
    local keyword = BestMatchInAttr( seriesDescAttr, ZERO_VAL, B0_VAL )
    if (keyword ~= nil) then
    
        -- We don't know which coefficient this is, so use the BN notation.
        TraceC( "Generic diffusion coefficient implied by series description keyword: " .. keyword .. "\n" )

        return NewPropWithValue( DIFF_COEF_PROP, B0_VAL )
        -- ===================================================   

    end
    
    -- if the series description contains other diffusion keywords substrings...
    local keyword = BestMatchInAttr( seriesDescAttr, DWI_VAL, DW_VAL, DIFFUSION_VAL, DIFF_VAL, SLASH_DIF_VAL )
    if (keyword ~= nil) then
    
        -- We don't know which coefficient this is, so use the BN notation.
        TraceC( "Generic diffusion coefficient implied by series description keyword: " .. keyword .. "\n" )

        return NewPropWithValue( DIFF_COEF_PROP, BN_VAL )
        -- ===================================================   

    end

    -- No tricks left (assume this is just a regular non-diffusion series
    if (manuStr) then
	TraceC( "Diffusion coefficient not found with vendor: " .. manuStr .. "\n" )
    end
    return nil 
    -- ===================================================    
    
end	

--[[
	MaxN: This portion of code was written to use functionality of IP that is not yet 
	implemented. There is no timeline on when this will be available and the functionality 
	itself is not considered critical. This is commented out to  prevent compiling and
	execution of unnecessary code.
	
function ExtractMIPSpecXAxisInfoMR()
-- ////////////////////////////////////////////////////////////////////////////

    local imageType = ValueForTag( IMAGE_TYPE_PROP )
    local imageOrientationAttr = GetAttr(IMAGE_ORIENTATION_PATIENT_TAG)
    
    if ( (imageType ~= "MIP") or not(imageOrientationAttr) ) then
	-- this is not a MIP sequence or does not have direction cosine information => we cannot proceed
	return nil;
    end

    local xx =  imageOrientationAttr:Get(1)
    local xy =  imageOrientationAttr:Get(2)
    local xz =  imageOrientationAttr:Get(3)

    local stringXX = string.format('%s', xx)
    local stringXY = string.format('%s', xy)
    local stringXZ = string.format('%s', xz)
    
    local formattedXX = string.sub(stringXX, 1, 9)
    local formattedXY = string.sub(stringXY, 1, 9)
    local formattedXZ = string.sub(stringXZ, 1, 9)
    
    local doubleXX = tonumber(formattedXX)
    local doubleXY = tonumber(formattedXY)
    local doubleXZ = tonumber(formattedXZ)    
    
    local xAxisOrientProp = AttrValue:new()
    xAxisOrientProp:AppendVal(doubleXX)
    xAxisOrientProp:AppendVal(doubleXY)
    xAxisOrientProp:AppendVal(doubleXZ)
    
    SetAttr( XAXIS_ORIENT_PROP, xAxisOrientProp)
    
    return xAxisOrientProp
end

function ExtractMIPSpecYAxisInfoMR()
-- ////////////////////////////////////////////////////////////////////////////

    local imageType = ValueForTag( IMAGE_TYPE_PROP )
    local imageOrientationAttr = GetAttr(IMAGE_ORIENTATION_PATIENT_TAG)
    
    if ( imageType ~= MIP_VAL or not(imageOrientationAttr) ) then
	-- this is not a MIP sequence or does not have direction cosine information => we cannot proceed
	return nil;
    end

    local yx =  imageOrientationAttr:Get(4)
    local yy =  imageOrientationAttr:Get(5)
    local yz =  imageOrientationAttr:Get(6)
    
    local stringYX = string.format('%s', yx)
    local stringYY = string.format('%s', yy)
    local stringYZ = string.format('%s', yz)
    
    local formattedYX = string.sub(stringYX, 1, 9)
    local formattedYY = string.sub(stringYY, 1, 9)
    local formattedYZ = string.sub(stringYZ, 1, 9)
    
    local doubleYX = tonumber(formattedYX)
    local doubleYY = tonumber(formattedYY)
    local doubleYZ = tonumber(formattedYZ)
    
    local yAxisOrientProp = AttrValue:new()
    yAxisOrientProp:AppendVal(doubleYX)
    yAxisOrientProp:AppendVal(doubleYY)
    yAxisOrientProp:AppendVal(doubleYZ)
    
    SetAttr( YAXIS_ORIENT_PROP, yAxisOrientProp)
    
    return yAxisOrientProp
end
--]]

function ExtractThumbnailGroupMR()
-- This function was added for further manipulation of the thumbnail sorting
-- for MR studies. Right now, Diffusion sequences are ordered with lesser priority 
-- than other Original image type sequences
-- ////////////////////////////////////////////////////////////////////////////
    local newGrpValue = nil
    local diffCoefProp = GetAttr(DIFF_COEF_PROP)
	local bPushDiffBehind = true
	
	--$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
	-- Sites that require this customization go here
    MOUNTAIN_VIEW_VAL = "Mountain View"
    COLUMBUS_VAL = "Columbus"
	UNIVCINN_VAL = "UNIV HOSP CINTI"
	CHRISTUS_VAL = "Christus St Francis Cabrini"
	
    local SiteName = GetAttr( INSTITUTION_NAME_TAG )
	local imageType = ValueForTag(IMAGE_TYPE_PROP)
	local sDescTag = GetAttr(SERIES_DESC_TAG)
    
	-- The following code ensures that scouts are placed at the front
	if ( SiteName ~= nil and BestWordMatchInAttr(SiteName, COLUMBUS_VAL, UNIVCINN_VAL) ) then 
        if (imageType =="Scout") then   
			newGrpValue = "00"
		end
	end
	
	-- The following code is a part of a site hack for Christus St Francis Cabrini to ensure that sequences titled
	-- "New Series" are not split but sent to the end of the thumbnail bar
	if ( SiteName~=nil and BestWordMatchInAttr(SiteName, CHRISTUS_VAL) ) then
		if( sDescTag~=nil and BestWordMatchInAttr(sDescTag, "New Series") ) then
			newGrpValue = "12"
		end
	end
	  
	-- Site hack for Spectrum Health to ensure that sequences titled
	-- "Function" or "function" are sent to the end of the thumbnail bar.
	if (SiteName~=nil and BestWordMatchInAttr(SiteName, "Spectrum")) then
        -- perform case insensitive search
        local descStr = ValueForTag(SERIES_DESC_PROP,"?") -- const string
        local seekWord = string.lower(descStr)
        if (string.find(seekWord, "function", 1, true) ~= nil) then
			newGrpValue = "23"
		end
	end
	  
	-- The following code ensures that diffusion sequences are not pushed to the end of the thumbnails with image type "Original"	
    if ( SiteName ~= nil and BestWordMatchInAttr(SiteName, MOUNTAIN_VIEW_VAL, COLUMBUS_VAL) ) then
		bPushDiffBehind = false
	end
	--$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
	
    if (diffCoefProp and bPushDiffBehind and ( ValueForTag(IMAGE_TYPE_PROP) == "Original" )) then
        newGrpValue = "02"
    end
    
    return NewPropWithValue( THUMBNAIL_GRP_PROP, newGrpValue )
end

-- ////////////////////////////////////////////////////////////////////////////
-- extract MR method
-- ////////////////////////////////////////////////////////////////////////////

function Execute()

    TraceC("Extract MR executing.\n")

    DeclareTags()
	
    -- N.B. Since this script is used for multiple MR based modalities (MR, MRA, MS) we
    -- don't do a single modality sanity check like in single modality scripts.
    
    -- Tag our output with the script version
    NewPropWithValue( MODALITY_SCRIPT_VER_PROP, "$Revision: 2 $ - $Modtime: 17/09/09 11:27a $ - MR" )
    
    -- Extract modality specific attributes
    ExtractDiffCoef()
    ExtractViewMR()
    ExtractEcho()
    ExtractBodyRegionMR()
    ExtractPulseSeq()
    ExtractDescriptionMR()
    ExtractPhaseMR()
    ExtractPhaseNameMR() 
    ExtractSuppressionType()
    ExtractThumbnailGroupMR()
    
	-- Experimental function, please leave commented out
	--ExtractMIPSpecXAxisInfoMR()
    --ExtractMIPSpecYAxisInfoMR()

    TraceC("Extract MR execution complete.\n")
    
    --==============================================
end