Skip to content

Commit

Permalink
add different scenedetect method
Browse files Browse the repository at this point in the history
  • Loading branch information
TNTwise committed Sep 4, 2024
1 parent a5303bf commit 8722314
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 33 deletions.
21 changes: 13 additions & 8 deletions backend/src/FFmpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from .Util import currentDirectory, log, printAndLog
import time
from time import sleep

from threading import Thread

def convertTime(remaining_time):
"""
Expand Down Expand Up @@ -89,9 +89,8 @@ def __init__(
frameSetupFunction=None,
crf: str = "18",
sharedMemoryID: str = None,
shm: shared_memory.SharedMemory = None,
inputFrameChunkSize: int = None,
outputFrameChunkSize: int = None,
channels=3,

):
"""
Generates FFmpeg I/O commands to be used with VideoIO
Expand Down Expand Up @@ -122,10 +121,16 @@ def __init__(
self.crf = crf
self.frameSetupFunction = frameSetupFunction
self.sharedMemoryID = sharedMemoryID
self.shm = shm
self.inputFrameChunkSize = inputFrameChunkSize
self.outputFrameChunkSize = outputFrameChunkSize

self.sharedMemoryThread = Thread(
target=lambda: self.writeOutInformation(self.outputFrameChunkSize)
)
self.inputFrameChunkSize = self.width * self.height * 3
self.outputFrameChunkSize = (
self.width * self.upscaleTimes * self.height * self.upscaleTimes * 3
)
self.shm = shared_memory.SharedMemory(
name=self.sharedMemoryID, create=True, size=self.outputFrameChunkSize
)
self.totalOutputFrames = self.totalInputFrames * self.ceilInterpolateFactor

self.writeOutPipe = self.outputFile == "PIPE"
Expand Down
16 changes: 3 additions & 13 deletions backend/src/RenderVideo.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,7 @@ def __init__(
self.renderThread = Thread(target=self.renderInterpolate)
printAndLog("Using Interpolation Model: " + self.interpolateModel)

self.inputFrameChunkSize = self.width * self.height * 3
self.outputFrameChunkSize = (
self.width * self.upscaleTimes * self.height * self.upscaleTimes * 3
)
self.shm = shared_memory.SharedMemory(
name=self.sharedMemoryID, create=True, size=self.outputFrameChunkSize
)

super().__init__(
inputFile=inputFile,
outputFile=outputFile,
Expand All @@ -122,14 +116,10 @@ def __init__(
frameSetupFunction=self.setupRender,
crf=crf,
sharedMemoryID=sharedMemoryID,
shm=self.shm,
inputFrameChunkSize=self.inputFrameChunkSize,
outputFrameChunkSize=self.outputFrameChunkSize,
channels=3
)

self.sharedMemoryThread = Thread(
target=lambda: self.writeOutInformation(self.outputFrameChunkSize)
)

self.sharedMemoryThread.start()
self.ffmpegReadThread = Thread(target=self.readinVideoFrames)
self.ffmpegWriteThread = Thread(target=self.writeOutVideoFrames)
Expand Down
106 changes: 94 additions & 12 deletions backend/src/SceneDetect.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,31 @@
from tqdm import tqdm
import cv2
from queue import Queue
import numpy as np
from .FFmpeg import FFMpegRender
from threading import Thread
from .Util import printAndLog
from queue import Queue
class SCDetectNP:
def __init__(self):
self.i0 = None
self.i1 = None
def forward(self, img1):
if self.i0 is None:
self.i0 = img1
self.image0mean = np.mean(self.i0)
return
self.i1 = img1
img1mean = np.mean(self.i1)
if self.image0mean > img1mean + 20 or self.image0mean < img1mean - 20:
self.image0mean = img1mean
return True
self.image0mean = img1mean
return False




class SceneDetect:
class SceneDetect(FFMpegRender):
"""
Class to detect scene changes based on a few parameters
sceneChangeSsensitivity: This dictates the sensitivity where a scene detect between frames is activated
Expand All @@ -18,37 +40,97 @@ def __init__(
sceneChangeSensitivity: float = 3.0,
sceneChangeMethod: str = "pyscenedetect",
):
self.getVideoProperties(inputFile)
super().__init__(
inputFile=inputFile,
outputFile=None,
interpolateFactor=1,
upscaleTimes=1,
benchmark=True,
overwrite=True,
sharedMemoryID=None,
channels=3
)
self.getVideoProperties(inputFile)
self.inputFile = inputFile
self.sceneChangeSensitivity = sceneChangeSensitivity
self.sceneChangeMethod = sceneChangeMethod


self.readThread = Thread(target=self.readinVideoFrames)

# add frame chunk size to ffmpegrender

def getFrameTo100x100img(self, image: bytes) -> np.ndarray:
frame = np.frombuffer(image,dtype=np.uint8).reshape(self.height, self.width, 3)
frame = cv2.resize(
frame, dsize=(100, 100)
)
return frame

def copy_queue(self,original_queue):
items = []

# Extract items to a list
while not original_queue.empty():
items.append(original_queue.get())

new_queue = Queue()
for item in items:
new_queue.put(item)
original_queue.put(item)

return new_queue

def printQueue(self,queue: Queue):
queue = self.copy_queue(queue)
t = []
while not queue.empty():
item = queue.get()
t.append(item)
print(t)
print(len(t))

def getPySceneDetectTransitions(self) -> Queue:
sceneChangeStack = Queue()
self.readThread.start()
sceneChangeQueue = Queue()
adaptiveDetector = AdaptiveDetector(
adaptive_threshold=self.sceneChangeSensitivity
)
openedVideo = open_video(self.inputFile)
frame_count = openedVideo.duration.frame_num
for frame_num in tqdm(range(frame_count - 1)):
frame = openedVideo.read()
frame = cv2.resize(
frame, dsize=(100, 100)
) # downscaling makes no difference in quality for scene change, bottlenecked by resize speed

for frame_num in tqdm(range(self.totalInputFrames - 1)):

frame = self.getFrameTo100x100img(self.readQueue.get())

detectedFrameList = adaptiveDetector.process_frame(
frame_num=frame_num, frame_img=frame
)
# if len(detectedFrameList) == 1:
# sceneChangeList += detectedFrameList
match len(detectedFrameList):
case 1:
sceneChangeStack.put(detectedFrameList[0] - 1)
return sceneChangeStack
sceneChangeQueue.put(detectedFrameList[0] - 1)
return sceneChangeQueue



def getMeanTransitions(self):
self.readThread.start()
sceneChangeQueue = Queue()
detector = SCDetectNP()
for frame_num in tqdm(range(self.totalInputFrames - 1)):
frame = self.getFrameTo100x100img(self.readQueue.get())
if detector.forward(frame):
sceneChangeQueue.put(frame_num-1)
return sceneChangeQueue

def getTransitions(self) -> Queue:
"Method that returns a list of ints where the scene changes are."

if self.sceneChangeMethod == "pyscenedetect":
return self.getPySceneDetectTransitions()
if self.sceneChangeMethod == "mean":
return self.getMeanTransitions()


if __name__ == "__main__":
Expand Down

0 comments on commit 8722314

Please sign in to comment.