function RemoveFramesFunction(movName,removeCornersFlag,removeEdgesFlag) % RemoveFramesFunction_v3_flycap loads the movie, plays it so the user can find the frames to keep, % and crops the rest of the frames. % % WARNING: the original movie is overwritten! Please keep all originals in a separate place. % %%%%%%% Arguments: % movName [string]: name of movie to crop % removeCornersFlag ['y' or 'n']; % 'y' means ignore corners of frames (connected components in frames that have a pixel in a corner). % 'n' means analyze frames "as is". % removeCornersFlag ['y' or 'n']; % 'y' means ignore corners of frames (connected components in frames that have a pixel in a corner). % 'n' means analyze frames "as is". % removeEdgesFlag ['y' or 'n']; % 'y' means remove all black pixels connected to edges (connected components in frames that have a pixel within % 3 pixels of the edge). % 'n' means analyze frames "as is". %%%%%% Instructions: % The movie is "shown" in 25 frame blocks. Record the FIRST frame to keep and the LAST frame to keep. 0 is ignored. % For example: if right side does not need to be cut, setting the LAST frame to keep to 0 (or any number greater % than numFrames) means that the right side will not get cut. Similarly for the left side. % You will get a chance to see the movie as many times as you want to confirm. After choosing the % first and last frame, the movie will be cropped appropriately and overwrite the existing file. % WARNING AGAIN: IT IS IMPORTANT TO KEEP THE ORIGINAL MOVIE IN A DIFFERENT FOLDER AS THE MOVIE IS OVERWRITTEN! workingDir = tempname; mkdir(workingDir); mkdir(workingDir,'images'); close % Load Original Movie And Create Sorted Images From Frames movOriginal = VideoReader(movName); numFrames = movOriginal.NumberOfFrames; %#ok<*VIDREAD> movHeight = movOriginal.Height; movWidth = movOriginal.Width; for i = 1:numFrames img = read(movOriginal,i); img = img(:,:,2); % take only green channel (camera was set to process in "green-scale") imwrite(img,fullfile(workingDir,'images',sprintf('img%d.jpg',i))); end imageNames = dir(fullfile(workingDir,'images','*.jpg')); imageNames = {imageNames.name}'; imageStrings = regexp([imageNames{:}],'(\d*)','match'); imageNumbers = str2double(imageStrings); [~,sortedIndices] = sort(imageNumbers); sortedImageNames = imageNames(sortedIndices); for n = 1:ceil(numFrames/25) close figure('units','normalized','outerposition',[0 0 1 1]) for i = 1+25*(n-1):min(25*n,numFrames) subplot(5,5,mod(i-1,25)+1) img = imread(fullfile(workingDir,'images',sortedImageNames{i})); imshow(img) title(['Frame = ',num2str(i)]) if mod(i+1,25) == 1 pause end imshow(img) title(['Frame = ',num2str(i)]) end end % Ask for frame boundaries % cut before left frame, after right frame, 0 gets ignored - e.g. if you want to crop before a certain frame only) movReplayAnswer = 'y'; leftFrameCut = 0; % defaults - movie unchanged rightFrameCut = 0; % defaults - movie unchanged framesToRemoveLeft = []; framesToRemoveRight = []; while not(isequal(movReplayAnswer,'n')) movieReplayPrompt = 'Show movie again? (y/n)'; movReplayAnswer = inputdlg(movieReplayPrompt,'Movie Replay?'); if isequal(movReplayAnswer,{}) % cancel is pressed break else movReplayAnswer = movReplayAnswer{1}; % take entry end if isequal(movReplayAnswer,'y') close figure('units','normalized','outerposition',[0 0 1 1]) for n = 1:ceil(numFrames/25) close figure for i = 1+25*(n-1):min(25*n,numFrames) subplot(5,5,mod(i-1,25)+1) img = imread(fullfile(workingDir,'images',sortedImageNames{i})); imshow(img) title(['Frame = ',num2str(i)]) if mod(i+1,25) == 1 pause end imshow(img) title(['Frame = ',num2str(i)]) end end elseif isequal(movReplayAnswer,'n') dlgTitle = 'Input first and last frames to keep. Use 0 to ignore that side.'; promptLeftCut = 'First frame to keep (0 to ignore):'; promptRightCut = 'Last frame to keep (0 to ignore):'; num_lines = 1; inputPrompts = {promptLeftCut,promptRightCut}; dlgDefaults = {'0','0'}; frameCells = inputdlg(inputPrompts,dlgTitle,num_lines,dlgDefaults); if not(isequal(frameCells,{})) % cancel not pressed leftFrameCut = str2num(frameCells{1}); %#ok<*ST2NM> rightFrameCut = str2num(frameCells{2}); end else display('Unrecognized Key') end end if leftFrameCut ~= 0 && not(isempty(leftFrameCut)) leftFrameCut = round(leftFrameCut); framesToRemoveLeft = 1:leftFrameCut-1; end if rightFrameCut ~= 0 && not(isempty(rightFrameCut)) framesToRemoveRight = rightFrameCut+1:numFrames; end framesToRemove = union(framesToRemoveLeft,framesToRemoveRight); % Crop Movie for i = framesToRemove delete(fullfile(workingDir,'images',sortedImageNames{i})) % BE CAREFUL, AS DELETE DOES NOT GIVE ANY WARNINGS WHEN REMOVING FILES end imageNames = dir(fullfile(workingDir,'images','*.jpg')); imageNames = {imageNames.name}'; imageStrings = regexp([imageNames{:}],'(\d*)','match'); imageNumbers = str2double(imageStrings); [~,sortedIndices] = sort(imageNumbers); sortedImageNames = imageNames(sortedIndices); numFrames = length(sortedImageNames); if isequal(removeCornersFlag,'y') cornerIdx = [sub2ind([movHeight movWidth],1:7,1:7),... sub2ind([movHeight movWidth],movHeight:-1:(movHeight-6),1:7),... sub2ind([movHeight movWidth],1:7,movWidth:-1:(movWidth-6)),... sub2ind([movHeight movWidth],movHeight:-1:(movHeight-6),movWidth:-1:(movWidth-6))]; for i = 1:numFrames img = imread(fullfile(workingDir,'images',sortedImageNames{i})); imgForCornerRemoval = im2bw(img,.3); % hard code low threshold to really get corners connectedComponentsForCornerRemoval = bwconncomp(~imgForCornerRemoval,4); connectedComponentsForCornerRemoval = connectedComponentsForCornerRemoval.PixelIdxList; for j = 1:length(connectedComponentsForCornerRemoval) if not(isempty(intersect(connectedComponentsForCornerRemoval{j},cornerIdx))) img(connectedComponentsForCornerRemoval{j}) = 256; % set to be WHITE in original image (since fly is black) % remember: white is 256 (this is a greyscale image) imshow(img) end end imwrite(img,fullfile(workingDir,'images',sprintf('img%d.jpg',i))); end imageNames = dir(fullfile(workingDir,'images','*.jpg')); imageNames = {imageNames.name}'; imageStrings = regexp([imageNames{:}],'(\d*)','match'); imageNumbers = str2double(imageStrings); [~,sortedIndices] = sort(imageNumbers); sortedImageNames = imageNames(sortedIndices); numFrames = length(sortedImageNames); end if isequal(removeEdgesFlag,'y') edgeIdx = [sub2ind([movHeight movWidth],1:movHeight,1*ones(1,movHeight)),... sub2ind([movHeight movWidth],1:movHeight,movWidth*ones(1,movHeight)),... sub2ind([movHeight movWidth],movHeight*ones(1,movWidth),1:movWidth),... sub2ind([movHeight movWidth],1*ones(1,movWidth),1:movWidth)]; for i = 1:numFrames img = imread(fullfile(workingDir,'images',sortedImageNames{i})); imgForEdgeRemoval = im2bw(img,.6); % hard code reasonably high threshold to really get all dark pixels % that form edges. NOTE: this will ALSO remove the fly if even a leg touches an edge % pixel or anything close to that - it will be visible in the % frames to remove. connectedComponentsForEdgeRemoval= bwconncomp(~imgForEdgeRemoval,4); connectedComponentsForEdgeRemoval = connectedComponentsForEdgeRemoval.PixelIdxList; for j = 1:length(connectedComponentsForEdgeRemoval) if not(isempty(intersect(connectedComponentsForEdgeRemoval{j},edgeIdx))) img(connectedComponentsForEdgeRemoval{j}) = 256; imshow(img) end end imwrite(img,fullfile(workingDir,'images',sprintf('img%d.jpg',i))); end imageNames = dir(fullfile(workingDir,'images','*.jpg')); imageNames = {imageNames.name}'; imageStrings = regexp([imageNames{:}],'(\d*)','match'); imageNumbers = str2double(imageStrings); [~,sortedIndices] = sort(imageNumbers); sortedImageNames = imageNames(sortedIndices); numFrames = length(sortedImageNames); end outputCroppedMovie = VideoWriter(movName); outputCroppedMovie.FrameRate = movOriginal.FrameRate; open(outputCroppedMovie); for i = 1:numFrames img = imread(fullfile(workingDir,'images',sortedImageNames{i})); writeVideo(outputCroppedMovie,img); end close(outputCroppedMovie); end