Skip to main content

image processing - Merge transcript and corresponding slides from video


This would be used to make little "books" out of edX courses, which I am taking.


I would like to write a small program, which automatically takes a screenshot of a video, when a certain amount of pixel (say 100) "pixel_change" change.


I also have a subRip file, which holds the transcript of the video file. I would like to kind of make a image book, where I have the images of the slide, and on the bottom the transcript for the appropriate slides.





  1. Automatically extract all slides from videos with timestamp.




  2. Read subRip file.





1
00:00:04,359 --> 00:00:07,009

Welcome to this lesson in micro and nano fabrication.

2
00:00:07,447 --> 00:00:09,873
The picture behind me shows a colorful SEM image

3
00:00:10,073 --> 00:00:11,948
of a bi-morph MEMS activator.


4
00:00:12,148 --> 00:00:14,872
And in the next few minutes, I will show how it was fabricated

5
00:00:15,072 --> 00:00:16,722
in our clean room at EPFL.

6
00:00:17,175 --> 00:00:20,188

Although it does not involve all possible fabrication steps

7
00:00:20,388 --> 00:00:23,276
that are nowadays available in advanced MEMS processes.



  1. Assign the slides according to their timestamps to each text block from the subRip file.





  2. Export Textblock and associated slides as one image.




enter image description here


How can I do it?


Thanks for any help!



Answer



We can use b3m2a1's suggestion to extract screenshots from the existing video file using the subRip timings for the subtitles. I'll assume each slide is displayed in the video when the subtitle occurs.


The subtitle timings in the subRip file provide a quick shortcut to locate video frames for each slide -- avoiding the need to scan the video for matching frames.



Read a subRip file and locate subtitle timings


SubRip files are text files that contain subtitles and timings for a video. First, read the subRip file into a list, where each item in subRip is a line from the file. I've saved the subRip text from the question in a sample file named "subtitles.srt".


subRip = ReadList["subtitles.srt", String];

Next, extract the lines that have the times each subtitle appears and disappears on the screen. The lines for these times have a specific format that we can use to select them using: Select[subRip,StringMatchQ[#,{__~~Whitespace~~"-->"~~Whitespace~~__}]&]. The subRip times use a comma in place of a decimal point. Fix that with StringReplace[...,","->"."]. Finally, split the times at the "-->" string with StringSplit[..., Whitespace~~"-->"~~Whitespace]. Combining these steps gives the start and end times for each subtitle as strings representing hours, minutes, and seconds.


subtitleTimings = 
StringSplit[
StringReplace[
Select[subRip, StringMatchQ[#, {__~~Whitespace~~"-->"~~Whitespace~~__}] &],
"," -> "."], Whitespace ~~ "-->" ~~ Whitespace]


We get these strings:



{{"00:00:04.359", "00:00:07.009"}, {"00:00:07.447", "00:00:09.873"},
{"00:00:10.073", "00:00:11.948"}, {"00:00:12.148", "00:00:14.872"},
{"00:00:15.072", "00:00:16.722"}, {"00:00:17.175", "00:00:20.188"},
{"00:00:20.388", "00:00:23.276"}}

Convert the timing strings to seconds


Convert each string with DateObject, and convert the times to seconds. Partition the values in pairs.



timesSeconds = 
Partition[
UnitConvert[
Total[DateValue[
DateObject[#], {"Hour", "Minute", "Second", "Millisecond"},
Quantity]] & /@ Flatten[subtitleTimings], "Seconds"], {2}]

These are the start end end times for each subtitle.



{{4.359s,7.009s},{7.447s,9.873s},{10.073s,11.948s},

{12.148s,14.872s},{15.072s,16.722s},{17.175s,20.188s},
{20.388s,23.276s}}

Find video frames for each slide and import


The frame rate is available from the video file. We need to know the frame rate to get a frame number from a time in seconds. The frame rate is a value in seconds. Assume a QuickTime video file.


frameRate = Quantity[Import["file.mov", "FrameRate"], 1/"Seconds"];

Using frameRate, compute beginning- and end-frame for each subtitle.


framesStartEnd = Round[timesSeconds*frameRate];


Average the start and end frames. This gives the frames to extract from the video when it's assumed a slide is displayed. Use frameList to get slides from the video.


frameList = 
Round[(First /@ framesStartEnd + Last /@ framesStartEnd)/2];

slides = Import["file.mov", {"ImageList", frameList}];

Timestamps and subtitles


SubRip allows multiple lines of text for each subtitle. For simplicity, assume one line of text per subtitle.


Get timestamps for each subtitle with:


timeStamps = First /@ timesSeconds



{4.359s,7.447s,10.073s,12.148s,15.072s,17.175s,20.388s}

Get the subtitles for each slide:


transcripts =
Split[
Select[subRip, !StringMatchQ[#, {__~~Whitespace~~"-->"~~Whitespace~~__}]&],
DigitQ[#] &]



{{1,"Welcome to this lesson in micro and nano fabrication."},
{2,"The picture behind me shows a colorful SEM image"},
{3,"of a bi-morph MEMS activator."},
{4,"And in the next few minutes, I will show how it was fabricated"},
{5,"in our clean room at EPFL."},
{6,"Although it does not involve all possible fabrication steps"},
{7,"that are nowadays available in advanced MEMS processes."}}

The results are slides, timeStamps and transcripts.



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[ ...

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...

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?