![](/profiles/wavemetrics/themes/wavemetrics/logo.png)
GUI for setting image scale using cursors
![](/sites/default/files/styles/thumbnail/public/tony/profile-images/2023-09/mugshot.png?itok=kFS-G0qQ)
tony
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:
- Use the "Load, Display and Scale Image..." menu item to load an image file, or
- Load an image of a graph (with linear axes) into Igor
- Display the image using Windows > New > Image Plot...
- Right click on image in plot and select "Set Image Scale"
- Position a pair of cursors at high and low values for each axis and type desired cursor values into the corresponding SetVariable controls
- Right click and select "Done with Set Image Scale" to finish
#pragma TextEncoding="UTF-8" #pragma rtGlobals=3 #pragma IndependentModule=ImageScaleGUI #pragma version=1.70 // GUI for setting image scaling using cursors // How to use: // Either select "Load, Display and Scale Image..." from Load Waves menu, // or 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'. // Uncomment the definition below to add items to trace popup menu that allow // scaling to be transferred from one displayed image to another: //#define CopyPasteScaling #ifdef CopyPasteScaling menu "TracePopup", dynamic ImageScaleGUI#ScaleMenuString(0), /Q, ImageScaleGUI#CopyImageScale() ImageScaleGUI#ScaleMenuString(1), /Q, ImageScaleGUI#PasteImageScale() end #endif menu "TracePopup", dynamic ImageScaleGUI#ImageTraceMenu(), /Q, ImageScaleGUI#ScaleImageInGraph() end menu "Load Waves" "Load, Display and Scale Image...", /Q, ImageScaleGUI#LoadAndScaleImage() end function /S ScaleMenuString(int paste) if (WinType("") != 1) return "" // don't do anything if Igor is just rebuilding the menu endif // figure out graph and trace names GetLastUserMenuInfo if (strlen(ImageNameList(s_graphname, ";")) == 0) return "" endif if (paste == 0) return "Copy Image Scale" elseif (cmpstr(GetScrapText()[0,7], "SetScale") == 0) return "Paste Image Scale" endif return "" end function CopyImageScale() GetLastUserMenuInfo wave /Z w = ImageNameToWaveRef(s_graphname, StringFromList(0, ImageNameList(s_graphname, ";"))) if (WaveExists(w) == 0) return 0 endif string cmd = "" sprintf cmd, "SetScale /P x, %g, %g, ###; SetScale /P y, %g, %g, ###;", DimOffset(w, 0), DimDelta(w, 0), DimOffset(w, 1), DimDelta(w, 1) PutScrapText cmd end function PasteImageScale() GetLastUserMenuInfo string strImage = StringFromList(0, ImageNameList(s_graphname, ";")) wave /Z wImage = ImageNameToWaveRef(s_graphname, strImage) if (WaveExists(wImage) == 0) return 0 endif variable x0, dx, y0, dy sscanf GetScrapText(), "SetScale /P x, %g, %g, ###; SetScale /P y, %g, %g, ###;", x0, dx, y0, dy if (V_flag != 4) return 0 endif int autoscale, dim string strAxis = "", strInfo = "", strFlags = "" strInfo = ImageInfo("", strImage, 0) variable indexMin, indexMax, delta, oldDelta for (dim=0;dim<2;dim+=1) autoscale = 1 strAxis = StringByKey(SelectString(dim, "XAXIS", "YAXIS"), strInfo) strFlags = StringByKey("SETAXISFLAGS", AxisInfo("", strAxis)) oldDelta = DimDelta(wImage, dim) delta = dim ? dY : dX if (GrepString(strFlags, "/")==0) GetAxis /Q $strAxis indexMin = scaleToIndex(wImage, V_min, dim) indexMax = scaleToIndex(wImage, V_max, dim) autoscale = 0 endif if (dim == 0) SetScale /P x, x0, delta, wImage else SetScale /P y, y0, delta, wImage endif if (autoscale == 0) v_min = IndexToScale(wImage, indexMin, dim) v_max = IndexToScale(wImage, indexMax, dim) 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(strFlags, "/R")) SetAxis /A $strAxis elseif (GrepString(strFlags, "/A")) SetAxis /A/R $strAxis endif endif endfor end function LoadAndScaleImage() try ImageLoad /T=any; AbortOnRTE catch // Clear the error silently. variable Verror = GetRTError(1) // 1 to clear the error doalert 0, "Could not load image" return 0 endtry if(v_flag == 0) return 0 endif Display AppendImage $S_fileName SetAxis/A/R left ScaleImageInGraph() end 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 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 strInfo = ImageInfo("", strImage, 0) string strXaxis = StringByKey("XAXIS",strInfo) string strYaxis = StringByKey("YAXIS",strInfo) 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 function ImgHook(STRUCT WMWinHookStruct &s) string strInfo, strXaxis, strYaxis switch (s.eventcode) case 6: case 7: case 8: strInfo = ImageInfo(s.winName, s.traceName, 0) // if s.tracename is empty this will be top image. strXaxis = StringByKey("XAXIS",strInfo) strYaxis = StringByKey("YAXIS",strInfo) 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 = limit(xcsr($s.cursorName, s.winName), min(V_Min,V_Max), max(V_Min,V_Max)) GetAxis /Q $strYaxis ptY = limit(vcsr($s.cursorName, s.winName), min(V_Min,V_Max), max(V_Min,V_Max)) STRUCT Point pt pt.h = PosFromAxisVal(s.winName, strXaxis, ptX) pt.v = PosFromAxisVal(s.winName, strYaxis, ptY) variable val = GrepString(s.cursorName,"[AB]") ? xcsr($s.cursorName, s.winName) : vcsr($s.cursorName, s.winName) SetVariable $"SetVar"+s.cursorName win=$s.winName, 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, s.winName) : xcsr($s.cursorName, s.winName) GetAxis /W=$s.winName/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), win=$s.winName, disable=1 endif endfor endswitch return 0 end function Rescale(STRUCT WMSetVariableAction &s) if (s.eventCode != 8) return 0 endif string strImage = StringFromList(0, ImageNameList(s.win, ";")) wave wImage = ImageNameToWaveRef(s.win, strImage) int isX = GrepString((s.ctrlName), "[AB]"), autoscale = 1 string strAxis, strInfo, strFlags strInfo = ImageInfo("", strImage, 0) strAxis = StringByKey(SelectString(isX, "YAXIS", "XAXIS"), strInfo) strFlags = StringByKey("SETAXISFLAGS", AxisInfo(s.win, strAxis)) variable indexMin, indexMax if(GrepString(strFlags, "/")==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) // don't use IndexToScale, because ends of axis may fall outside of image v_min = DimOffset(wImage, 1-isX) + indexMin*DimDelta(wImage, 1-isX) v_max = DimOffset(wImage, 1-isX) + indexMax*DimDelta(wImage, 1-isX) if (v_min > v_max) SetAxis /R/W=$s.win $strAxis v_min, v_max else SetAxis /W=$s.win $strAxis v_min, v_max endif elseif ((sign(oldDelta)==sign(delta)) %^ GrepString(strFlags, "/R")) // switch the axis limits so that image is not flipped SetAxis /A/W=$s.win $strAxis else SetAxis /A/R/W=$s.win $strAxis endif return 0 end function PosFromAxisVal(string graphNameStr, string axNameStr, variable val) variable pixel = PixelFromAxisVal(graphNameStr, axNameStr, val) variable resolution = ScreenResolution return resolution > 96 ? pixel * 72/resolution : pixel end
![](/sites/default/files/forum.png)
Forum
![](/sites/default/files/support.png)
Support
![](/sites/default/files/gallery.png)
Gallery
Igor Pro 9
Learn More
Igor XOP Toolkit
Learn More
Igor NIDAQ Tools MX
Learn More
Edited snippet to add a menu item that I find useful, and fixed some annoyances related to changing axis values after rescale.
December 1, 2020 at 09:21 am - Permalink
The updated file ImageScaleGUI_130.zip contains additional code that allows scaling to be transferred from one displayed image to another.
February 5, 2021 at 09:25 am - Permalink
Edited snippet to tweak the logic for autoscaling axes after image setscale.
April 13, 2021 at 03:11 am - Permalink