GUI for setting image scale using cursors

Often I want to quickly plot my data over an image of a plot from some reference source. Various packages already exist that have some kind interface for setting the dimension scaling of a graph image so that the image plot axes are registered with the graph window axes. This is a stand-alone piece of code to make it easy to set the image scaling interactively, by positioning cursors and typing in the desired values for the cursor positions.

How to use it:

  1. Use the "Load, Display and Scale Image..." menu item to load an image file, or
    1. Load an image of a graph (with linear axes) into Igor
    2. Display the image using Windows > New > Image Plot...
    3. Right click on image in plot and select "Set Image Scale"
  2. Position a pair of cursors at high and low values for each axis and type desired cursor values into the corresponding SetVariable controls
  3. Right click and select "Done with Set Image Scale" to finish

 

#pragma rtGlobals=3
#pragma ModuleName=ImageScaleGUI
#pragma version=1.2

// GUI for setting image scaling using cursors
// how to use:
// create a new image plot
// right click and select 'Set Image Scale'
// position cursors and enter desired values for cursor positions
// right click and select 'Done with Set Image Scale'
menu "TracePopup", dynamic
    ImageScaleGUI#ImageTraceMenu(), /Q, ImageScaleGUI#ScaleImageInGraph()
end

menu "Load Waves"
    "Load, Display and Scale Image...", /Q, ImageScaleGUI#LoadAndScaleImage()
end

static function LoadAndScaleImage()
    ImageLoad
    if(v_flag==0)
        return 0
    endif
    Display
    AppendImage $S_fileName
    SetAxis/A/R left
    ScaleImageInGraph()
end

static function /T ImageTraceMenu()
    if (WinType("")!=1)
        return "" // don't do anything if Igor is just rebuilding the menu
    endif  
    ControlInfo SetVarA
    if(v_flag!=0)
        return "Done with Set Image Scale"
    endif
    // figure out graph and trace names
    GetLastUserMenuInfo
    return SelectString(strlen(ImageNameList(s_graphname, ";")) > 0, "", "Set Image Scale")
end

static function ScaleImageInGraph()
    ControlInfo SetVarA
    if(v_flag!=0) // SetVar exists, clean up
        SetWindow kwTopWin hook(setscaleGUI)=$""
        KillControl SetVarA; KillControl SetVarB; KillControl SetVarC; KillControl SetVarD
        Cursor /K A; Cursor /K B; Cursor /K C; Cursor /K D
        return 1
    endif  
    string strImage=StringFromList(0, ImageNameList("", ";"))
    wave wImage=ImageNameToWaveRef("", strImage)
    variable hSize=DimSize(wImage, 0), vSize=DimSize(wImage,1)
    // vertical hairs for X cursors
    Cursor /N=1/S=2/I/H=2/C=(65535,0,0)/p A $strImage 0.1*hSize, 0.2*vSize
    Cursor /N=1/S=2/I/H=2/C=(65535,0,0)/p B $strImage 0.9*hSize, 0.2*vSize
    // horizontal hairs for Y cursors
    Cursor /N=1/S=2/I/H=3/C=(0,65535,0)/p C $strImage 0.2*hSize, 0.9*vSize
    Cursor /N=1/S=2/I/H=3/C=(0,65535,0)/p D $strImage 0.2*hSize, 0.1*vSize
   
    string info=ImageInfo("", strImage, 0)
    string strXaxis=StringByKey("XAXIS",info)
    string strYaxis=StringByKey("YAXIS",info)
    STRUCT Point pt
   
    Make /free/T csr={"A","B","C","D"}
    int i
    for(i=0;i<4;i+=1)
        SetVariable $"SetVar"+csr[i] title="", value=_NUM: i<2 ? xcsr($csr[i]) : vcsr($csr[i])
        SetVariable $"SetVar"+csr[i] limits={-Inf,Inf,0}, size={40,10}, fsize=14, Proc=ImageScaleGUI#Rescale
        SetVariable $"SetVar"+csr[i] valueColor=(65535*(i<2),65535*(i>1),0)
    endfor

    SetWindow kwTopWin hook(setscaleGUI)=ImageScaleGUI#ImgHook, hookevents=4
    // enter ImgHook function with resize event to reposition setvars
    STRUCT WMWinHookStruct s
    s.eventcode=6
    ImgHook(s)
end

static function ImgHook(STRUCT WMWinHookStruct &s)     
    string info, strXaxis, strYaxis
   
    switch (s.eventcode)
        case 6:
        case 7:
        case 8:        
            info=ImageInfo("", s.traceName, 0)
            strXaxis=StringByKey("XAXIS",info)
            strYaxis=StringByKey("YAXIS",info)     
           
            if(s.eventcode==7) // cursormoved
                if(GrepString(s.cursorName,"[A-D]")==0)
                    return 0
                endif
                   
                // keep axis coordinates within bounds of axes
                variable ptX, ptY
                GetAxis /Q $strXaxis
                ptX=max(xcsr($s.cursorName), min(V_Min,V_Max))
                ptX=min(ptX, max(V_Min,V_Max))
                GetAxis /Q $strYaxis
                ptY=max(vcsr($s.cursorName), min(V_Min,V_Max))
                ptY=min(ptY, max(V_Min,V_Max))
               
                STRUCT Point pt
                pt.h=PixelFromAxisVal("", strXaxis, ptX)
                pt.v=PixelFromAxisVal("", strYaxis, ptY)
               
                variable val=GrepString(s.cursorName,"[AB]") ? xcsr($s.cursorName) : vcsr($s.cursorName)
                       
                SetVariable $"SetVar"+s.cursorName value=_NUM:val, pos={pt.h-20,pt.v-10}, disable=0
                break
            endif
   
            // reposition or disable setvars when window is resized
            s.eventCode=7 // prepare to reenter this function with cursormoved eventcode
            Make /free/T csr={"A","B","C","D"}
            int i
            for(i=0;i<4;i+=1)
                s.cursorName=csr[i]
                variable csrpos = i>1 ? vcsr($s.cursorName) : xcsr($s.cursorName)
                GetAxis /Q $SelectString(i>1, strXaxis, strYaxis)
                if(csrpos>min(v_max,v_min) && csrpos<max(v_max,v_min))
                    ImgHook(s)
                else
                    SetVariable $"setvar"+(s.cursorName) disable=1
                endif
            endfor
    endswitch
    return 0
end

static function Rescale(STRUCT WMSetVariableAction &s) 
    if(s.eventCode!=8)
        return 0
    endif
    string strImage=StringFromList(0, ImageNameList("", ";"))
    wave wImage=ImageNameToWaveRef("", strImage)
    int isX = GrepString((s.ctrlName), "[AB]"), autoscale=1
    string strAxis, info, flags
    info=ImageInfo("", strImage, 0)
    strAxis=StringByKey(SelectString(isX, "YAXIS", "XAXIS"),info)
    info=AxisInfo("", strAxis)
    flags=StringByKey("SETAXISFLAGS", info)
    variable indexMin, indexMax
    if(GrepString(flags, "/")==0)
        GetAxis /Q $strAxis
        indexMin=scaleToIndex(wImage, V_min, 1-isX)
        indexMax=scaleToIndex(wImage, V_max, 1-isX)
        autoscale=0
    endif
   
    variable ValAC, ValBD, delta, offset, oldDelta
    ControlInfo $SelectString(isX, "SetVarC", "SetVarA")
    ValAC=V_Value
    ControlInfo $SelectString(isX, "SetVarD", "SetVarB")
    ValBD=V_Value
           
    delta = isX ? (ValBD-ValAC)/(pcsr(B)-pcsr(A)) : (ValBD-ValAC)/(qcsr(D)-qcsr(C))
    offset = isX ? ValAC - delta*pcsr(A) : ValAC - delta*qcsr(C)
    oldDelta = DimDelta(wImage, 1-isX)
    if(isX)
        SetScale /P x, offset, delta , wImage
    else
        SetScale /P y, offset, delta , wImage
    endif
   
    if(autoscale==0)
        v_min=IndexToScale(wImage, indexMin, 1-isX)
        v_max=IndexToScale(wImage, indexMax, 1-isX)
        if(v_min>v_max)
            SetAxis /R $strAxis v_min, v_max
        else
            SetAxis $strAxis v_min, v_max
        endif
    endif
   
    if(sign(oldDelta)!=sign(delta))
        // switch the axis limits so that image is not flipped
        if(GrepString(flags, "/R"))
            SetAxis /A $strAxis
        elseif(GrepString(flags, "/A"))
            SetAxis /A/R $strAxis
        endif
    endif

    return 0
end

 

ImageScaleGUI120_0.zip

Edited snippet to add a menu item that I find useful, and fixed some annoyances related to changing axis values after rescale.

Forum

Support

Gallery

Igor Pro 8

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More