-1

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 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\' + $($_.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: How to create a fast slideshow in python?, image slide show using tkinter and ImageTk, Python: auto slideshow with PIL/TkInter with no saving images, 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:

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't identify non-letter keys. I only stumbled upon cv2.waitKeyEx 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?

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36
Ξένη Γήινος
  • 2,181
  • 1
  • 9
  • 35
  • You should be able to do that quite easily with **OpenCV** and its `imshow()` and `waitKey()`. Not the best example, but see here for a concept https://dev.to/siddharth2016/how-to-make-a-slideshow-using-opencv-5ge7 – Mark Setchell Jun 05 '23 at 06:50
  • You can probably do it with `feh` too, example here https://stackoverflow.com/a/70449081/2836621 – Mark Setchell Jun 05 '23 at 06:54
  • 1
    And VLC can't play a video manually frame by frame? Pause it and use "e" to step through it frame by frame. – 9769953 Jun 05 '23 at 07:47
  • needs more focus. you describe many building blocks. there's a lot of text in the question but I haven't found what you actually require help with. don't say everything. – Christoph Rackwitz Jun 05 '23 at 18:23

1 Answers1

2

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('\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()
Ξένη Γήινος
  • 2,181
  • 1
  • 9
  • 35