如何在Python中将视频转换成图像幻灯片?

huangapple go评论65阅读模式
英文:

How to turn a video into an image slideshow in Python?

问题

以下是您要翻译的内容:

"The title might be misnamed, since English isn't my first language.

Long story short, I need to analyze the debug information of some VPN application, and it doesn't keep log files locally, it only has a diagnostics information window, so I can't get raw text dump, although there is a save to file button but it is useless, because every time I try to connect to VPN the window resets, and I am controlling the application programmatically.

So I can only screen record it and analyze the debug information using the video.

I used the following commands to turn the captured videos to images:

gci C:\Users\Xeni\Documents -filter *.wmv | %{ $folder = 'C:\Users\Xeni\Desktop\' + $($_.name -replace '.wmv') md $folder; D:\ffmpeg\ffmpeg.exe -hwaccel cuda -i $_.fullname -vf fps=25 "$folder\%05d.bmp" }

The videos have a framerate of 25 and resolution of 1920x1080. And I need the output images to be lossless because the debug information is textual.

There are thousands of images in each of the folders, and I quickly realized I cannot hope to use Photos app to do the job.

So I want to turn it into a slide show and found a bunch of questions here: https://stackoverflow.com/questions/60477816/how-to-create-a-fast-slideshow-in-python, https://stackoverflow.com/questions/59132423/image-slide-show-using-tkinter-and-imagetk, https://stackoverflow.com/questions/65256826/python-auto-slideshow-with-pil-tkinter-with-no-saving-images, https://stackoverflow.com/questions/72758528/trying-to-make-image-slideshow-with-tkinter-python3 ...

But none of them actually solves the problem, despite being ostensibly relevant.

First they load all images upfront, this cannot happen here as each image is exactly 5.9326171875 MiB in size, and each folder measures dozens of Gibibytes and I have only 16GiB RAM, there simply isn't enough memory for it.

Second they just show the images one by one, the images are shown for a fixed period of time, and you cannot control what is being displayed.

None of them is the case here, and that's why the title is a misnomer.

What I want is very simple, first the application should take the path of the folder containing such images as input, and then scan the directory for all files that match '\d{5}.bmp', and store the 'list' of the file names in memory, and that is what should stay in memory.

When an image is about to be displayed, only then will it be loaded into memory, and it should stay on screen indefinitely until manually switched. After being switched it should either be unloaded from memory immediately, or be unloaded after its index's distance from the currently displayed image's index becomes larger than a fixed small value.

And then there should be a timeline, the timestamp(?) corresponding to each image can be easily calculated, the timestamp in seconds is simply filename divided by 25, and I should be able to control what is displayed by manipulating the timeline.

So this sounds like a video player but it is not a video player, because in video players the next frame is displayed automatically, whereas in this case it MUST NOT, the frames should only be switched manually.

So then why don't I just pause the video? Because I need frame by frame precision, I need to be able to see the next frame by pressing right arrow key and the frame before by pressing left arrow key. Most video players don't have this functionality.

I guess what I have described can be easily done by some expensive video editor, but I don't have enough money and I don't want to buy them. I am very experienced in Python but I didn't write many GUI programs. I am sorry I didn't provide enough code.

How can this be done?


Update

The suggested examples were very bad, but I quickly came up with the following code that does part of what I wanted in under 10 minutes:


def slideshow(folder): images = [str(file) for file in Path(folder).iterdir() if re.match('\d{5}.bmp', file.name)] total = len(images) index = 0 key = -1 while key != 27: img = cv2.imread(images[index]) cv2.imshow('', img) key = cv2.waitKeyEx(0) if key == 2555904: index += 1 elif key == 2424832: index -= 1 index %= total del img cv2.destroyAllWindows()

if __name__ == '__main__': slideshow(sys.argv[1]) ```

I struggled for a bit because I have to identify the key pressed, and all Google search points to `cv2.waitKey` which is totally useless in this regard, it can't identify non-letter keys. I only stumbled upon [`cv2.waitKeyEx`](https://stackoverflow.com/a/66573722/16383578) by chance.

But this isn't what I wanted, it isn't fullscreen, and doesn't have any GUI elements at all. So no timeline.

Now how do I add timeline to this thing?"

<details>
<summary>英文:</summary>

The title might be misnamed, since English isn&#39;t my first language.

Long story short, I need to analyze the debug information of some VPN application, and it doesn&#39;t keep log files locally, it only has a diagnostics information window, so I can&#39;t get raw text dump, although there is a save to file button but it is useless, because everytime I try to connect to VPN the window resets, and I am controlling the application programmatically. 

So I can only screen record it and analyze the debug information using the video.

I used the following commands to turn the captured videos to images:

gci C:\Users\Xeni\Documents -filter *.wmv | %{
$folder = 'C:\Users\Xeni\Desktop&#39; + $($.name -replace '.wmv')
md $folder;
D:\ffmpeg\ffmpeg.exe -hwaccel cuda -i $
.fullname -vf fps=25 "$folder%05d.bmp"
}


The videos have a framerate of 25 and resolution of 1920x1080. And I need the output images to be lossless because the debug information is textual.

There are thousands of images in each of the folders, and I quickly realized I cannot hope to use Photos app to do the job.

So I want to turn it into a slide show and found a bunch of questions here: https://stackoverflow.com/questions/60477816/how-to-create-a-fast-slideshow-in-python, https://stackoverflow.com/questions/59132423/image-slide-show-using-tkinter-and-imagetk, https://stackoverflow.com/questions/65256826/python-auto-slideshow-with-pil-tkinter-with-no-saving-images, https://stackoverflow.com/questions/72758528/trying-to-make-image-slideshow-with-tkinter-python3 ...

But none of them actually solves the problem, despite being ostensibly relevant.

First they load all images upfront, this cannot happen here as each image is exactly 5.9326171875 MiB in size, and each folder measures dozens of Gibibytes and I have only 16GiB RAM, there simply isn&#39;t enough memory for it.

Second they just show the images one by one, the images are shown for a fixed period of time, and you cannot control what is being displayed.

None of them is the case here, and that&#39;s why the title is a misnomer.

What I want is very simple, first the application should take the path of the folder containing such images as input, and then scan the directory for all files that match `&#39;\d{5}.bmp&#39;`, and store the `list` of the file names in memory, and that is what should stay in memory.

When an image is about to be displayed, only then will it be loaded into memory, and it should stay on screen indefinitely until manually switched. After being switched it should  either be unloaded from memory immediately, or be unloaded after its index&#39;s distance from the currently displayed image&#39;s index becomes larger than a fixed small value.

And then there should be a timeline, the timestamp(?) corresponding to each image can be easily calculated, the timestamp in seconds is simply filename divided by 25, and I should be able to control what is displayed by manipulating the timeline. 

So this sounds like a video player but it is not a video player, because in video players the next frame is displayed automatically, whereas in this case it ***MUST NOT***, the frames should only be switched manually.

So then why don&#39;t I just pause the video? Because I need frame by frame precision, I need to be able to see the next frame by pressing right arrow key and the frame before by pressing left arrow key. Most video players don&#39;t have this functionality.

I guess what I have described can be easily done by some expensive video editor, but I don&#39;t have enough money and I don&#39;t want to buy them. I am very experienced in Python but I didn&#39;t write many GUI programs. I am sorry I didn&#39;t provide enough code.

How can this be done?


----------


## **Update** ##

The suggested examples were very bad, but I quickly came up with the following code that does part of what I wanted in under 10 minutes:

import cv2
import re
import sys
from pathlib import Path

def slideshow(folder):
images = [str(file) for file in Path(folder).iterdir() if re.match('\d{5}.bmp', file.name)]
total = len(images)
index = 0
key = -1
while key != 27:
img = cv2.imread(images[index])
cv2.imshow('', img)
key = cv2.waitKeyEx(0)
if key == 2555904:
index += 1
elif key == 2424832:
index -= 1
index %= total
del img
cv2.destroyAllWindows()

if name == 'main':
slideshow(sys.argv[1])


I struggled for a bit because I have to identify the key pressed, and all Google search points to `cv2.waitKey` which is totally useless in this regard, it can&#39;t identify non-letter keys. I only stumbled upon [`cv2.waitKeyEx`](https://stackoverflow.com/a/66573722/16383578) by chance.

But this isn&#39;t what I wanted, it isn&#39;t fullscreen, and doesn&#39;t have any GUI elements at all. So no timeline.

Now how do I add timeline to this thing?

</details>


# 答案1
**得分**: 2

我终于做到了!我已经完成了我想要的一切。

```python
import cv2
import re
import sys
from pathlib import Path

class Slices:
    def __init__(self, folder):
        self.images = [
            str(file) for file
            in Path(folder).iterdir()
            if re.match('\\d{5}.bmp', file.name)
        ]
        self.index = 0
        self.total = len(self.images)
        cv2.namedWindow('slideshow', 0)
        cv2.setWindowProperty('slideshow', 0, 1)
        cv2.createTrackbar('index', 'slideshow', 0, self.total-1, self.update)
    
    def update(self, pos):
        pos %= self.total
        self.img = cv2.imread(self.images[pos])
        cv2.imshow('slideshow', self.img)
        self.index = pos

    def slideshow(self):
        key = -1
        while key != 27:
            self.update(self.index)
            key = cv2.waitKeyEx(0)
            if key == 2555904:
                self.index += 1
            elif key == 2424832:
                self.index -= 1
            self.index %= self.total
            cv2.setTrackbarPos('index', 'slideshow', self.index)
            del self.img

        cv2.destroyAllWindows()


if __name__ == '__main__':
    slices = Slices(sys.argv[1])
    slices.slideshow()
英文:

I finally did it! I have accomplished everything I wanted to.

import cv2
import re
import sys
from pathlib import Path

class Slices:
    def __init__(self, folder):
        self.images = [
            str(file) for file
            in Path(folder).iterdir()
            if re.match(&#39;\d{5}.bmp&#39;, file.name)
        ]
        self.index = 0
        self.total = len(self.images)
        cv2.namedWindow(&#39;slideshow&#39;, 0)
        cv2.setWindowProperty(&#39;slideshow&#39;, 0, 1)
        cv2.createTrackbar(&#39;index&#39;, &#39;slideshow&#39;, 0, self.total-1, self.update)
    
    def update(self, pos):
        pos %= self.total
        self.img = cv2.imread(self.images[pos])
        cv2.imshow(&#39;slideshow&#39;, self.img)
        self.index = pos

    def slideshow(self):
        key = -1
        while key != 27:
            self.update(self.index)
            key = cv2.waitKeyEx(0)
            if key == 2555904:
                self.index += 1
            elif key == 2424832:
                self.index -= 1
            self.index %= self.total
            cv2.setTrackbarPos(&#39;index&#39;, &#39;slideshow&#39;, self.index)
            del self.img

        cv2.destroyAllWindows()


if __name__ == &#39;__main__&#39;:
    slices = Slices(sys.argv[1])
    slices.slideshow()

huangapple
  • 本文由 发表于 2023年6月5日 14:22:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76403945.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定