Skip to main content

image processing - Extract timestamp of specific frames in video


I would like to do the following : I have a set of slides and a video in which those slides are discussed. Now I would like to extract the timestamp when the slide appears in the video. I would suggest to proceed in the following way:




  1. Import the video Import[] (How can I import a mp4.file ?)




  2. Import the slides as images





  3. For all frames in the video, I define the following function: I compare a certain amount of pixels of the frame to all the slides images. When a certain percentage of pixels are the same, I return the slide image and the corresponding timestamp.




My questions:



  1. How can I extract all timestamps for a video frames ?

  2. How can I extract the values of pixels within a certain geometric region of the frame.


In my video, a person might stand in fron of the slides, therefor I cannot simply say that I detect a slide in the video, if all pixels of the frame and the slide are the same.



Thanks.


Here is some material to try:


Instead of a video, one can use a gif:


enter image description here


One can try to extract the timestamp of this frame:


enter image description here


And if someone is steading in front (please excuse my drawing skills):


enter image description here



Please try with the following material: YOu can find the frames and the slides to match. Aim: Find position of the slides within the frame list.



https://www.dropbox.com/sh/l1deic1ris2il6w/AAAOU_ICZM_f0T-9M9kfNNeRa?dl=0




Answer



First I have to say that I'm a bit skeptical whether what you want to do can work in general. What if the person blocks everything that is unique of that slide? What if some slides look the same?


But let's ignore those possible problems for now and try a very simple approach. This is only meant as a starting point! My answer is based on this very good answer here and this one, which contains more explanation and ways to improve.


First let us import your example data:


gif = Import["https://i.stack.imgur.com/k4ChI.gif"];
framenohead = Import["https://i.stack.imgur.com/JMthj.png"];
framewithhead = Import["https://i.stack.imgur.com/pwjwb.png"];


We scale the images down to 32x32 and obtain the pixel data using ImageData. Scaling down will increase the robustness against small differences between the slide we are searching for and the video, as well as decrease the computation time. Note that you could probably scale down the whole video beforehand. We search for the frame with the head on it, change this line to search for the other one if you want to try it.


seeked = Flatten[ImageData@ImageResize[framewithhead, {32, 32}], 1];
small = Flatten[ImageData[ImageResize[#, {32, 32}]], 1] & /@ gif;

In order to decide whether two colors are similar we can define the following function. Play around with the threshold value!


SimilarColor[a_, b_] := If[Total[(a - b)^2] < 0.0005, 1, 0];

Now we just pick the frame with the highest score, i.e. the highest number of sectors that are similar to the frame we are looking for.


score = Total@MapThread[SimilarColor, {#, seeked}] & /@ small;
Position[score, Max[score]]


This returns 15 in both cases (with and without head)!


Edit: for the provided slides


Lets rename your slides, such that {{1}} became {{001}}. You can do it with something like


Do[RenameFile[
NotebookDirectory[] <> "\\frames\\{{" <> ToString[i] <> "}}.jpg",
NotebookDirectory[] <> "\\frames\\{{0" <> ToString[i] <> "}}.jpg"], {i, 10, 99}]

Now we import all those images, for example:


frames = Import[#] & /@ 

FileNames["*.jpg", NotebookDirectory[] <> "\\frames"];
slides = Import[#] & /@
FileNames["*.jpg", NotebookDirectory[] <> "\\slides"];

We can scale down the images, I used a slightly higher resolution because of some details in your slides.


res = 48;
smallframes =
Flatten[ImageData[ImageResize[#, {res, res}]], 1] & /@ frames;
smallslides =
Flatten[ImageData[ImageResize[#, {res, res}]], 1] & /@ slides;


I also slightly changed the color comparison, I'm not actually sure that you need to change it, but why not try a different one :)


SimilarColor[a_, b_] := If[And @@ ((# < 0.05) & /@ ((a - b)^2)), 1, 0];

Now comes the heavy calculation (takes a couple of minutes on my laptop): We label each frame by the slide that's in the background.


labels = Monitor[Table[
With[{score =
Total@MapThread[SimilarColor, {#, smallframes[[i]]}] & /@ smallslides},
Position[score, Max[score]]][[1, 1]], {i, Length[frames]}], i]



{2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,12,12,12,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,3,3,3,3,3,3,3,3,3,3}



This list should contain all the information you need, in particular the first occurrence of slide 9 (with the photo mask) is


 Min@Position[labels, 9]


95



Comments

Popular posts from this blog

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

equation solving - Invert and fit implicitly defined curve

I need to fit an implicitly defined curve. I thought I could get some data out of Solve , and then using FindFit . Therefore, I would like to find the relation the parametric curve defined by $F(x,y)=0$: Solve[-(1/2) + 1/2 (0.41202 BesselK[0, 0.1 Sqrt[x^2 + y^2]] + (0.101483 x BesselK[1, 0.1 Sqrt[x^2 + y^2]])/Sqrt[x^2 + y^2]) == 0, y] But I can't get an output: Solve was unable to solve the system with inexact coefficients or the system obtained by direct rationalization of inexact numbers present in the system. Since many of the methods used by Solve require exact input, providing Solve with an exact version of the system may help. >> Edit: In particular, I would like to fit the data coming from the curve with the expression of another curve, and not with a function $f(x)$. In particular, since this clearly looks like a cardioid , I would like it to fit to something like it. What other strategies could I try?

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...