1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import youtube_dl
# Youtube video URL
url = "..."
downloadOptions = {}
# Instanciates the downloader
ytDownloadHandler = youtube_dl.YoutubeDL(downloadOptions)
# Fetches video metadata
infoDict = ytDownloadHandler.extract_info(url, download=False)
formats = infoDict.get('formats',None)
for f in formats:
# Our OCR algorithm needs a good quality image, we only process 1440p60
if f.get('format_note',None) == '1440p60':
url = f.get('url',None)
videoCapture = cv2.VideoCapture(url)
# ... rest of the video processing is happening here
videoCapture.release()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# The video capture we ended up with previously
videoCapture = cv2.VideoCapture(url)
currentIndex = 0
frameCount = 0
# Emergency hatch, we cap the max number of frames at 1000.
while currentIndex<1000:
# Reads the next frame, returns false if none
retVal, frame = videoCapture.read()
if not retVal:
print("No more frames, input stream is over. Exiting ...")
break
currentIndex+=1
# ... image optimisation steps are happening here
# Saving the image as a png file
outputFileName =r"D:\outputFolder\image-" + str(currentIndex) + ".png"
cv2.imwrite(outputFileName, frame)
#Skip 360 frames, i.e. 6 seconds for 60 fps
frameCount += 360
videoCapture.set(1,frameCount)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import cv2
import pytesseract
from pathlib import Path
# Loads up the tesseract module
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
### This loop searches all png files in the given folder.
# Then it runs the OCR tool on each one, to produce a block of text containing the kill feed.
for framePngPath in Path(r'D:/path/to/frames/').glob('*.png'):
# Reads a frame
im = cv2.imread(f"D:/path/to/frames/{framePngPath.name}")
# Runs OCR
OcrKillFeed = pytesseract.image_to_string(im)
print('Text output:', OCRkillfeed)
TRUPER “7
{s051]
ling
forcemetokillyou [Melee] [WSSE] 4deadpixel
MarSkiNEW *7=e= [CUCA] STEFF
Marilyn Monroe #6 € [100] ronaldinol212
[TV] Lodochnik over
[ZIPF] xxitzadi
= StumpyL Twitch ¥
PASTICKOCTANE
[S051] slana3 “9 NekéruKo
POWEROFTHANOS
[TMC] Santa Acid “%""®
vw
Marilyn Monroe ==
(100) ronaldinoi212
——
- 25 foryou.
(AL
—
hank Mudrine
1
2
3
4
5
6
7
8
9
OcrKillFeed = pytesseract.image_to_string(im)
print('Text output:', OCRkillfeed)
# Inserts the result into the 'frames' collection (we're dumping it as is) :
insert_result = framesCollection.insert_one({
"videoId": 1, # each video has a unique ID (1 is just an example)
"frameNumber": 12 # each png file has a number that we will fill out here (12 is also just an example)
"usernames": OCRkillfeed,
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
MongoDB "frame" document:
{
"_id": {
"$oid": "437535263cf465664f01421"},
"videoId": <videoID>,
"frameNumber": "frame0",
"usernames": "TRUPER = [5051] ling ting
forcemetokillyou [Melee] [WSSE] 4deadpixel
MarSkiNEW >= [CUCA] STEFF
Marilyn Monroe a2 2 [100] ronaldinol212
[TTV] Lodochnik ore [ZIPF] xxitzadi
StumpyL Twitch =e PASTICKOCTANE
[5051] slana3 4 =Nekoru Ko
POWER.OFTHANOS [TMC] Santa Acid’
Marilyn Monroe «gar [100] ronaldino!212
id |. 25 for yo"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
isInVideoCollectionAlready = False
for framePngPath in Path(r'D:/path/to/frames/').glob('*.png'):
im = cv2.imread(f"D:/path/to/frames/{framePngPath.name}")
OcrKillFeed = pytesseract.image_to_string(im)
# This step is where we are filtering out as much noise as possible from the OCR results, to keep only actual usernames.
# It is explained in finer details just below this snippet.
OCR_usernames_array = re.findall(extractUsernamesRegex, OCRkillfeed)
# We need to extract the frame number from the png file
# Opted out of the snippet for simplicity sake
frameNumber = <frameNumber>
# No need to continue if no usernames were found
if (OCR_usernames_array == []):
continue
# Checks if the video currently processed is already in the DB, if so we just want to update and add to it
if (isInVideoCollectionAlready == False):
videoDoc = videosCollection.find_one({ "name": <nameOfTheVideoBeingProcessed> })
if (videoDoc != None):
isInVideoCollectionAlready = True
if (isInVideoCollectionAlready):
# Updates the video document to add the frame and its reported matches:
videoUpdate_result = videosCollection.update_one(
{ "name": <nameOfTheVideoBeingProcessed> },
{ "$set": { f"usernamesOccurences.{frameName}": OCR_usernames_array }
})
else: # If it's not in the DB, we insert a new document
videoObject = { "name": inputFiles[useVideoNumber].name,
"usernamesOccurences": {} }
videoObject["usernamesOccurences"][frameName] = OCR_usernames_array
# Inserts the result into the 'videos' collection:
videoInsert_result = videosCollection.insert_one(videoObject)
print("Done")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
MongoDB "video" document:
{
"_id": { "$oid": "437535263cf465664f01422" },
"name": <videoName>,
"usernamesOccurences": {
"frame0": ["TRUPER","ling","ting","forcemetokillyou","4deadpixel","MarSkiNEW","STEFF","Marilyn","Monroe", ...rest of the usernames ],
... rest of the frames
"frame99": ["ARADEN"] },
"date": { "$date": {
"$numberLong": "1673863200000"
}},
"title": <videoTitle>,
"creator": "ITemp",
"url": <videoUrl>
}
1
2
3
4
5
6
7
8
9
import levenshtein from 'js-levenshtein';
/* Simplified implementation details,
we are looping through the usernames from the DB
... */
const levDistance = levenshtein(/* username from the DB ->*/username, usernameInput /*<- username typed in the search box*/);
/* if the distance is below the threshold (can be 1, 2 or 3), we have a match! */
if (levDistance <= partialMatchMaxDifference) {
// ... deal with the match here
}