//============================================================= // Name: EyelicTracker // Authors: // Payne Chang (paynechang@gmail.com) // Rick Gray (rick@mail.clm.utexas.edu) // Randy Chitwood (randy@mail.clm.utexas.edu) // Dr. Johnston's Lab @2012 //============================================================= // Use modern global access method. #pragma rtGlobals = 1 //============================================================= // EyelidTracker() Macro EyelidTracker() //================================ // Notification Print "=== EyelicTracker ===" //================================ // Global flags Variable /G flagThresholding = 0 //================================ // Global variables // Video Variable /G numFrames = 350 Variable /G framePerSecond = 350 Variable /G frameIndex = 0 // ROI Variable /G roiLeft, roiRight , roiTop, roiBottom, roiWidth, roiHeight // Image processing Variable /G threshold = 128 Variable /G pixelCount Variable /G lineCount Variable /G upperEyelid = 0 Variable /G lowerEyelid = 1 //================================ // Create control panel DoWindow /K ControlPanel execute "ControlPanel()" End Macro //============================================================= // Control Panel Window ControlPanel() : Panel //================================= // Suppress macro output Silent 1 //================================= // Parameters of control panel Variable pnlX = 700 Variable pnlY = 10 Variable pnlWidth = 300 Variable pnlHeight = 350 NewPanel /W=(pnlX, pnlY, pnlX + pnlWidth, pnlY + pnlHeight) SetDrawLayer UserBack SetDrawEnv fillfgc = (65535 * 0.6, 65535 * 1, 65535 * 0.6) DrawRect 2, 2, pnlWidth - 4, pnlHeight - 4 //================================== // Parameters of widgets Variable itemWidth = 150 Variable itemHeight = 20 Variable itemX = 10 Variable itemY = 10 Variable itemYGap = 5 //================================== // Open video file Button btnOpenVideoFile, pos={itemX, itemY}, size={itemWidth, itemHeight} Button btnOpenVideoFile, title="Open Video File", proc=OpenVideoFileFunc // Select ROI itemY += (itemHeight + itemYGap) Button btnSelectROI, pos={itemX, itemY}, size={itemWidth, itemHeight} Button btnSelectROI, title="Select ROI", proc=SelectROIFunc // Analyze Video itemY += (itemHeight + itemYGap) Button btnAnalyzeVideo, pos={itemX, itemY}, size={itemWidth, itemHeight} Button btnAnalyzeVideo, title="Analyze Video", proc=AnalyzeVideoFunc // Exit itemY += (itemHeight + itemYGap) Button btnExit, pos={itemX, itemY}, size={itemWidth, itemHeight} Button btnExit, title="Exit", proc=ExitFunc //===================================== itemY += (itemHeight + itemYGap * 2) DrawLine 2, itemY, pnlWidth - 3, itemY // SetVariable for Number of Frames itemY += (itemYGap * 2) SetVariable svbNumFrames, pos={itemX, itemY}, size={itemWidth, itemHeight} SetVariable svbNumFrames, title="Number of Frames", value=numFrames // SetVariable for Frames per Second itemY += (itemHeight + itemYGap) SetVariable svbFPS, pos={itemX, itemY}, size={itemWidth, itemHeight} SetVariable svbFPS, title="Frames per Second", value=framePerSecond // SetVariable for Frame Index itemY += (itemHeight + itemYGap) SetVariable svbFrameIndex, pos={itemX, itemY}, size={itemWidth, itemHeight} SetVariable svbFrameIndex, title="Frame Index", proc=ImageProcessingFuncSVB SetVariable svbFrameIndex, value=frameIndex // Button: Go to Beginning itemY += (itemHeight + itemYGap) Button btnGoToBeginning, pos={itemX, itemY}, size={itemWidth, itemHeight} Button btnGoToBeginning, title="Go to Beginning", proc=ImageProcessingFuncBTN1 //===================================== itemY += (itemHeight + itemYGap * 2) DrawLine 2, itemY, pnlWidth - 3, itemY // Check Box for Thresholding itemY += (itemYGap * 2) CheckBox ckbThresholding, pos={itemX, itemY}, size={itemWidth, itemHeight} CheckBox ckbThresholding, title="Thresholding", proc=ImageProcessingFuncCKB CheckBox ckbThresholding, variable=flagThresholding // Slider for Threshold itemY += (itemHeight + itemYGap) Slider sldThreshold, pos={itemX, itemY}, size={280, itemHeight} Slider sldThreshold, title="Threshold", proc=ImageProcessingFuncSLD Slider sldThreshold, limits={0, 255, 1}, vert=0, ticks= 25, variable=threshold // SetVariable for Threshold itemY += (itemHeight + itemYGap) + 30 SetVariable svbThreshold, pos={itemX, itemY}, size={itemWidth, itemHeight} SetVariable svbThreshold, title="Threshold", proc=ImageProcessingFuncSVB SetVariable svbThreshold, value=threshold End //============================================================= // Function for Open Video File button Function OpenVideoFileFunc(ctrlName) : ButtonControl String ctrlName //============================================= // Notification Print "=== Video Information from File ===" //============================================= // Kills the old QuickTime window PlayMovieAction /Z kill //============================================= // Open one video file PlayMovie //============================================= // Get basic information // The information in the video file is not accurate. // Manual input is required. // Start time PlayMovieAction stop, gotoBeginning, getTime Variable videoTimeStart = V_value // Video delta time PlayMovieAction step = 1, getTime Variable videoDeltaTime = V_value - videoTimeStart Print "Delta Time =", videoDeltaTime * 1e3, "(ms)" Print "Acquisition Rate =", 1/videoDeltaTime, "(fps)" // End time PlayMovieAction gotoEnd, getTime Variable videoTimeEnd = V_value // Calculated Number of frames // Igor/QuickTime does not check out of range error. Variable videoNumFrames = videoTimeEnd / videoDeltaTime + 1 Print "Number of Frames =", videoNumFrames //============================================= // Extract the first frame // The current frame is extracted into an 8-bit RGB image wave named M_MovieFrame PlayMovieAction gotoBeginning, extract // Convert the image into 8-bit grayscale image wave (M_RGB2Gray) ImageTransform rgb2gray M_MovieFrame //============================================= // Create grayscale window DoWindow /K GrayscaleWindow NewImage /N=GrayscaleWindow M_RGB2Gray End //============================================================= // Select ROI from the marquee in GrayscaleWindow Function SelectROIFunc(ctrlName) : ButtonControl String ctrlName // Notification Print "=== Select ROI ===" // Global variables NVAR numFrames = root:numFrames // Extract coordinates of the marquee GetMarquee /W=GrayscaleWindow left, top Printf "Marquee (left, right, top, bottom) = (%g, %g, %g, %g)\r", V_left, V_right, V_top, V_bottom // Global ROI variables NVAR roiLeft = root:roiLeft; roiLeft = ceil(V_left) NVAR roiRight = root:roiRight; roiRight = floor(V_right) NVAR roiTop = root:roiTop; roiTop = ceil(V_top) NVAR roiBottom = root:roiBottom; roiBottom= floor(V_bottom) NVAR roiWidth = root:roiWidth; roiWidth = roiRight - roiLeft + 1 NVAR roiHeight = root:roiHeight; roiHeight = roiBottom - roiTop + 1 Printf "ROI (left, right, top, bottom) = (%d, %d, %d, %d)\r", roiLeft, roiRight, roiTop, roiBottom Printf "ROI (width, height) = (%d, %d)\r", roiWidth, roiHeight // Create wave roiImage Duplicate /O /R=(roiLeft, roiRight)(roiTop, roiBottom) M_RGB2Gray, roiImage // Create ROIWindow and display roiImage DoWindow /K ROIWindow NewImage /N=ROIWindow roiImage // Create wave to store line scan data Make /O /N=(roiHeight) wLineScan = 0 // Waves Make /N=(numFrames) /O wPixelCount=0 Make /N=(numFrames) /O wLineCount=0 Make /N=(numFrames, roiHeight) /O wLineScan2D=0 Make /N=(numFrames) /O wUpperEyelid=0 Make /N=(numFrames) /O wLowerEyelid=0 Make /N=(numFrames) /O wUpperEyelidRaw=0 Make /N=(numFrames) /O wLowerEyelidRaw=0 // Create LineScanWindow DoWindow /K LineScanWindow Display /N=LineScanWindow /W=(10, 300, 210, 450) wLineScan Label /W=LineScanWindow left "Normalzied Line Integration" Label /W=LineScanWindow bottom "Y Coordinate (Pixel)" SetAxis /W=LineScanWindow left -0.01, 1 ModifyGraph /W=LineScanWindow lsize=1.5 // Process image ImageProcessingFunc() //============================================= // Create TraceWindow DoWindow /K TraceWindow Display /N=TraceWindow /W=(450, 300, 750, 500) wPixelCount, wLineCount ModifyGraph rgb(wLineCount)=(0, 0, 65535) Label /W=TraceWindow left "Fraction of Eyelid Closure (FEC)" Label /W=TraceWindow bottom "Frame Index" SetAxis /W=TraceWindow left -0.01, 1 ModifyGraph /W=TraceWindow lsize=1.5 //============================================= // Create EyelidWindow DoWindow /K EyelidWindow Display /N=EyelidWindow /W=(450, 500, 750,700) wUpperEyelid, wLowerEyelid ModifyGraph rgb(wUpperEyelid)=(65535, 0, 0) ModifyGraph rgb(wLowerEyelid)=(16384,48896,65280) Label /W=EyelidWindow left "Fractioin of Eyelid Closure (FEC)" Label /W=EyelidWindow bottom "Frame Index" SetAxis /W=EyelidWindow left -0.01, 1 SetAxis /W=EyelidWindow bottom 0, numFrames ModifyGraph /W=EyelidWindow lsize=1.5 //============================================= // Create LineScan2DWindow DoWindow /K LineScan2DWindow NewImage /N=LineScan2DWindow wLineScan2D AppendToGraph /W=LineScan2DWindow wUpperEyelidRaw, wLowerEyelidRaw ModifyGraph rgb(wUpperEyelidRaw)=(65535, 0, 0) ModifyGraph rgb(wLowerEyelidRaw)=(16384,48896,65280) Label /W=LineScan2DWindow left "Y Coordinate (pixel)" Label /W=LineScan2DWindow top "Frame Index" ModifyGraph /W=LineScan2DWindow margin(left)=20, margin(top)=20 ModifyGraph /W=LineScan2DWindow lsize=1.5 End //============================================================= // AnalyzeVideoFunc Function AnalyzeVideoFunc(ctrlName) : ButtonControl String ctrlName //============================================= // Notification Print "=== Analyze Video ===" //============================================= // Global variable NVAR numFrames = root:numFrames NVAR frameIndex = root:frameIndex NVAR numVideos = root:numVideos NVAR pixelCount = root:pixelCount NVAR lineCount = root:lineCount WAVE wPixelCount = root:wPixelCount WAVE wLineCount = root:wLineCount WAVE wLineScan2D = root:wLineScan2D WAVE wUpperEyelid = root:wUpperEyelid WAVE wLowerEyelid = root:wLowerEyelid WAVE wUpperEyelidRaw = root:wUpperEyelidRaw WAVE wLowerEyelidRaw = root:wLowerEyelidRaw NVAR upperEyelid = root:upperEyelid NVAR lowerEyelid = root:lowerEyelid // Reset variables wPixelCount = 0 wLineCount = 0 wLineScan2D = 0 wUpperEyelid = 0 wLowerEyelid = 0 wUpperEyelidRaw = 0 wLowerEyelidRaw = 0 //============================================= // Get baseline frameIndex = 0 ImageProcessingFunc() Variable areaCountBaseline = pixelCount Variable pixelCountBaseline = lineCount Variable upperEyelid0 = upperEyelid Variable lowerEyelid0 = lowerEyelid //============================================= // Go through each frame for(frameIndex = 0; frameIndex < numFrames; frameIndex += 1) // Image processing ImageProcessingFunc() // Save eyelid position index wPixelCount[frameIndex] = (areaCountBaseline - pixelCount) / areaCountBaseline wLineCount[frameIndex] = (pixelCountBaseline - lineCount) / pixelCountBaseline wUpperEyelidRaw[frameIndex] = upperEyelid wLowerEyelidRaw[frameIndex] = lowerEyelid wUpperEyelid[frameIndex] = (upperEyelid - upperEyelid0) / (lowerEyelid0 - upperEyelid0) wLowerEyelid[frameIndex] = (lowerEyelid0 - lowerEyelid) / (lowerEyelid0 - upperEyelid0) // Update graphs DoUpdate endfor //============================================= frameIndex = numFrames - 1 End //============================================================= // Exit function Function ExitFunc(ctrlName) : ButtonControl String ctrlName // Kill control panel DoWindow /K ControlPanel // Kill QuickTime window PlayMovieAction /Z kill // Kill GrayscaleWindow DoWindow /K GrayscaleWindow // Kill AnalysisWindow DoWindow /K ROIWindow // Kill ResultWindow DoWindow /K ResultWindow // Kill TraceWindow DoWindow /K TraceWindow // Kill LineScanWindow DoWindow /K LineScanWindow // Kill LineScan2DWindow DoWindow /K LineScan2DWindow // Kill EyelidWindow DoWindow /K EyelidWindow End //============================================================= // Image Processing Function // CheckBox Function ImageProcessingFuncCKB(ctrlName, checked) : CheckBoxControl String ctrlName Variable checked ImageProcessingFunc() End // Slider Function ImageProcessingFuncSLD(name, value, event) :SliderControl String name Variable value Variable event ImageProcessingFunc() End // SetVariable Function ImageProcessingFuncSVB(ctrlName, varNum, varStr, varName) :SetVariableControl String ctrlName Variable varNum String varStr String varName ImageProcessingFunc() End // Button Function ImageProcessingFuncBTN1(ctrlName) : ButtonControl String ctrlName NVAR frameIndex frameIndex = 0 ImageProcessingFunc() End //========================== // Image Processing Function Function ImageProcessingFunc() //========================== // Global variables NVAR frameIndex = root:frameIndex NVAR numFrames = root:numFrames NVAR roiWidth = root:roiWidth NVAR roiHeight = root:roiHeight NVAR roiLeft = root:roiLeft NVAR roiTop = root:roiTop NVAR threshold = root:threshold NVAR flagThresholding = root:flagThresholding WAVE roiImage, wLineScan, wLineScan2D WAVE M_MovieFrame, M_RGB2Gray NVAR upperEyelid = root:upperEyelid NVAR lowerEyelid = root:lowerEyelid //========================== // Local variables Variable x, y, value //========================== // Extract one frame if(frameIndex < 0) frameIndex = 0 endif if(frameIndex >= numFrames) frameIndex = numFrames - 1 endif PlayMovieAction frame = frameIndex, extract //========================== // Convert the image into grayscale image wave ImageTransform rgb2gray M_MovieFrame //========================== // Go through each pixel NVAR pixelCount = root:pixelCount NVAR lineCount = root:lineCount Variable pixelCountTmp // Reset area count pixelCount = 0 lineCount = 0 //=================================== for(y = 0; y < roiHeight; y += 1) // Reset pixel count pixelCountTmp = 0 //=================================== // Go through each pixel along one horizontal line for(x = 0; x < roiWidth; x += 1) // Extract pixel value value = M_RGB2Gray[roiLeft + x][roiTop + y] // Thresholding if(flagThresholding) if(value > threshold) roiImage[x][y] = 255 else roiImage[x][y] = 0 // Count pixelCount += 1 endif else roiImage[x][y] = value // Count pixelCount += (255 - value) endif // Integration along one horizontal line pixelCountTmp += roiImage[x][y] endfor //=================================== // Save integration data wLineScan[y] = pixelCountTmp / 255.0 / roiWidth wLineScan2D[frameIndex][y] = wLineScan[y] if(wLineScan[y] < 0.5) lineCount += 1 endif endfor //=================================== // upperEyelid and lowerEyelid WaveStats /Q wLineScan if(V_min < 0.5) // upperEyelid for(y = 0; y < roiHeight; y +=1) if(wLineScan[y] < 0.5) upperEyelid = y break endif endfor // lowerEyelid for(y = roiHeight - 1; y >= 0; y -= 1) if(wLineScan[y] < 0.5) lowerEyelid = y break endif endfor endif End //=============================================// // End of procedure