mirror of
https://github.com/Astatin3/vidscout.git
synced 2026-06-09 00:28:04 -06:00
Write lots of code
This commit is contained in:
@@ -1,3 +1,6 @@
|
|||||||
|
*.mp4
|
||||||
|
.idea/
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from pytubefix import YouTube
|
||||||
|
from pytubefix.cli import on_progress
|
||||||
|
|
||||||
|
TBAip = "https://www.thebluealliance.com/api/v3"
|
||||||
|
# Free TBA Key!
|
||||||
|
headers = {"User-Agent": "Mozilla/5.0", "X-TBA-Auth-Key": "fzQY0pv6qwfwuII5Xx2bmP57BBSuE0maxKailYlrI0e1EdfKCq6F3Th9FFDqpW7f"}
|
||||||
|
RES = '1080p'
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
sys.exit("Usage: downloader.py <event code>")
|
||||||
|
|
||||||
|
evcode = sys.argv[1]
|
||||||
|
|
||||||
|
event_matches = requests.get(TBAip + "/event/" + evcode + "/matches", headers=headers).json()
|
||||||
|
|
||||||
|
video_urls = [None for _ in range(len(event_matches))]
|
||||||
|
|
||||||
|
for match in event_matches:
|
||||||
|
if match["comp_level"] != "qm": continue
|
||||||
|
match_urls = []
|
||||||
|
for video_url in match["videos"]:
|
||||||
|
if video_url["type"] != "youtube": continue
|
||||||
|
match_urls.append(video_url["key"])
|
||||||
|
if len(match_urls) != 0:
|
||||||
|
video_urls[match["match_number"]] = match_urls
|
||||||
|
|
||||||
|
if not os.path.exists(evcode):
|
||||||
|
os.makedirs(evcode)
|
||||||
|
|
||||||
|
for i, match in enumerate(video_urls):
|
||||||
|
if match is None: continue
|
||||||
|
# print(match)
|
||||||
|
for url in match:
|
||||||
|
if os.path.exists(os.path.join(evcode, str(i)+".mp4")):
|
||||||
|
continue
|
||||||
|
yt = YouTube("https://youtube.com/watch?v="+url, on_progress_callback=on_progress)
|
||||||
|
if yt.author != "FIRSTRoboticsCompetition": continue
|
||||||
|
|
||||||
|
|
||||||
|
for idx, stream in enumerate(yt.streams):
|
||||||
|
if stream.resolution == RES:
|
||||||
|
break
|
||||||
|
|
||||||
|
print(f"Downloading match {i}, {stream.resolution}")
|
||||||
|
|
||||||
|
# print(yt.streams[idx])
|
||||||
|
yt.streams[idx].download(output_path = evcode, filename = str(i)+".mp4")
|
||||||
|
|
||||||
|
# ys = yt.streams.get_highest_resolution()
|
||||||
|
# print(yt.title)
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
+250
@@ -0,0 +1,250 @@
|
|||||||
|
import math
|
||||||
|
from time import time
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from cv2.typing import Point, Scalar
|
||||||
|
|
||||||
|
from src.videoCrop import contains_start, contains_end
|
||||||
|
import src.imageTools as imageTools
|
||||||
|
|
||||||
|
redColor = (120, 70, 238)
|
||||||
|
colorRange = 60
|
||||||
|
|
||||||
|
if len(sys.argv) < 3:
|
||||||
|
print("Usage: playVideo.py <event code> <match> <undistortion config>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
class distorton_config:
|
||||||
|
def __init__(self):
|
||||||
|
self.k_value = 0
|
||||||
|
self.zoom_value = 1
|
||||||
|
self.dots = [
|
||||||
|
[0.,0.],
|
||||||
|
[0.,0.],
|
||||||
|
[0.,0.],
|
||||||
|
[0.,0.]
|
||||||
|
]
|
||||||
|
|
||||||
|
dconf = distorton_config()
|
||||||
|
conf_split = sys.argv[3].split(',')
|
||||||
|
dconf.k_value = float(conf_split[0])
|
||||||
|
dconf.zoom_value = float(conf_split[1])
|
||||||
|
dconf.dots[0][0] = float(conf_split[2])
|
||||||
|
dconf.dots[0][1] = float(conf_split[3])
|
||||||
|
dconf.dots[1][0] = float(conf_split[4])
|
||||||
|
dconf.dots[1][1] = float(conf_split[5])
|
||||||
|
dconf.dots[2][0] = float(conf_split[6])
|
||||||
|
dconf.dots[2][1] = float(conf_split[7])
|
||||||
|
dconf.dots[3][0] = float(conf_split[8])
|
||||||
|
dconf.dots[3][1] = float(conf_split[9])
|
||||||
|
|
||||||
|
filename = sys.argv[1] + "/" + sys.argv[2] + ".mp4"
|
||||||
|
|
||||||
|
blueColor = (280, 30, 125)
|
||||||
|
blueColorUnselected = (int(blueColor[0]/3), int(blueColor[1]/3), int(blueColor[2]/3))
|
||||||
|
redColor = (125, 30, 280)
|
||||||
|
redColorUnselected = (int(redColor[0]/3), int(redColor[1]/3), int(redColor[2]/3))
|
||||||
|
|
||||||
|
cap = cv2.VideoCapture(filename)
|
||||||
|
totalFrames = cap.get(cv2.CAP_PROP_FRAME_COUNT)
|
||||||
|
FPS = cap.get(cv2.CAP_PROP_FPS)
|
||||||
|
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
|
||||||
|
|
||||||
|
|
||||||
|
selAlliance = "blue"
|
||||||
|
selAllianceIndex = 1
|
||||||
|
allianceIndex = [
|
||||||
|
"blue-1",
|
||||||
|
"blue-2",
|
||||||
|
"blue-3",
|
||||||
|
"red-1",
|
||||||
|
"red-2",
|
||||||
|
"red-3"
|
||||||
|
]
|
||||||
|
|
||||||
|
boxes = {
|
||||||
|
"blue-1": np.zeros((int(totalFrames), 4)),
|
||||||
|
"blue-2": np.zeros((int(totalFrames), 4)),
|
||||||
|
"blue-3": np.zeros((int(totalFrames), 4)),
|
||||||
|
"red-1": np.zeros((int(totalFrames), 4)),
|
||||||
|
"red-2": np.zeros((int(totalFrames), 4)),
|
||||||
|
"red-3": np.zeros((int(totalFrames), 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
def undistort(frame):
|
||||||
|
display_frame = imageTools.radialUndistort(frame, dconf.k_value, dconf.zoom_value)
|
||||||
|
return imageTools.perspectiveUndistort(display_frame, dconf.dots)
|
||||||
|
|
||||||
|
def dispColorCircle(frame, text, location, color):
|
||||||
|
cv2.circle(frame, location, 20, color, -1)
|
||||||
|
cv2.putText(frame, text, (location[0]-7, location[1]+8), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (255, 255, 255), 2)
|
||||||
|
|
||||||
|
def distance(p1, p2):
|
||||||
|
return math.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2)
|
||||||
|
|
||||||
|
clicked = False
|
||||||
|
def click_event(event, x, y, flags, param):
|
||||||
|
global selAllianceIndex
|
||||||
|
global selAlliance
|
||||||
|
|
||||||
|
global clicked
|
||||||
|
global clickStart
|
||||||
|
global mousePos
|
||||||
|
|
||||||
|
if not isPaused: return
|
||||||
|
if event == cv2.EVENT_LBUTTONDOWN:
|
||||||
|
clicked = True
|
||||||
|
for i in range(1,4):
|
||||||
|
if distance((x, y), (int(width/2)+50*i,50)) < 20:
|
||||||
|
print("Click event detected")
|
||||||
|
selAllianceIndex = i
|
||||||
|
selAlliance = "red"
|
||||||
|
clicked = False
|
||||||
|
rerender()
|
||||||
|
elif distance((x, y), (int(width/2)+50*i-200,50)) < 20:
|
||||||
|
selAllianceIndex = i
|
||||||
|
selAlliance = "blue"
|
||||||
|
clicked = False
|
||||||
|
rerender()
|
||||||
|
if clicked:
|
||||||
|
clickStart = (x, y)
|
||||||
|
if clicked and event == cv2.EVENT_LBUTTONUP:
|
||||||
|
clicked = False
|
||||||
|
boxes[selAlliance+"-"+str(selAllianceIndex)][int(curFrame)] = np.array([
|
||||||
|
clickStart[0], clickStart[1],
|
||||||
|
x-clickStart[0], y-clickStart[1]
|
||||||
|
])
|
||||||
|
restartTracking()
|
||||||
|
rerender()
|
||||||
|
if clicked and event == cv2.EVENT_MOUSEMOVE:
|
||||||
|
mousePos = (x,y)
|
||||||
|
rerender()
|
||||||
|
|
||||||
|
|
||||||
|
cv2.imshow('frame', np.zeros((1,1)))
|
||||||
|
cv2.setMouseCallback('frame', click_event)
|
||||||
|
|
||||||
|
isPaused = True
|
||||||
|
def rerender():
|
||||||
|
global frame
|
||||||
|
|
||||||
|
global clicked
|
||||||
|
global clickStart
|
||||||
|
global mousePos
|
||||||
|
|
||||||
|
framecopy = frame.copy()
|
||||||
|
cv2.rectangle(framecopy, (0,0),(500,100), (0,0,0), -1)
|
||||||
|
cv2.putText(framecopy, "Paused" if isPaused else "Unpaused", (0,20), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (255, 255, 255), 2)
|
||||||
|
cv2.putText(framecopy, "Frame: "+str(curFrame)+"/"+str(totalFrames), (0,40), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (255, 255, 255), 2)
|
||||||
|
cv2.putText(framecopy, "FPS: "+str(round(1.0 / (time() - start_time)))+"/"+str(round(FPS)), (0,60), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (255, 255, 255), 2)
|
||||||
|
|
||||||
|
for i in range(1,4):
|
||||||
|
dispColorCircle(framecopy, str(i), (int(width/2)+50*i,50), redColor if selAlliance == "red" and selAllianceIndex == i else redColorUnselected)
|
||||||
|
dispColorCircle(framecopy, str(i), (int(width/2)+50*i-200,50), blueColor if selAlliance == "blue" and selAllianceIndex == i else blueColorUnselected)
|
||||||
|
|
||||||
|
for alliance in allianceIndex:
|
||||||
|
if alliance.startswith("red"):
|
||||||
|
color = redColor
|
||||||
|
else:
|
||||||
|
color = blueColor
|
||||||
|
|
||||||
|
box = boxes[alliance][int(curFrame)]
|
||||||
|
if box is None: continue
|
||||||
|
if np.sum(box) == 0: continue
|
||||||
|
|
||||||
|
cv2.rectangle(framecopy, (int(box[0]), int(box[1])), (int(box[0]+box[2]), int(box[1]+box[3])), color, 3)
|
||||||
|
dispColorCircle(framecopy, alliance.split("-")[1], (int(box[0]+box[2]/2), int(box[1]+box[3]/2)), color)
|
||||||
|
|
||||||
|
|
||||||
|
# cv2.rectangle(framecopy, ())
|
||||||
|
|
||||||
|
if clicked:
|
||||||
|
if selAlliance.startswith("red"):
|
||||||
|
color = redColor
|
||||||
|
else:
|
||||||
|
color = blueColor
|
||||||
|
cv2.rectangle(framecopy, (clickStart[0], clickStart[1]), (mousePos[0], mousePos[1]), color, 3)
|
||||||
|
|
||||||
|
|
||||||
|
cv2.imshow('frame', framecopy)
|
||||||
|
|
||||||
|
|
||||||
|
multiTracker = None
|
||||||
|
trackingEnabled = [False, False, False, False, False, False]
|
||||||
|
|
||||||
|
def restartTracking():
|
||||||
|
global multiTracker
|
||||||
|
global trackingEnabled
|
||||||
|
multiTracker = cv2.legacy.MultiTracker_create()
|
||||||
|
# Initialize MultiTracker
|
||||||
|
for i, alliance in enumerate(allianceIndex):
|
||||||
|
box = boxes[alliance][int(curFrame)]
|
||||||
|
if np.sum(box) == 0:
|
||||||
|
trackingEnabled[i] = False
|
||||||
|
continue
|
||||||
|
multiTracker.add(cv2.legacy.TrackerCSRT_create(), frame, box)
|
||||||
|
trackingEnabled[i] = True
|
||||||
|
|
||||||
|
def track(frame):
|
||||||
|
global multiTracker
|
||||||
|
global trackingEnabled
|
||||||
|
|
||||||
|
if multiTracker is None:
|
||||||
|
restartTracking()
|
||||||
|
|
||||||
|
|
||||||
|
success, trackedBoxes = multiTracker.update(frame)
|
||||||
|
if not success: return
|
||||||
|
for i, box in enumerate(trackedBoxes):
|
||||||
|
boxes[allianceIndex[i]][int(curFrame)] = np.array(box)
|
||||||
|
|
||||||
|
|
||||||
|
isInMatch = False
|
||||||
|
while cap.isOpened():
|
||||||
|
global frame
|
||||||
|
global start_time
|
||||||
|
start_time = time()
|
||||||
|
ret, frame = cap.read()
|
||||||
|
if not ret:
|
||||||
|
break
|
||||||
|
|
||||||
|
if not isInMatch and contains_start(frame):
|
||||||
|
isInMatch = True
|
||||||
|
elif isInMatch and contains_end(frame):
|
||||||
|
break
|
||||||
|
|
||||||
|
# print(f"{i}, {isInMatch}")
|
||||||
|
curFrame = cap.get(cv2.CAP_PROP_POS_FRAMES)
|
||||||
|
if not isInMatch: continue
|
||||||
|
if curFrame % 5 != 0: continue
|
||||||
|
|
||||||
|
if not isPaused:
|
||||||
|
if cv2.waitKey(1) & 0xff == 32:
|
||||||
|
isPaused = not isPaused
|
||||||
|
|
||||||
|
frame = undistort(frame)
|
||||||
|
track(frame)
|
||||||
|
rerender()
|
||||||
|
|
||||||
|
while isPaused:
|
||||||
|
k = cv2.waitKey(0) & 0xff
|
||||||
|
if k == 81: # Left arrow
|
||||||
|
cap.set(cv2.CAP_PROP_POS_FRAMES, cap.get(cv2.CAP_PROP_POS_FRAMES) - 1 - 15)
|
||||||
|
curFrame = cap.get(cv2.CAP_PROP_POS_FRAMES)
|
||||||
|
restartTracking()
|
||||||
|
break
|
||||||
|
# if k == 83: # right arrow
|
||||||
|
# cap.set(cv2.CAP_PROP_POS_FRAMES, cap.get(cv2.CAP_PROP_POS_FRAMES) - 1 + 15)
|
||||||
|
# break
|
||||||
|
if k == 27:
|
||||||
|
cap.release()
|
||||||
|
break
|
||||||
|
if k == 32:
|
||||||
|
isPaused = not isPaused
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
cap.release()
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import numpy as np
|
||||||
|
import cv2
|
||||||
|
|
||||||
|
|
||||||
|
def perspectiveUndistort(image, dots):
|
||||||
|
height, width = image.shape[:2]
|
||||||
|
|
||||||
|
src_pts = np.array(dots, dtype=np.float32)
|
||||||
|
dst_pts = np.array([[0, 0], [width - 1, 0], [width - 1, height - 1], [0, height - 1]], dtype=np.float32)
|
||||||
|
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
|
||||||
|
return cv2.warpPerspective(image, M, (width, height))
|
||||||
|
|
||||||
|
def radialUndistort(original_image, k_value=0, zoom_value=1):
|
||||||
|
height, width = original_image.shape[:2]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Radial distortion correction
|
||||||
|
center_x, center_y = width / 2, height / 2
|
||||||
|
x, y = np.meshgrid(np.arange(width), np.arange(height))
|
||||||
|
x = x.astype(np.float32) - center_x
|
||||||
|
y = y.astype(np.float32) - center_y
|
||||||
|
r = np.sqrt(x ** 2 + y ** 2)
|
||||||
|
r_max = np.max(r)
|
||||||
|
scale = float(zoom_value)
|
||||||
|
x_distorted = x * (1 + k_value * (r / r_max)) * scale
|
||||||
|
y_distorted = y * (1 + k_value * (r / r_max)) * scale
|
||||||
|
map_x = (x_distorted + center_x).astype(np.float32)
|
||||||
|
map_y = (y_distorted + center_y).astype(np.float32)
|
||||||
|
return cv2.remap(original_image, map_x, map_y, cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT)
|
||||||
|
|
||||||
|
def getFrameFromVideo(vidpath, framenum):
|
||||||
|
cap = cv2.VideoCapture(vidpath)
|
||||||
|
cap.set(1,framenum)
|
||||||
|
ret, frame = cap.read()
|
||||||
|
if ret:
|
||||||
|
return frame
|
||||||
|
return None
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
image_size = (640, 360)
|
||||||
|
|
||||||
|
start_img = cv2.imread("./images/start.png")
|
||||||
|
end_img = cv2.imread("./images/end.png")
|
||||||
|
|
||||||
|
def containsImage(frame, image):
|
||||||
|
if frame is None: return False
|
||||||
|
frame = cv2.resize(frame, image_size, interpolation=cv2.INTER_AREA)
|
||||||
|
frame = frame[323:345, 305:335]
|
||||||
|
res = cv2.matchTemplate(frame, image, cv2.TM_CCOEFF_NORMED)
|
||||||
|
found = False
|
||||||
|
for _ in zip(*(np.where(res >= .98))[::-1]):
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
return found
|
||||||
|
|
||||||
|
def contains_start(frame):
|
||||||
|
return containsImage(frame, start_img)
|
||||||
|
|
||||||
|
def contains_end(frame):
|
||||||
|
return containsImage(frame, end_img)
|
||||||
|
|
||||||
|
def search_video(video_path, search_img, start_index = 0):
|
||||||
|
cap = cv2.VideoCapture(video_path)
|
||||||
|
|
||||||
|
if not cap.isOpened():
|
||||||
|
print("Cannot open camera")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
i = -1
|
||||||
|
while True:
|
||||||
|
# Capture frame-by-frame
|
||||||
|
ret, frame = cap.read()
|
||||||
|
if not ret:
|
||||||
|
print("Can't receive frame (stream end?). Exiting ...")
|
||||||
|
break
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
if i < start_index: continue
|
||||||
|
|
||||||
|
frame = cv2.resize(frame, image_size, interpolation = cv2.INTER_AREA)
|
||||||
|
crop_img = frame[323:345, 305:335]
|
||||||
|
|
||||||
|
res = cv2.matchTemplate(crop_img, search_img, cv2.TM_CCOEFF_NORMED)
|
||||||
|
found = False
|
||||||
|
for _ in zip(*(np.where(res >= .98))[::-1]):
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if found:
|
||||||
|
# cv2.waitKey(0)
|
||||||
|
break
|
||||||
|
|
||||||
|
cap.release()
|
||||||
|
return i
|
||||||
|
|
||||||
|
|
||||||
|
def videoCrop(video_path):
|
||||||
|
start = search_video(video_path, start_img)
|
||||||
|
end = search_video(video_path, end_img, start)
|
||||||
|
return start, end
|
||||||
+160
@@ -0,0 +1,160 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import filedialog, messagebox
|
||||||
|
from PIL import Image, ImageTk
|
||||||
|
from scipy.optimize import minimize_scalar
|
||||||
|
|
||||||
|
from src import videoCrop
|
||||||
|
from src.imageTools import radialUndistort, perspectiveUndistort, getFrameFromVideo
|
||||||
|
|
||||||
|
class AdvancedDistortionCorrectionTool:
|
||||||
|
def __init__(self, master):
|
||||||
|
self.master = master
|
||||||
|
self.master.title("Advanced Distortion Correction Tool")
|
||||||
|
self.master.geometry("1200x800")
|
||||||
|
|
||||||
|
self.image = None
|
||||||
|
self.original_image = None
|
||||||
|
self.displayed_image = None
|
||||||
|
self.k = 0 # Initialize K value
|
||||||
|
self.dots = []
|
||||||
|
self.dragging = None
|
||||||
|
|
||||||
|
# Create UI elements
|
||||||
|
# self.load_button = tk.Button(self.master, text="Load Image", command=self.load_image)
|
||||||
|
# self.load_button.pack(pady=10)
|
||||||
|
|
||||||
|
self.canvas = tk.Canvas(self.master)
|
||||||
|
self.canvas.pack()
|
||||||
|
# self.canvas.width
|
||||||
|
self.canvas.bind("<Button-1>", self.on_click)
|
||||||
|
self.canvas.bind("<B1-Motion>", self.on_drag)
|
||||||
|
self.canvas.bind("<ButtonRelease-1>", self.on_release)
|
||||||
|
|
||||||
|
self.k_label = tk.Label(self.master, text="K value: 0")
|
||||||
|
self.k_label.pack(pady=5)
|
||||||
|
|
||||||
|
self.k_slider = tk.Scale(self.master, from_=-1.0, to=1.0, resolution=0.01, orient=tk.HORIZONTAL, length=300, command=self.update_k)
|
||||||
|
self.k_slider.pack(pady=10)
|
||||||
|
|
||||||
|
self.zoom_slider = tk.Scale(self.master, from_=0.5, to=5.0, resolution=0.01, orient=tk.HORIZONTAL, label="Zoom", length=300, command=self.update_zoom)
|
||||||
|
self.zoom_slider.pack(pady=10)
|
||||||
|
|
||||||
|
self.perspective_var = tk.BooleanVar()
|
||||||
|
self.perspective_check = tk.Checkbutton(self.master, text="Apply Perspective Correction", variable=self.perspective_var, command=self.update_persp)
|
||||||
|
self.perspective_check.pack(pady=10)
|
||||||
|
|
||||||
|
self.save_button = tk.Button(self.master, text="Save Image", command=self.save_image, state=tk.DISABLED)
|
||||||
|
self.save_button.pack(pady=10)
|
||||||
|
|
||||||
|
self.display_image_scale = 0.5
|
||||||
|
|
||||||
|
self.k_value = 0
|
||||||
|
self.zoom_value = 1
|
||||||
|
|
||||||
|
def update_zoom(self, zoom_value):
|
||||||
|
self.zoom_value = zoom_value
|
||||||
|
self.update_image(self.k_value, zoom_value)
|
||||||
|
|
||||||
|
def update_k(self, k_value):
|
||||||
|
self.k_value = k_value
|
||||||
|
self.update_image(k_value, self.zoom_value)
|
||||||
|
|
||||||
|
def update_persp(self):
|
||||||
|
self.update_image(self.k_value, self.zoom_value)
|
||||||
|
|
||||||
|
def load_image(self, image):
|
||||||
|
print(image)
|
||||||
|
self.original_image = image
|
||||||
|
self.save_button.config(state=tk.NORMAL)
|
||||||
|
height, width = self.original_image.shape[:2]
|
||||||
|
self.dots = [
|
||||||
|
[10, 10],
|
||||||
|
[width-10, 10],
|
||||||
|
[width-10, height-10],
|
||||||
|
[10, height-10]
|
||||||
|
]
|
||||||
|
self.update_image(self.k_value, self.zoom_value)
|
||||||
|
|
||||||
|
def display_image(self):
|
||||||
|
if self.image is not None:
|
||||||
|
self.displayed_image = self.image.copy()
|
||||||
|
if not self.perspective_var.get():
|
||||||
|
for x, y in self.dots:
|
||||||
|
cv2.circle(self.displayed_image, (int(x), int(y)), 15, (0, 255, 0), -1)
|
||||||
|
image = cv2.cvtColor(self.displayed_image, cv2.COLOR_BGR2RGB)
|
||||||
|
image = Image.fromarray(image)
|
||||||
|
image.thumbnail((image.width*self.display_image_scale, image.height*self.display_image_scale), Image.LANCZOS)
|
||||||
|
photo = ImageTk.PhotoImage(image)
|
||||||
|
self.canvas.config(width=photo.width(), height=photo.height())
|
||||||
|
self.canvas.create_image(0, 0, anchor=tk.NW, image=photo)
|
||||||
|
self.canvas.image = photo
|
||||||
|
|
||||||
|
def update_image(self, k_value, zoom_value):
|
||||||
|
if self.original_image is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.image = radialUndistort(self.original_image, float(k_value), float(zoom_value))
|
||||||
|
|
||||||
|
if self.perspective_var.get():
|
||||||
|
self.image = perspectiveUndistort(self.image, self.dots)
|
||||||
|
|
||||||
|
self.display_image()
|
||||||
|
self.k_label.config(text=f"K value: {self.k:.2f}")
|
||||||
|
|
||||||
|
def count_lines(self, image):
|
||||||
|
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
||||||
|
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
|
||||||
|
lines = cv2.HoughLines(edges, 1, np.pi/180, 200)
|
||||||
|
return len(lines) if lines is not None else 0
|
||||||
|
|
||||||
|
def on_click(self, event):
|
||||||
|
x, y = event.x / self.display_image_scale, event.y / self.display_image_scale
|
||||||
|
for i, (dot_x, dot_y) in enumerate(self.dots):
|
||||||
|
if abs(x - dot_x) < 50 and abs(y - dot_y) < 50:
|
||||||
|
self.dragging = i
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def on_drag(self, event):
|
||||||
|
if self.dragging is not None:
|
||||||
|
self.dots[self.dragging] = [event.x / self.display_image_scale, event.y / self.display_image_scale]
|
||||||
|
self.update_image(self.k_value, self.zoom_value)
|
||||||
|
|
||||||
|
def on_release(self, event):
|
||||||
|
self.dragging = None
|
||||||
|
|
||||||
|
def save_image(self):
|
||||||
|
print(",".join([
|
||||||
|
str(self.k_value),
|
||||||
|
str(self.zoom_value),
|
||||||
|
|
||||||
|
str(self.dots[0][0]),
|
||||||
|
str(self.dots[0][1]),
|
||||||
|
str(self.dots[1][0]),
|
||||||
|
str(self.dots[1][1]),
|
||||||
|
str(self.dots[2][0]),
|
||||||
|
str(self.dots[2][1]),
|
||||||
|
str(self.dots[3][0]),
|
||||||
|
str( self.dots[3][1])
|
||||||
|
]))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) < 1:
|
||||||
|
print("Usage: undistort.py <event code>")
|
||||||
|
filename = sys.argv[1] + "/" + (os.listdir(sys.argv[1])[0])
|
||||||
|
|
||||||
|
root = tk.Tk()
|
||||||
|
app = AdvancedDistortionCorrectionTool(root)
|
||||||
|
|
||||||
|
start, stop = videoCrop.videoCrop(filename)
|
||||||
|
if start != stop:
|
||||||
|
print("Found video start and end")
|
||||||
|
|
||||||
|
app.load_image(getFrameFromVideo(filename, start))
|
||||||
|
|
||||||
|
root.mainloop()
|
||||||
Reference in New Issue
Block a user