#pragma TextEncoding = "UTF-8" #pragma rtGlobals=3 // Use modern global access method and strict wave access. //Code writen by: //Kyle Zarzana //University of Colorado Boulder //Set of functions to call ffmpeg through Igor to make movies. Requires that the computer knows where //ffmpeg is installed. Works best when the image names are of the format [Image base name]_[Image number]. //The image number should always have the same number of digits. //You CANNOT have any other files in the images folder except the images!!! (I can't image why you would). //As usual: //Variable names start with "v" //Index variable names start with "i" //String names start with "s" or "str" //Constant names will start with "k", and then follow the above rules depending on their type //Path names start with "p" //Wave names start with "w" //Text wave names start with "tw" //Wave reference wave names start with "rw" //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //You can also add ffmpeg to your system variables path, which removes the need to have the full path. Using the full path seems a little more robust. STRCONSTANT kstrffmpeg_Path_Windows="C:ffmpeg:bin:ffmpeg" //Use Igor formating here STRCONSTANT kstrffmpeg_Path_Mac=":usr:local:bin:ffmpeg" //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// MENU "Video Tools" "Make panel", Make_Video_Panel() END //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FUNCTION Make_Video_Panel() String strVideo_Window=UniqueName("Panel_Make_Video_", 9, 0) NewPanel/W=(221,54,1410,290)/N=$strVideo_Window as strVideo_Window TabControl P0_Tab_Video_Panel,pos={2.00,6.00},size={1184.00,225.00},proc=Tab_Controller_Video,tabLabel(0)="Make Video",tabLabel(1)="Change Speed",value= 0 //Tab 0 controls SetVariable T0_SetVar_Image_Source_Path,pos={10.00,35.00},size={1105.00,19.00},bodyWidth=1000,title="Images Folder Path",value= _STR:"" Button T0_Button_Image_Source_Path,pos={1120,34.00},size={52.00,20.00},proc=Button_Locate_Folder_Video,title="Browse" SetVariable T0_SetVar_Image_Name,pos={38.00,60.00},size={357.00,19.00},bodyWidth=280,title="Images Name",value= _STR:"" SetVariable T0_SetVar_Bit_Rate,pos={70.00,85.00},size={175.00,19.00},bodyWidth=130,title="Bit Rate",format="%.2W0P",limits={0,inf,0},value= _NUM:800000 SetVariable T0_SetVar_Framerate,pos={258.00,85.00},size={93.00,19.00},bodyWidth=35,title="Frame rate",value= _NUM:15 CheckBox T0_Check_Scale_Value,pos={400,87},size={45.00,16.00},title="Scale?",value= 0,proc=Check_Scale_Value SetVariable T0_SetVar_Scale_Value,pos={450,85.00},size={102.00,19.00},bodyWidth=40,title="Scale value",limits={0,inf,0},value= _NUM:800 SetVariable T0_SetVar_Video_Folder_Path,pos={17.00,110.00},size={1098.00,19.00},bodyWidth=1000,title="Video Folder Path",value= _STR:"" Button T0_Button_Video_Folder_Path,pos={1120,109.00},size={52.00,20.00},proc=Button_Locate_Folder_Video,title="Browse" SetVariable T0_SetVar_File_Name,pos={58.00,135.00},size={267.00,19.00},bodyWidth=210,title="File Name",value= _STR:"" Button T0_Button_Make_Video,pos={115.00,160.00},size={82.00,20.00},proc=Button_Make_Video,title="Make Video",fColor=(16386,65535,16385) CheckBox T0_Check_Erase_Bat,pos={202.00,162.00},size={184.00,16.00},title="Remove bat file? (Windows only)",value= 1 SetVariable T0_SetVar_Video_Command,pos={22.00,183.00},size={1093.00,19.00},bodyWidth=1000,title="Video Command",value= _STR:"" //Tab 1 controls SetVariable T1_SetVar_Video_Source_Path,pos={10.00,35.00},size={1105.00,19.00},bodyWidth=1000,title="Source Video Path",value= _STR:"",disable=1 Button T1_Button_Video_Source_Path,pos={1120,34.00},size={52.00,20.00},proc=Button_Locate_File_Video,title="Browse",disable=1 SetVariable T1_SetVar_Dest_Folder_Path,pos={10.00,60.00},size={1105.00,19.00},bodyWidth=1000,title="Dest Folder Path",value= _STR:"",disable=1 Button T1_Button_Dest_Folder_Path,pos={1120,59.00},size={52.00,20.00},proc=Button_Locate_Folder_Video,title="Browse",disable=1 SetVariable T1_SetVar_File_Name,pos={58.00,85.00},size={267.00,19.00},bodyWidth=210,title="File Name",value= _STR:"",disable=1 SetVariable T1_SetVar_Speed_Change,pos={58.00,110.00},size={97.00,19.00},bodyWidth=40,title="Speed change",value= _NUM:1,limits={-inf,inf,0},disable=1 TitleBox T1_Title_Speed_Note,pos={164.00,112.00},size={509.00,16.00},title="Enter the factor you want to SLOW the video down by (e.g. 2 means the video will be twice as long)",frame=0,disable=1 Button T1_Button_Change_Speed,pos={115.00,135.00},size={82.00,20.00},proc=Button_Change_Video_Speed,title="Make Video",fColor=(16386,65535,16385),disable=1 CheckBox T1_Check_Erase_Bat,pos={202.00,137.00},size={184.00,16.00},disable=1,title="Remove bat file? (Windows only)",value= 1 SetVariable T1_SetVar_Video_Command,pos={22.00,159.00},size={1093.00,19.00},bodyWidth=1000,disable=1,title="Video Command",value= _STR:"" END ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //Enables/disables controls based on which tab is active FUNCTION Tab_Controller_Video(ctrlName,tabNum) : TabControl String ctrlName Variable tabNum String strControls_2_Disable=ControlNameList("",";", "T*") //Makes a list of all the controls on the tabs Took out the Panel_Red_Baron in the ControlNameList command. Not sure it's needed String strControls_2_Enable=ControlNameList("",";", ("T"+num2str(tabNum)+"*")) //Makes a list of the controls on the active tab ModifyControlList strControls_2_Disable, disable=1 //Disables all the controls on all the tabs ModifyControlList strControls_2_Enable, disable=0 //Enables the controls on the active tabs IF(tabnum==0) ControlInfo T0_Check_Scale_Value Check_Scale_Value("", V_Value) ENDIF END ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //Locates files FUNCTION Button_Locate_File_Video(ctrlName) : ButtonControl String ctrlName Variable refNum String strMessage = "Select a file" String strCtrl_Name=ReplaceString("_Button_", ctrlName, "_SetVar_") //I can't imagine why _SetVar_ would be in the name twice, so this should work fine PathInfo/S home //If you've saved the experiment, start looking in the folder with the experiment Open/D/R/T="????"/M=strMessage refNum //The "????" means that Igor will not be looking for a particular file type IF(strlen(S_fileName)>0) SetVariable $strCtrl_Name, value=_STR:S_fileName ENDIF KillPath/Z pPath END ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //Function for finding folders FUNCTION Button_Locate_Folder_Video(ctrlName) : ButtonControl String ctrlName String strCtrl_Name=ReplaceString("_Button_", ctrlName, "_SetVar_") PathInfo/S home //If you've saved the experiment, start looking in the folder with the experiment String strMessage="Select a folder" NewPath/Q/O/M=strMessage pTemp_Path //Gets the path to a FOLDER (not a file) PathInfo pTemp_path IF(strlen(S_Path)>0) SetVariable $strCtrl_Name, value=_STR:S_Path //Put the string in the setvariable with the same name as the button IF(cmpstr(ctrlName, "T0_Button_Image_Source_Path")==0) //If the button was for the path to the images, do some extra work to get the image names String strFile_Full_Name=IndexedFile(pTemp_path, 0, "????") String strFile_Type=ParseFilePath(0, strFile_Full_Name, ".", 1, 0) //Get the file extension (last item in a period separated path) String strFile=ParseFilePath(1, strFile_Full_Name, ".", 1, 0) //Remove the file extension strFile=strFile[0,strlen(strFile)-2] //Remove the period at the end String strFile_Number=StringFromList(ItemsInList(strFile, "_")-1, strFile, "_") //Grabs the last item in the underscore separated list String strFile_Name_Start=strFile[0,strlen(strFile)-2-strlen(strFile_Number)] String strFormat_String="%0"+num2str(strlen(strFile_Number))+"d" //Gets the format string for the file number (Is there a better way to do this?) String strFile_Name_Template=strFile_Name_Start+"_"+strFormat_String+"."+strFile_Type SetVariable T0_SetVar_Image_Name, value=_STR:strFile_Name_Template SetVariable T0_SetVar_File_Name, value=_STR:strFile_Name_Start+".mp4" ENDIF ENDIF KillPath/Z pTemp_path END ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //Enables/disables the scale set variable FUNCTION Check_Scale_Value(ctrlName,checked) : CheckBoxControl String ctrlName Variable checked SetVariable T0_SetVar_Scale_Value,disable=SelectNumber(checked, 2, 0) END ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //Creates the command to make the video and executes a batch file with that command (Windows) or runs the Applescript (Mac) FUNCTION Button_Make_Video(ctrlName) : ButtonControl String ctrlName String strVideo_Window_List=WinList("Panel_Make_Video_*", ";", "WIN:64") //Gets the name of the top panel String strVideo_Window=StringFromList(0,strVideo_Window_List) Variable vIs_Windows=StringMatch(IgorInfo(2), "*Win*") //Checks the OS String strPath_Separator=SelectString(vIs_Windows, "/", "\\") String strffmpeg_path_format=Create_Path_Video(SelectString(vIs_Windows, kstrffmpeg_Path_Mac, kstrffmpeg_Path_Windows), strPath_Separator) //Creates the ffmpeg path ControlInfo/W=$strVideo_Window T0_SetVar_Video_Folder_Path String strVideo_Folder_Path=S_Value IF(strlen(strVideo_Folder_Path)==0) DoAlert/T="WARNING" 0, "Select a folder for the video!" Return -1 ENDIF ControlInfo/W=$strVideo_Window T0_SetVar_Image_Source_Path String strImage_Source_Path=S_Value String strImage_Source_Path_Format=Create_Path_Video(SelectString(vIs_Windows, ":"+RemoveListItem(0,strImage_Source_Path,":"),strImage_Source_Path), strPath_Separator) ControlInfo/W=$strVideo_Window T0_SetVar_Image_Name String strImage_Name=S_Value String strImage_Name_Format=SelectString(vIs_Windows, strImage_Name, ReplaceString("%", strImage_Name, "%%%%")) //Batch file needs double % to run, and need double % to print a single %. ControlInfo/W=$strVideo_Window T0_SetVar_Bit_Rate Variable vBit_Rate=V_Value String strBit_Rate sprintf strBit_Rate, "%W0P", vBit_Rate ControlInfo/W=$strVideo_Window T0_SetVar_Framerate String strFramerate=num2str(V_Value) String strScale_Value="" ControlInfo/W=$strVideo_Window T0_Check_Scale_Value IF(V_Value) ControlInfo/W=$strVideo_Window T0_SetVar_Scale_Value strScale_Value="-vf scale="+S_Value+":-1 " ENDIF ControlInfo/W=$strVideo_Window T0_SetVar_File_Name String strVideo_File_Name=S_Value String strVideo_File_Name_No_Extension=StringFromList(0, strVideo_File_Name, ".") Variable vTime=DateTime String strDateTime=ReplaceString("-", Secs2Date(vTime, -2), "")+"_"+ReplaceString(":", Secs2Time(vTime, 3), "") String strVideo_Folder_Path_Format=Create_Path_Video(SelectString(vIs_Windows, ":"+RemoveListItem(0,strVideo_Folder_Path,":"),strVideo_Folder_Path), strPath_Separator) String strVideo_File_Path=strVideo_Folder_Path_Format+strVideo_File_Name GetFileFolderInfo/Q/Z strVideo_Folder_Path+strVideo_File_Name IF(V_IsFile) DoAlert/T="WARNING" 0, "Video file already exists! Deal with this before running ffmpeg!" Return -1 ENDIF //libx264 encoding has worked well so far. I think libx265 is more efficient, but I have other things to do String strBatch_Command=strffmpeg_path_format+" -framerate "+strFramerate+" -i "+strImage_Source_Path_Format+strImage_Name_Format+" -c:v libx264 -b:v "+strBit_Rate+" "+strVideo_File_Path String strTerminal_Command=strffmpeg_path_format+" -framerate "+strFramerate+" -i "+strImage_Source_Path_Format+strImage_Name+" -c:v libx264 -b:v "+strBit_Rate+" "+strVideo_File_Path SetVariable T0_SetVar_Video_Command, value=_STR:strTerminal_Command IF(vIs_Windows) //use full paths for everything so I don't have to use cd String strBatch_Path=strVideo_Folder_Path+strVideo_File_Name_No_Extension+"_"+strDateTime+"_Batch.bat" String strBatch_Path_Format=Create_Path_Video(strBatch_Path, strPath_Separator) Variable vFileID_Batch Open vFileID_Batch as strBatch_Path fprintf vFileID_Batch, strBatch_Command //Write the new string to the batch file Close vFileID_Batch ExecuteScriptText strBatch_Path_Format ControlInfo/W=$strVideo_Window T0_Check_Erase_Bat //Kill the batch file if the user wants IF(V_Value) DeleteFile/Z strBatch_Path ENDIF ELSE //Mac code String Script_Command="do shell script \""+strffmpeg_path_format+" -framerate "+strFramerate+" -i "+strImage_Source_Path_Format+strImage_Name_Format+" -c:v libx264 -b:v "+strBit_Rate+" "+strScale_Value+strVideo_File_Path+"\"" ExecuteScriptText Script_Command ENDIF END ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///Creates the command to change the video speed and executes a batch file with that command (Windows) or runs the Applescript (Mac) FUNCTION Button_Change_Video_Speed(ctrlName) : ButtonControl String ctrlName String strVideo_Window_List=WinList("Panel_Make_Video_*", ";", "WIN:64") String strVideo_Window=StringFromList(0,strVideo_Window_List) Variable vIs_Windows=StringMatch(IgorInfo(2), "*Win*") //Checks the OS String strPath_Separator=SelectString(vIs_Windows, "/", "\\") String strffmpeg_path_format=Create_Path_Video(SelectString(vIs_Windows, kstrffmpeg_Path_Mac, kstrffmpeg_Path_Windows), strPath_Separator) ControlInfo/W=$strVideo_Window T1_SetVar_Video_Source_Path String strSource_Path=S_Value String strSource_Path_Format=Create_Path_Video(SelectString(vIs_Windows, ":"+RemoveListItem(0,strSource_Path,":"), strSource_Path), strPath_Separator) String strSource_Extension=StringFromList(1, ParseFilePath(0, strSource_Path, ":", 1, 0), ".") ControlInfo/W=$strVideo_Window T1_SetVar_Dest_Folder_Path String strDest_Folder_Path=S_Value String strDest_Folder_Path_Format=Create_Path_Video(SelectString(vIs_Windows, ":"+RemoveListItem(0,strDest_Folder_Path,":"), strDest_Folder_Path), strPath_Separator) ControlInfo/W=$strVideo_Window T1_SetVar_File_Name String strFile_Name=S_Value String strFile_Name_No_Extension=StringFromList(0, strFile_Name, ".") IF(strlen(StringFromList(1, strFile_Name, "."))==0) //Make sure the user added an extension to the new file name strFile_Name=strFile_Name+"."+strSource_Extension SetVariable T1_SetVar_File_Name, value=_STR:strFile_Name ENDIF ControlInfo/W=$strVideo_Window T1_SetVar_Speed_Change Variable vPTS=V_Value String strPTS sprintf strPTS, "%0.04f", vPTS Variable vTime=DateTime String strDateTime=ReplaceString("-", Secs2Date(vTime, -2), "")+"_"+ReplaceString(":", Secs2Time(vTime, 3), "") GetFileFolderInfo/Q/Z strDest_Folder_Path+strFile_Name IF(V_IsFile) DoAlert/T="WARNING" 0, "Video file already exists! Deal with this before running ffmpeg!" Return -1 ENDIF String strBatch_Command=strffmpeg_path_format+" -i "+strSource_Path_Format+" -filter:v \"setpts="+strPTS+"*PTS\" "+strDest_Folder_Path_Format+strFile_Name SetVariable T1_SetVar_Video_Command, value=_STR:strBatch_Command IF(vIs_Windows) String strBatch_Path=strDest_Folder_Path+strFile_Name_No_Extension+"_"+strDateTime+"_Batch.bat" String strBatch_Path_Format=Create_Path_Video(strBatch_Path, strPath_Separator) Variable vFileID_Batch Open vFileID_Batch as strBatch_Path fprintf vFileID_Batch, strBatch_Command //Write the new string to the batch file Close vFileID_Batch ExecuteScriptText strBatch_Path_Format ControlInfo/W=$strVideo_Window T0_Check_Erase_Bat //Kill the batch file if the user wants IF(V_Value) DeleteFile/Z strBatch_Path ENDIF ELSE //Mac code String Script_Command="do shell script \""+strffmpeg_path_format+" -i "+strSource_Path_Format+" -filter:v setpts="+strPTS+"*PTS "+strDest_Folder_Path_Format+strFile_Name+"\"" ExecuteScriptText Script_Command ENDIF END ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //Takes a path with Igor path separators (colons) and returns a path with the proper separators. I can probably replace most of this with //ParseFilePath(5), but I'm feeling lazy right now and don't want to take the time to rewrite and test the code. FUNCTION/S Create_Path_Video(strOld_path, strSeparator) String strOld_Path, strSeparator //The bat file needs \ as the path separator, so you need to use \\ to get this to print properly in the string. The other files store the paths as string, so they need \\\\ (which will printed as \\ and then read as \) String strOS=IgorInfo(2) //Figures out whether you are using Mac OS or Windows. String strNew_Path String strQuotes="" IF(stringmatch(strOS, "*Win*")) //If the first character after OS: is W, then you are running Windows Variable vDrive_End=strSearch(strOld_Path, ":", 0, 2) //PC paths on Igor are C:Users:etc, so I first replace all the colons AFTER the first one with the separator string String strPath_minus_Drive=strOld_path[vDrive_End+1,strlen(strOld_path)] String strWin_Path_minus_Drive=ReplaceString(":", strPath_minus_Drive, strSeparator) //Replace all the colons IF(cmpstr(strSeparator, "\\")==0) //The batch file uses single slashes as the path separator, and if there are spaces in the path to the jsp and the log file, then you need quotes around that whole path in the batch file IF(StringMatch(strWin_Path_minus_Drive, "* *")) strQuotes="\"" ENDIF ENDIF strNew_Path=strQuotes+strOld_path[0,vDrive_End-1]+":"+strSeparator+strWin_Path_minus_Drive+strQuotes //Append the drive to the front of the string ELSE //Mac code strNew_Path=strQuotes+ReplaceString(":", strOld_Path, strSeparator)+strQuotes ENDIF Return strNew_Path END ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////