From d549db405901f37e44024d0d677dcce22a095893 Mon Sep 17 00:00:00 2001 From: u-sho Date: Thu, 7 Sep 2023 09:55:46 +0900 Subject: [PATCH 1/5] format with Black --- .vscode/extensions.json | 6 + .vscode/settings.json | 5 +- CameraCalibration/cameraCalibration.py | 39 +++-- .../cameraCalibrationWithUndistortion.py | 49 +++--- gui/circleDetection.py | 163 ++++++++++-------- gui/data/fft.py | 6 +- video_analysis/circleDetection.py | 148 +++++++++------- video_analysis/fft.py | 28 +-- video_analysis/time-freq-spect.py | 14 +- 9 files changed, 265 insertions(+), 193 deletions(-) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..c8228d8 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "ms-python.vscode-pylance", + "ms-python.black-formatter" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index e2f77d8..f00ee28 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,6 @@ { - "editor.renderWhitespace": "boundary" + "editor.renderWhitespace": "boundary", + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" + }, } \ No newline at end of file diff --git a/CameraCalibration/cameraCalibration.py b/CameraCalibration/cameraCalibration.py index 77ec1be..79cfdd4 100644 --- a/CameraCalibration/cameraCalibration.py +++ b/CameraCalibration/cameraCalibration.py @@ -6,30 +6,35 @@ import glob # Defining the dimensions of checkerboard -CHECKERBOARD = (6,9) +CHECKERBOARD = (6, 9) criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) # Creating vector to store vectors of 3D points for each checkerboard image objpoints = [] # Creating vector to store vectors of 2D points for each checkerboard image -imgpoints = [] +imgpoints = [] # Defining the world coordinates for 3D points -objp = np.zeros((1, CHECKERBOARD[0]*CHECKERBOARD[1], 3), np.float32) -objp[0,:,:2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2) +objp = np.zeros((1, CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32) +objp[0, :, :2] = np.mgrid[0 : CHECKERBOARD[0], 0 : CHECKERBOARD[1]].T.reshape(-1, 2) prev_img_shape = None # Extracting path of individual image stored in a given directory -images = glob.glob('./images/*.jpg') +images = glob.glob("./images/*.jpg") for fname in images: img = cv2.imread(fname) - gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Find the chess board corners # If desired number of corners are found in the image then ret = true - ret, corners = cv2.findChessboardCorners(gray, CHECKERBOARD, cv2.CALIB_CB_ADAPTIVE_THRESH+ - cv2.CALIB_CB_FAST_CHECK+cv2.CALIB_CB_NORMALIZE_IMAGE) - + ret, corners = cv2.findChessboardCorners( + gray, + CHECKERBOARD, + cv2.CALIB_CB_ADAPTIVE_THRESH + + cv2.CALIB_CB_FAST_CHECK + + cv2.CALIB_CB_NORMALIZE_IMAGE, + ) + """ If desired number of corner are detected, we refine the pixel coordinates and display @@ -38,19 +43,19 @@ if ret == True: objpoints.append(objp) # refining pixel coordinates for given 2d points. - corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria) - + corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) + imgpoints.append(corners2) # Draw and display the corners - img = cv2.drawChessboardCorners(img, CHECKERBOARD, corners2,ret) - - cv2.imshow('img',img) + img = cv2.drawChessboardCorners(img, CHECKERBOARD, corners2, ret) + + cv2.imshow("img", img) cv2.waitKey(0) cv2.destroyAllWindows() -h,w = img.shape[:2] +h, w = img.shape[:2] """ Performing camera calibration by @@ -58,7 +63,9 @@ and corresponding pixel coordinates of the detected corners (imgpoints) """ -ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None) +ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera( + objpoints, imgpoints, gray.shape[::-1], None, None +) print("Camera matrix : \n") print(mtx) diff --git a/CameraCalibration/cameraCalibrationWithUndistortion.py b/CameraCalibration/cameraCalibrationWithUndistortion.py index 6bf3dd2..f6ac932 100644 --- a/CameraCalibration/cameraCalibrationWithUndistortion.py +++ b/CameraCalibration/cameraCalibrationWithUndistortion.py @@ -4,53 +4,58 @@ import glob # Defining the dimensions of checkerboard -CHECKERBOARD = (7,10) +CHECKERBOARD = (7, 10) criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) # Creating vector to store vectors of 3D points for each checkerboard image objpoints = [] # Creating vector to store vectors of 2D points for each checkerboard image -imgpoints = [] +imgpoints = [] # Defining the world coordinates for 3D points -objp = np.zeros((1, CHECKERBOARD[0]*CHECKERBOARD[1], 3), np.float32) -objp[0,:,:2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2) +objp = np.zeros((1, CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32) +objp[0, :, :2] = np.mgrid[0 : CHECKERBOARD[0], 0 : CHECKERBOARD[1]].T.reshape(-1, 2) prev_img_shape = None # Extracting path of individual image stored in a given directory -images = glob.glob('./CameraCalibration/img_camera/*.jpg') +images = glob.glob("./CameraCalibration/img_camera/*.jpg") for fname in images: img = cv2.imread(fname) print(fname) - gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Find the chess board corners # If desired number of corners are found in the image then ret = true - ret, corners = cv2.findChessboardCorners(gray, CHECKERBOARD, cv2.CALIB_CB_ADAPTIVE_THRESH+ - cv2.CALIB_CB_FAST_CHECK+cv2.CALIB_CB_NORMALIZE_IMAGE) - + ret, corners = cv2.findChessboardCorners( + gray, + CHECKERBOARD, + cv2.CALIB_CB_ADAPTIVE_THRESH + + cv2.CALIB_CB_FAST_CHECK + + cv2.CALIB_CB_NORMALIZE_IMAGE, + ) + """ If desired number of corner are detected, we refine the pixel coordinates and display them on the images of checker board """ if ret == True: - print('here') + print("here") objpoints.append(objp) # refining pixel coordinates for given 2d points. - corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria) - + corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) + imgpoints.append(corners2) # Draw and display the corners - img = cv2.drawChessboardCorners(img, CHECKERBOARD, corners2,ret) - - cv2.imshow('./CameraCalibration/img_corner/',img) + img = cv2.drawChessboardCorners(img, CHECKERBOARD, corners2, ret) + + cv2.imshow("./CameraCalibration/img_corner/", img) cv2.waitKey(0) cv2.destroyAllWindows() -h,w = img.shape[:2] +h, w = img.shape[:2] print(h, w) """ @@ -59,7 +64,9 @@ and corresponding pixel coordinates of the detected corners (imgpoints) """ -ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None) +ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera( + objpoints, imgpoints, gray.shape[::-1], None, None +) print("Camera matrix : \n") print(mtx) @@ -74,16 +81,16 @@ img = cv2.imread(images[0]) # Refining the camera matrix using parameters obtained by calibration -newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h)) +newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h)) # Method 1 to undistort the image dst = cv2.undistort(img, mtx, dist, None, newcameramtx) # Method 2 to undistort the image -mapx,mapy=cv2.initUndistortRectifyMap(mtx,dist,None,newcameramtx,(w,h),5) +mapx, mapy = cv2.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w, h), 5) -dst = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR) +dst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR) # Displaying the undistorted image -cv2.imshow("./CameraCalibration/img_undistort/",dst) +cv2.imshow("./CameraCalibration/img_undistort/", dst) cv2.waitKey(0) diff --git a/gui/circleDetection.py b/gui/circleDetection.py index 4fed326..d9419ad 100644 --- a/gui/circleDetection.py +++ b/gui/circleDetection.py @@ -12,10 +12,9 @@ class Application(tk.Frame): - def __init__(self, master, video_source=1): + def __init__(self, master, video_source=0): super().__init__(master) - # --------------------------------------------------------- # Open the video source # --------------------------------------------------------- @@ -34,29 +33,27 @@ def __init__(self, master, video_source=1): self.width_and_margin = self.width + 30 self.height_and_margin = self.height + 50 - # set widget size - self.master.geometry(f"{max(self.width_and_margin, 600)}x{self.height_and_margin+270}") + self.master.geometry( + f"{max(self.width_and_margin, 600)}x{self.height_and_margin+270}" + ) self.master.title("Tkinter with Video Streaming and Capture") - # --------------------------------------------------------- # initialize setting # --------------------------------------------------------- - + self.csvfile = "test.csv" self.init_time = time() self.circle_detection_flag = False self.save_flag = False - ###fitting parameter### self.mdist = 20 self.par1 = 100 self.par2 = 60 - ###csv用意### columns_name = ["time"] @@ -64,13 +61,12 @@ def __init__(self, master, video_source=1): lis = [f"x{i + 1}", f"y{i + 1}", f"r{i + 1}"] columns_name.extend(lis) - with open(self.csvfile, "w", newline = '') as f: + with open(self.csvfile, "w", newline="") as f: writer_object = writer(f) writer_object.writerow(columns_name) f.close() ############# - # --------------------------------------------------------- # Font # --------------------------------------------------------- @@ -82,12 +78,7 @@ def __init__(self, master, video_source=1): self.font_frame = fontConfig self.font_btn_big = fontConfig_bold - - self.font_lbl_bigger = font.Font(family=fontFamily, size=45, weight="bold") - self.font_lbl_big = font.Font(family=fontFamily, size=20, weight="bold") - self.font_lbl_middle = font.Font(family=fontFamily, size=15, weight="bold") - self.font_lbl_small = font.Font(family=fontFamily, size=12, weight="normal") - + self.font_lbl = fontConfig # --------------------------------------------------------- # Widget @@ -95,157 +86,187 @@ def __init__(self, master, video_source=1): self.create_widgets() - # --------------------------------------------------------- # Canvas Update # --------------------------------------------------------- - self.delay = 15 #[milli seconds] + self.delay = 15 # [milli seconds] self.update() - def create_widgets(self): - ###Frame_Camera### - self.frame_cam = tk.LabelFrame(self.master, text='Camera', font=self.font_frame) + self.frame_cam = tk.LabelFrame(self.master, text="Camera", font=self.font_frame) self.frame_cam.place(x=10, y=10) - self.frame_cam.configure(width=self.width_and_margin, height=self.height_and_margin) + self.frame_cam.configure( + width=self.width_and_margin, height=self.height_and_margin + ) self.frame_cam.grid_propagate(0) - #Canvas + # Canvas self.canvas1 = tk.Canvas(self.frame_cam) self.canvas1.configure(width=self.width, height=self.height) self.canvas1.grid(column=0, row=0, padx=10, pady=10) ###Frame_Camera_End### - ###Frame_Buttons### - self.frame_btn = tk.LabelFrame(self.master, text='Control', font=self.font_frame) - self.frame_btn.place(x=10, y=10+self.height_and_margin) + self.frame_btn = tk.LabelFrame( + self.master, text="Control", font=self.font_frame + ) + self.frame_btn.place(x=10, y=10 + self.height_and_margin) self.frame_btn.configure(width=max(self.width_and_margin, 570), height=100) self.frame_btn.grid_propagate(0) - #circle detection button - self.btn_snapshot = tk.Button(self.frame_btn, text='円検出', font=self.font_btn_big) - self.btn_snapshot.configure(width=12, height=1, command=self.press_circle_detection) + # circle detection button + self.btn_snapshot = tk.Button( + self.frame_btn, text="円検出", font=self.font_btn_big + ) + self.btn_snapshot.configure( + width=12, height=1, command=self.press_circle_detection + ) self.btn_snapshot.grid(column=0, row=0, padx=10, pady=10) - #Close button - self.btn_close = tk.Button(self.frame_btn, text='Close', font=self.font_btn_big) + # Close button + self.btn_close = tk.Button(self.frame_btn, text="Close", font=self.font_btn_big) self.btn_close.configure(width=12, height=1, command=self.press_close_button) self.btn_close.grid(column=1, row=0, padx=10, pady=10) - #Seve button - self.btn_save = tk.Button(self.frame_btn, text='CSV出力', font=self.font_btn_big) + # Seve button + self.btn_save = tk.Button(self.frame_btn, text="CSV出力", font=self.font_btn_big) self.btn_save.configure(width=12, height=1, command=self.press_save_flag) self.btn_save.grid(column=2, row=0, padx=10, pady=10) ###Frame_Buttons_End### - ##Frame_params### - self.frame_param = tk.LabelFrame(self.master, text='Parameters', font=self.font_frame) - self.frame_param.place(x=10, y=+10+100+self.height_and_margin) + self.frame_param = tk.LabelFrame( + self.master, text="Parameters", font=self.font_frame + ) + self.frame_param.place(x=10, y=+10 + 100 + self.height_and_margin) self.frame_param.configure(width=max(self.width_and_margin, 570), height=150) self.frame_param.grid_propagate(0) - #min Dist - self.minDist_label = tk.Label(self.frame_param, text="min dist", font=self.font_frame) + # min Dist + self.minDist_label = tk.Label( + self.frame_param, text="min dist", font=self.font_lbl + ) self.minDist_label.grid(column=0, row=0, padx=10, pady=10) self.minDist_number = tk.DoubleVar() self.minDist_number.set(self.mdist) - self.minDist_var = ttk.Entry(self.frame_param, textvariable=self.minDist_number, width=5) + self.minDist_var = ttk.Entry( + self.frame_param, textvariable=self.minDist_number, width=5 + ) self.minDist_var.grid(column=1, row=0, padx=10, pady=10) - #param1 - self.param1_label = ttk.Label(self.frame_param, text="param1", font=self.font_frame) + # param1 + self.param1_label = ttk.Label( + self.frame_param, text="param1", font=self.font_lbl + ) self.param1_label.grid(column=2, row=0, padx=10, pady=10) self.param1_number = tk.DoubleVar() self.param1_number.set(self.par1) - self.param1_var = ttk.Entry(self.frame_param, textvariable=self.param1_number, width=5) + self.param1_var = ttk.Entry( + self.frame_param, textvariable=self.param1_number, width=5 + ) self.param1_var.grid(column=3, row=0, padx=10, pady=10) - #param2 - self.param2_label = ttk.Label(self.frame_param, text="param2", font=self.font_frame) + # param2 + self.param2_label = ttk.Label( + self.frame_param, text="param2", font=self.font_lbl + ) self.param2_label.grid(column=4, row=0, padx=10, pady=10) self.param2_number = tk.DoubleVar() self.param2_number.set(self.par2) - self.param2_var = ttk.Entry(self.frame_param, textvariable=self.param2_number, width=5) + self.param2_var = ttk.Entry( + self.frame_param, textvariable=self.param2_number, width=5 + ) self.param2_var.grid(column=5, row=0, padx=10, pady=10) - #change - self.btn_change = tk.Button(self.frame_param, text='Change', font=self.font_btn_big) + # change + self.btn_change = tk.Button( + self.frame_param, text="Change", font=self.font_btn_big + ) self.btn_change.configure(width=12, height=1, command=self.press_change) self.btn_change.grid(column=4, row=1, padx=10, pady=10) ##Frame_params_End### - def update(self): - #Get a frame from the video source + # Get a frame from the video source ret, frame = self.vcap.read() self.second = time() - self.init_time if ret: # キャリブレーション - mtx = np.array([[2.23429413e+03, 0.00000000e+00, 6.36470010e+02], - [0.00000000e+00, 2.31772325e+03, 5.74525725e+02], - [0.00000000e+00, 0.00000000e+00, 1.00000000e+00]]) - _dist = np.array([[-0.77271385, -0.55940247, -0.00505415, 0.08305395, 1.77990709]]) + mtx = np.array( + [ + [2.23429413e03, 0.00000000e00, 6.36470010e02], + [0.00000000e00, 2.31772325e03, 5.74525725e02], + [0.00000000e00, 0.00000000e00, 1.00000000e00], + ] + ) + _dist = np.array( + [[-0.77271385, -0.55940247, -0.00505415, 0.08305395, 1.77990709]] + ) wh = (1080, 1920) newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, _dist, wh, 1, wh) dst = cv2.undistort(frame, mtx, _dist, None, newcameramtx) - frame = frame[40:40+self.width, 100:100+self.height] + frame = frame[40 : 40 + self.width, 100 : 100 + self.height] frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) if self.circle_detection_flag: gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY) - circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, - dp=1, - minDist=self.mdist, - param1=self.par1, - param2=self.par2, - minRadius=0, - maxRadius=0) + circles = cv2.HoughCircles( + gray, + cv2.HOUGH_GRADIENT, + dp=1, + minDist=self.mdist, + param1=self.par1, + param2=self.par2, + minRadius=0, + maxRadius=0, + ) if circles is not None: for circle in circles: for x, y, r in circle: - frame = cv2.circle(frame, (int(x), int(y)), int(r), (255, 0, 0), 3) + frame = cv2.circle( + frame, (int(x), int(y)), int(r), (255, 0, 0), 3 + ) if self.save_flag: - with open(self.csvfile, "a", newline='') as f: + with open(self.csvfile, "a", newline="") as f: writer_object = writer(f) - writer_object.writerow([self.second] + circle.flatten().tolist()) + writer_object.writerow( + [self.second] + circle.flatten().tolist() + ) f.close() self.photo = PIL.ImageTk.PhotoImage(image=PIL.Image.fromarray(frame)) - #self.photo -> Canvas + # self.photo -> Canvas self.canvas1.create_image(0, 0, image=self.photo, anchor=tk.NW) self.master.after(self.delay, self.update) else: self.vcap.set(cv2.CAP_PROP_POS_FRAMES, 0) - def press_close_button(self): self.master.destroy() self.vcap.release() def press_circle_detection(self): self.circle_detection_flag = not self.circle_detection_flag - self.btn_snapshot.config(text= ('円検出中' if self.circle_detection_flag else '円検出')) + self.btn_snapshot.config(text=("円検出中" if self.circle_detection_flag else "円検出")) def press_save_flag(self): self.save_flag = not self.save_flag - self.btn_save.config(text= ('CSV出力中' if self.save_flag else 'CSV出力')) + self.btn_save.config(text=("CSV出力中" if self.save_flag else "CSV出力")) def press_change(self): ###fitting parameter### @@ -254,11 +275,11 @@ def press_change(self): self.par2 = self.param2_number.get() - def main(): root = tk.Tk() - app = Application(master=root)#Inherit + app = Application(master=root) # Inherit app.mainloop() + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/gui/data/fft.py b/gui/data/fft.py index ca56b12..5948948 100644 --- a/gui/data/fft.py +++ b/gui/data/fft.py @@ -8,7 +8,7 @@ x_coodinates = [] -with open('./gui/simple-pendulum-fetch.csv', "r") as f: +with open("./gui/simple-pendulum-fetch.csv", "r") as f: reader_object = reader(f) old_t = 190.8394623 old_x = 196.5 @@ -19,7 +19,7 @@ # 足りないデータは線形補間 while t > (old_t + CAMERA_SECONDS_PER_FRAME + CAMERA_SECONDS_PER_FRAME_EPSILON): old_t += CAMERA_SECONDS_PER_FRAME - old_x = x1 + (x1-old_x)/((t-old_t)/CAMERA_SECONDS_PER_FRAME) + old_x = x1 + (x1 - old_x) / ((t - old_t) / CAMERA_SECONDS_PER_FRAME) x_coodinates.append(old_x) x_coodinates.append(x1) @@ -37,4 +37,4 @@ plt.xlabel("frequency / Hz") plt.ylabel("amplitude / px") plt.grid() -plt.show() \ No newline at end of file +plt.show() diff --git a/video_analysis/circleDetection.py b/video_analysis/circleDetection.py index deb6e06..1eab77f 100644 --- a/video_analysis/circleDetection.py +++ b/video_analysis/circleDetection.py @@ -16,7 +16,6 @@ delay = 1 # milli seconds - class Application(tk.Frame): def __init__(self, master, video_source="video_analysis/iPhone_data/IMG_5477.mov"): super().__init__(master) @@ -34,14 +33,15 @@ def __init__(self, master, video_source="video_analysis/iPhone_data/IMG_5477.mov self.width = int(self.vcap.get(cv2.CAP_PROP_FRAME_WIDTH)) self.height = int(self.vcap.get(cv2.CAP_PROP_FRAME_HEIGHT)) print(f"resolusion: {self.width}x{self.height}") - self.width_and_margin = self.width//2 + 30 - self.height_and_margin = self.height//2 + 30 + self.width_and_margin = self.width // 2 + 30 + self.height_and_margin = self.height // 2 + 30 self.frame_rates = self.vcap.get(cv2.CAP_PROP_FPS) print(f"fps: {self.frame_rates}") - - self.master.geometry(f"{self.width_and_margin+20}x{self.height_and_margin + self.height//2 +222}") + self.master.geometry( + f"{self.width_and_margin+20}x{self.height_and_margin + self.height//2 +222}" + ) self.master.title("Tkinter with Video Streaming and Capture") # --------------------------------------------------------- @@ -53,13 +53,11 @@ def __init__(self, master, video_source="video_analysis/iPhone_data/IMG_5477.mov self.circle_detection_flag = False self.save_flag = False - ###fitting parameter### self.mdist = 50 self.par1 = 100 self.par2 = 31 - ###csv用意### self.frame_iterator = 0 columns_name = ["frame iterator", "time"] @@ -68,13 +66,12 @@ def __init__(self, master, video_source="video_analysis/iPhone_data/IMG_5477.mov lis = [f"x{i + 1}", f"y{i + 1}", f"r{i + 1}"] columns_name.extend(lis) - with open(self.csvfile, "w", newline = '') as f: + with open(self.csvfile, "w", newline="") as f: writer_object = writer(f) writer_object.writerow(columns_name) f.close() ############# - # --------------------------------------------------------- # Font # --------------------------------------------------------- @@ -92,108 +89,125 @@ def __init__(self, master, video_source="video_analysis/iPhone_data/IMG_5477.mov self.font_lbl_middle = font.Font(family=fontFamily, size=15, weight="bold") self.font_lbl_small = font.Font(family=fontFamily, size=12, weight="normal") - # --------------------------------------------------------- # Widget # --------------------------------------------------------- self.create_widgets() - # --------------------------------------------------------- # Canvas Update # --------------------------------------------------------- - self.delay = 15 #[milli seconds] + self.delay = 15 # [milli seconds] self.update() - def create_widgets(self): - ###Frame_Camera### - self.frame_cam = tk.LabelFrame(self.master, text='Camera', font=self.font_frame) + self.frame_cam = tk.LabelFrame(self.master, text="Camera", font=self.font_frame) self.frame_cam.place(x=10, y=10) - self.frame_cam.configure(width=self.width_and_margin, height=self.height_and_margin) + self.frame_cam.configure( + width=self.width_and_margin, height=self.height_and_margin + ) self.frame_cam.grid_propagate(0) - #Canvas + # Canvas self.canvas1 = tk.Canvas(self.frame_cam) self.canvas1.configure(width=self.width, height=self.height) self.canvas1.grid(column=0, row=0, padx=10, pady=10) ###Frame_Camera_End### - ###Frame_Buttons### - self.frame_btn = tk.LabelFrame(self.master, text='Control', font=self.font_frame) - self.frame_btn.place(x=10, y=self.height_and_margin+10) + self.frame_btn = tk.LabelFrame( + self.master, text="Control", font=self.font_frame + ) + self.frame_btn.place(x=10, y=self.height_and_margin + 10) self.frame_btn.configure(width=self.width_and_margin, height=100) self.frame_btn.grid_propagate(0) - #circle detection button - self.btn_snapshot = tk.Button(self.frame_btn, text='円検出', font=self.font_btn_big) - self.btn_snapshot.configure(width=12, height=1, command=self.press_circle_detection) + # circle detection button + self.btn_snapshot = tk.Button( + self.frame_btn, text="円検出", font=self.font_btn_big + ) + self.btn_snapshot.configure( + width=12, height=1, command=self.press_circle_detection + ) self.btn_snapshot.grid(column=0, row=0, padx=20, pady=10) - #Close button - self.btn_close = tk.Button(self.frame_btn, text='Close', font=self.font_btn_big) + # Close button + self.btn_close = tk.Button(self.frame_btn, text="Close", font=self.font_btn_big) self.btn_close.configure(width=12, height=1, command=self.press_close_button) self.btn_close.grid(column=1, row=0, padx=20, pady=10) - #Seve button - self.btn_save = tk.Button(self.frame_btn, text='CSV出力', font=self.font_btn_big) + # Seve button + self.btn_save = tk.Button(self.frame_btn, text="CSV出力", font=self.font_btn_big) self.btn_save.configure(width=12, height=1, command=self.press_save_flag) self.btn_save.grid(column=2, row=0, padx=20, pady=10) ###Frame_Buttons_End### - ##Frame_params### - self.frame_param = tk.LabelFrame(self.master, text='Parameters', font=self.font_frame) - self.frame_param.place(x=10, y=+10+100+self.height_and_margin) + self.frame_param = tk.LabelFrame( + self.master, text="Parameters", font=self.font_frame + ) + self.frame_param.place(x=10, y=+10 + 100 + self.height_and_margin) self.frame_param.configure(width=max(self.width_and_margin, 570), height=150) self.frame_param.grid_propagate(0) - - #min Dist - self.minDist_label = tk.Label(self.frame_param, text="min dist", font=self.font_frame) + + # min Dist + self.minDist_label = tk.Label( + self.frame_param, text="min dist", font=self.font_frame + ) self.minDist_label.grid(column=0, row=0, padx=10, pady=10) self.minDist_number = tk.DoubleVar() self.minDist_number.set(self.mdist) - self.minDist_var = ttk.Entry(self.frame_param, textvariable=self.minDist_number, width=5) + self.minDist_var = ttk.Entry( + self.frame_param, textvariable=self.minDist_number, width=5 + ) self.minDist_var.grid(column=1, row=0, padx=10, pady=10) - #param1 - self.param1_label = ttk.Label(self.frame_param, text="param1", font=self.font_frame) + # param1 + self.param1_label = ttk.Label( + self.frame_param, text="param1", font=self.font_frame + ) self.param1_label.grid(column=2, row=0, padx=10, pady=10) self.param1_number = tk.DoubleVar() self.param1_number.set(self.par1) - self.param1_var = ttk.Entry(self.frame_param, textvariable=self.param1_number, width=5) + self.param1_var = ttk.Entry( + self.frame_param, textvariable=self.param1_number, width=5 + ) self.param1_var.grid(column=3, row=0, padx=10, pady=10) - #param2 - self.param2_label = ttk.Label(self.frame_param, text="param2", font=self.font_frame) + # param2 + self.param2_label = ttk.Label( + self.frame_param, text="param2", font=self.font_frame + ) self.param2_label.grid(column=4, row=0, padx=10, pady=10) self.param2_number = tk.DoubleVar() self.param2_number.set(self.par2) - self.param2_var = ttk.Entry(self.frame_param, textvariable=self.param2_number, width=5) + self.param2_var = ttk.Entry( + self.frame_param, textvariable=self.param2_number, width=5 + ) self.param2_var.grid(column=5, row=0, padx=10, pady=10) - #change - self.btn_change = tk.Button(self.frame_param, text='Change', font=self.font_btn_big) + # change + self.btn_change = tk.Button( + self.frame_param, text="Change", font=self.font_btn_big + ) self.btn_change.configure(width=12, height=1, command=self.press_change) self.btn_change.grid(column=6, row=0, padx=10, pady=10) ##Frame_params_End### - def update(self): - #Get a frame from the video source + # Get a frame from the video source ret, frame = self.vcap.read() self.second = time() - self.init_time @@ -211,24 +225,35 @@ def update(self): # 圧縮 # frame = frame[40:420, 100:540] - frame = cv2.resize(frame, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_LINEAR) + frame = cv2.resize( + frame, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_LINEAR + ) frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) if self.circle_detection_flag: gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY) - circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, - dp=1, - minDist=self.mdist, - param1=self.par1, - param2=self.par2, - minRadius=0, - maxRadius=0) + circles = cv2.HoughCircles( + gray, + cv2.HOUGH_GRADIENT, + dp=1, + minDist=self.mdist, + param1=self.par1, + param2=self.par2, + minRadius=0, + maxRadius=0, + ) if circles is not None: for circle in circles: - circle_colors = [(255,0,0), (0, 255, 0), (0, 0, 255)] + circle_colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255)] circle_colors_itr = 0 for x, y, r in circle: - frame = cv2.circle(frame, (int(x), int(y)), int(r), circle_colors[circle_colors_itr//3], 3) + frame = cv2.circle( + frame, + (int(x), int(y)), + int(r), + circle_colors[circle_colors_itr // 3], + 3, + ) circle_colors_itr += 1 # for circle in circles: # for x, y, r in circle: @@ -241,30 +266,29 @@ def update(self): write_list += circle.flatten().tolist() else: print("no detected, at", self.second) - with open(self.csvfile, "a", newline='') as f: + with open(self.csvfile, "a", newline="") as f: writer_object = writer(f) writer_object.writerow(write_list) f.close() self.photo = PIL.ImageTk.PhotoImage(image=PIL.Image.fromarray(frame)) - #self.photo -> Canvas + # self.photo -> Canvas self.canvas1.create_image(0, 0, image=self.photo, anchor=tk.NW) self.master.after(self.delay, self.update) else: self.vcap.set(cv2.CAP_PROP_POS_FRAMES, 0) - def press_close_button(self): self.master.destroy() self.vcap.release() def press_circle_detection(self): self.circle_detection_flag = not self.circle_detection_flag - self.btn_snapshot.config(text= ('円検出中' if self.circle_detection_flag else '円検出')) + self.btn_snapshot.config(text=("円検出中" if self.circle_detection_flag else "円検出")) def press_save_flag(self): self.save_flag = not self.save_flag - self.btn_save.config(text= ('CSV出力中' if self.save_flag else 'CSV出力')) + self.btn_save.config(text=("CSV出力中" if self.save_flag else "CSV出力")) def press_change(self): ###fitting parameter### @@ -273,11 +297,11 @@ def press_change(self): self.par2 = self.param2_number.get() - def main(): root = tk.Tk() - app = Application(master=root)#Inherit + app = Application(master=root) # Inherit app.mainloop() + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/video_analysis/fft.py b/video_analysis/fft.py index 51b22e5..c9675c4 100644 --- a/video_analysis/fft.py +++ b/video_analysis/fft.py @@ -6,17 +6,17 @@ CAMERA_SECONDS_PER_FRAME = 0.033 # CAMERA_SECONDS_PER_FRAME_EPSILON = 0.005 -start_frame=1000000000 -start_time=100000000.0 -end_frame=0 -end_time=0.0 +start_frame = 1000000000 +start_time = 100000000.0 +end_frame = 0 +end_time = 0.0 x_coodinates = [] -with open('./video_analysis/iPhone_data/simple-fetch.csv', "r") as f: +with open("./video_analysis/iPhone_data/simple-fetch.csv", "r") as f: reader_object = reader(f) - old_frame = 86 #131 + old_frame = 86 # 131 old_x = 1000000000 i = 0 for frame, t, x, _y, _r in reader_object: @@ -32,9 +32,9 @@ # 足りないデータは線形補間 while frame > old_frame + 1: old_frame += 1 - old_x = x + (x-old_x)/2.0 + old_x = x + (x - old_x) / 2.0 x_coodinates.append(old_x) - print('no data', old_frame) + print("no data", old_frame) if i < 5000: x_coodinates.append(x) @@ -43,9 +43,11 @@ f.close() # フレーム間隔(sec) -CAMERA_SECONDS_PER_FRAME = (end_time - start_time)/float((end_frame - start_frame)*1000) -print(CAMERA_SECONDS_PER_FRAME*1000, "ms") -print(float(end_frame - start_frame)/(end_time - start_time), "Hz") +CAMERA_SECONDS_PER_FRAME = (end_time - start_time) / float( + (end_frame - start_frame) * 1000 +) +print(CAMERA_SECONDS_PER_FRAME * 1000, "ms") +print(float(end_frame - start_frame) / (end_time - start_time), "Hz") # x_coodinates = np.array(x_coodinates) fftResult = rfft(x_coodinates) @@ -61,8 +63,8 @@ # 直流成分が極端に大きく出たため、そこだけ取り除いた。(前処理不足) plt.plot(freq[1:100], fftResult[1:100]) # plt.plot(freq, fftResult) -plt.yscale('log') +plt.yscale("log") plt.xlabel("frequency / Hz") plt.ylabel("amplitude / px") plt.grid() -plt.show() \ No newline at end of file +plt.show() diff --git a/video_analysis/time-freq-spect.py b/video_analysis/time-freq-spect.py index 7a8680b..0870e5b 100644 --- a/video_analysis/time-freq-spect.py +++ b/video_analysis/time-freq-spect.py @@ -4,25 +4,27 @@ from scipy import fftpack from scipy import signal -plt.close('all') +plt.close("all") input_file = "./video_analysis/iPhone_data/cycloid-fetch.csv" -framenumber, time, data = np.loadtxt(input_file, unpack=True, delimiter=",", usecols = (0, 1, 2)) +framenumber, time, data = np.loadtxt( + input_file, unpack=True, delimiter=",", usecols=(0, 1, 2) +) # サンプリング周波数 # fs = (framenumber[-1]-framenumber[0])/(time[-1]-time[0]) # print(fs, 'Hz') # 21.2405442036222257 fs = 59.96 -f, t, Sxx = signal.spectrogram(data, fs) #nperseg=512) +f, t, Sxx = signal.spectrogram(data, fs) # nperseg=512) print(Sxx) plt.figure() # plt.pcolormesh(t,fftpack.fftshift(f), fftpack.fftshift(Sxx, axes=0), shading='gouraud') -plt.pcolormesh(t, f, Sxx, shading='gouraud') +plt.pcolormesh(t, f, Sxx, shading="gouraud") # plt.xlim([0,500]) plt.xlabel("time [sec]") plt.ylabel("freqency [Hz]") -cbar = plt.colorbar() #カラーバー表示のため追加 -cbar.ax.set_ylabel("Intensity") #カラーバーの名称表示のため追加 +cbar = plt.colorbar() # カラーバー表示のため追加 +cbar.ax.set_ylabel("Intensity") # カラーバーの名称表示のため追加 plt.show() From 3998372cfe327311a6638107d1607126c7c03480 Mon Sep 17 00:00:00 2001 From: u-sho Date: Thu, 7 Sep 2023 09:59:41 +0900 Subject: [PATCH 2/5] =?UTF-8?q?=E3=83=90=E3=83=BC=E3=82=B8=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E5=9B=BA=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .python-version | 1 + requirements.txt | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 .python-version create mode 100644 requirements.txt diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..9ac3804 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.11.5 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9ac8193 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,13 @@ +contourpy==1.1.0 +cycler==0.11.0 +fonttools==4.42.1 +kiwisolver==1.4.5 +matplotlib==3.7.2 +numpy==1.25.2 +opencv-python==4.8.0.76 +packaging==23.1 +Pillow==10.0.0 +pyparsing==3.0.9 +python-dateutil==2.8.2 +scipy==1.11.2 +six==1.16.0 From 98e488addf3e6f7d1e7c36ace767d39e6211e387 Mon Sep 17 00:00:00 2001 From: u-sho Date: Thu, 7 Sep 2023 20:35:31 +0900 Subject: [PATCH 3/5] =?UTF-8?q?wip.=20GUI=E3=82=A2=E3=83=97=E3=83=AA?= =?UTF-8?q?=E3=81=AE=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF=E3=82=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/img/440x380.png | Bin 0 -> 9863 bytes gui/circleDetection.py | 286 +++++++++++++++++++++-------------------- 2 files changed, 146 insertions(+), 140 deletions(-) create mode 100644 app/img/440x380.png diff --git a/app/img/440x380.png b/app/img/440x380.png new file mode 100644 index 0000000000000000000000000000000000000000..39a9b13a9dc93ebf8ef90cfe8cd82a8f870cc434 GIT binary patch literal 9863 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 Canvas - self.canvas1.create_image(0, 0, image=self.photo, anchor=tk.NW) - self.master.after(self.delay, self.update) - else: + can_get_frame, frame = self.vcap.read() + + if not can_get_frame: + # maybe unreachable + self.video_capture = ImageTk.PhotoImage(file="app/img/440x380.png") + self.canvas1.create_image(0, 0, image=self.video_capture, anchor=tk.NW) self.vcap.set(cv2.CAP_PROP_POS_FRAMES, 0) + stderr("couldn't get camera frame") + exit() + + # カメラ歪補正 + # frame = self.calibration(frame) + frame = frame[0 : self.height, 0 : self.width] + frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + + if self.circle_detection_flag: + gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY) + circles = cv2.HoughCircles( + gray, + cv2.HOUGH_GRADIENT, + dp=1, + minDist=self.mdist, + param1=self.par1, + param2=self.par2, + minRadius=0, + maxRadius=0, + ) + if circles is not None: + for circle in circles: + circle_color = (255, 0, 0) + circle_thickness = 3 + for x, y, r in circle: + frame = cv2.circle( + frame, + (int(x), int(y)), + int(r), + circle_color, + circle_thickness, + ) + + if self.save_flag: + with open(self.csv_file, "a", newline="") as f: + writer_object = writer(f) + writer_object.writerow([second] + circle.flatten().tolist()) + f.close() + self.video_capture = ImageTk.PhotoImage(image=Image.fromarray(frame)) + self.canvas1.create_image(0, 0, image=self.video_capture, anchor=tk.NW) + self.master.after(self.delay, self.update) def press_close_button(self): self.master.destroy() @@ -277,7 +283,7 @@ def press_change(self): def main(): root = tk.Tk() - app = Application(master=root) # Inherit + app = Application(master=root, video_source=0) # Inherit app.mainloop() From f5fe598a0651a9c71281c8180a2d5c8355a31ee3 Mon Sep 17 00:00:00 2001 From: opto comb Date: Tue, 12 Sep 2023 18:34:40 +0900 Subject: [PATCH 4/5] =?UTF-8?q?wip.=20=EF=BC=93=E3=81=A4=E3=81=AE=E6=8C=AF?= =?UTF-8?q?=E3=82=8A=E5=AD=90=E3=82=92=E5=87=BA=E3=81=97=E3=82=8F=E3=81=91?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- video_analysis/circleDetection.py | 63 ++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/video_analysis/circleDetection.py b/video_analysis/circleDetection.py index 1eab77f..78551c5 100644 --- a/video_analysis/circleDetection.py +++ b/video_analysis/circleDetection.py @@ -12,12 +12,12 @@ import numpy as np import sys -video_source = "video_analysis/iPhone_data/IMG_5475.mov" +video_source = "video_analysis/iPhone_data/simple.mov" delay = 1 # milli seconds class Application(tk.Frame): - def __init__(self, master, video_source="video_analysis/iPhone_data/IMG_5477.mov"): + def __init__(self, master, video_source=video_source): super().__init__(master) # --------------------------------------------------------- @@ -100,6 +100,7 @@ def __init__(self, master, video_source="video_analysis/iPhone_data/IMG_5477.mov # --------------------------------------------------------- self.delay = 15 # [milli seconds] + self.config() self.update() def create_widgets(self): @@ -197,15 +198,58 @@ def create_widgets(self): ) self.param2_var.grid(column=5, row=0, padx=10, pady=10) - # change - self.btn_change = tk.Button( - self.frame_param, text="Change", font=self.font_btn_big + # apply + self.btn_apply = tk.Button( + self.frame_param, text="Apply", font=self.font_btn_big ) - self.btn_change.configure(width=12, height=1, command=self.press_change) - self.btn_change.grid(column=6, row=0, padx=10, pady=10) + self.btn_apply.configure(width=12, height=1, command=self.press_apply) + self.btn_apply.grid(column=6, row=0, padx=10, pady=10) ##Frame_params_End### + def config(self): + ret, frame = self.vcap.read() + + if ret: + frame = cv2.resize(frame, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_LINEAR) + frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY) + circles = cv2.HoughCircles( + gray, + cv2.HOUGH_GRADIENT, + dp=1, + minDist=self.mdist, + param1=100, + param2=self.par2, + minRadius=0, + maxRadius=0, + ) + if circles is not None: + for circle in circles: + circle_colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255)] + circle_colors_itr = 0 + for x, y, r in circle: + frame = cv2.circle( + frame, + (int(x), int(y)), + int(r), + circle_colors[circle_colors_itr // 3], + 3, + ) + frame = cv2.line(frame, pt1=(0, y+r), pt2=(self.width, y+r), color=circle_colors[circle_colors_itr // 3]) + circle_colors_itr += 1 + + # TODO: create_widget のときは、閾値 y1, y2 と minDist, par2, それから applyボタン(self.config)、初期設定完了ボタン(self.finish_config) + # def finish_config したら CSV出力もーどに切り替え、中断ボタンを表示、出力完了でダイアログを出す。 + + self.photo = PIL.ImageTk.PhotoImage(image=PIL.Image.fromarray(frame)) + self.canvas1.create_image(0, 0, image=self.photo, anchor=tk.NW) + self.master.after(self.delay, self.config) + + else: + self.vcap.set(cv2.CAP_PROP_POS_FRAMES, 0) + + def update(self): # Get a frame from the video source ret, frame = self.vcap.read() @@ -237,7 +281,7 @@ def update(self): cv2.HOUGH_GRADIENT, dp=1, minDist=self.mdist, - param1=self.par1, + param1=100, param2=self.par2, minRadius=0, maxRadius=0, @@ -263,6 +307,7 @@ def update(self): write_list = [self.frame_iterator, self.second] if circles is not None: for circle in circles: + # TODO: y 座標と self.y1, self.y2 で順番を変えたりする write_list += circle.flatten().tolist() else: print("no detected, at", self.second) @@ -290,7 +335,7 @@ def press_save_flag(self): self.save_flag = not self.save_flag self.btn_save.config(text=("CSV出力中" if self.save_flag else "CSV出力")) - def press_change(self): + def press_apply(self): ###fitting parameter### self.mdist = self.minDist_number.get() self.par1 = self.param1_number.get() From 81ecdf74685e0f5f3acd4e6002c7257ef3ed5d19 Mon Sep 17 00:00:00 2001 From: opto comb Date: Tue, 19 Sep 2023 21:27:35 +0900 Subject: [PATCH 5/5] wip. refactoring --- video_analysis/circle-detection.py | 121 +++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 video_analysis/circle-detection.py diff --git a/video_analysis/circle-detection.py b/video_analysis/circle-detection.py new file mode 100644 index 0000000..da36ed8 --- /dev/null +++ b/video_analysis/circle-detection.py @@ -0,0 +1,121 @@ +import tkinter as tk +from tkinter import _Cursor, _Relief, _ScreenUnits, _TakeFocusValue, Misc +from typing import Any +from typing_extensions import Literal + +import cv2 +from cv2.typing import MatLike, Size + +import numpy as np + +video_source = "video_analysis/iPhone_data/simple.mov" + + +class Application(tk.Frame): + def __init__( + self, + master: Misc | None = None, + cnf: dict[str, Any] | None = ..., + *, + background: str = ..., + bd: _ScreenUnits = ..., + bg: str = ..., + border: _ScreenUnits = ..., + borderwidth: _ScreenUnits = ..., + class_: str = ..., + colormap: Misc | Literal["new", ""] = ..., + container: bool = ..., + cursor: _Cursor = ..., + height: _ScreenUnits = ..., + highlightbackground: str = ..., + highlightcolor: str = ..., + highlightthickness: _ScreenUnits = ..., + name: str = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + takefocus: _TakeFocusValue = ..., + visual: str | tuple[str, int] = ..., + width: _ScreenUnits = ..., + ) -> None: + super().__init__( + master, + cnf, + background=background, + bd=bd, + bg=bg, + border=border, + borderwidth=borderwidth, + class_=class_, + colormap=colormap, + container=container, + cursor=cursor, + height=height, + highlightbackground=highlightbackground, + highlightcolor=highlightcolor, + highlightthickness=highlightthickness, + name=name, + padx=padx, + pady=pady, + relief=relief, + takefocus=takefocus, + visual=visual, + width=width, + ) + + +def getVideoCapture(filename: str) -> cv2.VideoCapture: + vcap = cv2.VideoCapture(filename) + if not vcap.isOpened(): + raise FileExistsError(f"cannot open the video file: {filename}") + return vcap + + +def getVideoInfo(vcap: cv2.VideoCapture) -> {"width": float, "height": float, "fps": float}: + width: float = vcap.get(cv2.CAP_PROP_FRAME_WIDTH) + height: float = vcap.get(cv2.CAP_PROP_FRAME_HEIGHT) + fps: float = vcap.get(cv2.CAP_PROP_FPS) + return {"width": width, "height": height, "fps": fps} + +def calibrate( + frame: MatLike, + cameraMatrix: MatLike = np.array( + [ + [2.23429413e03, 0.00000000e00, 6.36470010e02], + [0.00000000e00, 2.31772325e03, 5.74525725e02], + [0.00000000e00, 0.00000000e00, 1.00000000e00], + ] + ), + distortionCoefficients: MatLike = np.array( + [[-0.77271385, -0.55940247, -0.00505415, 0.08305395, 1.77990709]] + ), + imageSize: Size=(1080,1920), + scalingParameter: float=1 # 変換後に出る画像端の歪をどの程度含むか +) -> MatLike: + optCamMtx, (x, y, width, height) = cv2.getOptimalNewCameraMatrix(cameraMatrix, distortionCoefficients, imageSize, scalingParameter, imageSize) + dst = cv2.undistort(frame, cameraMatrix, distortionCoefficients, None, optCamMtx) + dst = dst[y: y+height, x: x+width] + return dst + +def circleDetect(frame: MatLike, minDist: float, param2: float) -> MatLike: + gray_frame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY) + circles = cv2.HoughCircles( + gray_frame, + cv2.HOUGH_GRADIENT, # Hough変換手法 + dp=1, # 検出基準の緩さ + minDist=minDist, # 検出される円の最小距離(円の重複度) + param1=100, # Canny法 の Hysteresis処理 の上限値 + param2=param2, # Canny法 の Hysteresis処理 の下限値(検出感度の逆) + # minRadius=0, # 検出円の半径の下限値 + # maxRadius=0 # 検出円の半径の上限値 + ) + return circles + +def main(): + root = tk.Tk() + app = Application(master=root) + app.mainloop() + + +if __name__ == "__main__": + main()