[發問] 新手問題 python3 & OpenCV 4.5.3 版本 關於執行程式會卡頓的問題 2

@癸宏_胡 我不是來解決你的問題的。我稍微調整了你原文的格式(字都沒改),這樣可讀性較佳。再看看有沒有前輩可以協助你囉。Good luck! - by @sky

這是一個在照片上畫直線 然後量測值線長度的程式練習
這段程式代碼有一個問題 在執行程式的時候 會卡頓

在想應該是 迴圈的問題 函式中 有一段 :

        directory = 'C:/VSC_PY/GUI_AI/sand_train/text_dir/'  # 目錄路徑
        total_items = count_files(directory)
        if total_items > 1:
            print("此目錄下的項數總計大於 2。")
            print(total_items)
            # 讀取照片
            img1 = cv2.imread('C:/VSC_PY/GUI_AI/sand_train/text_dir/TEST1.jpg') 
            #複製照片避免殘影現象 None   
            img1 = img1.copy()  
        else:
            print("此目錄下的項數總計不足 2。")
            img1 = img.copy()

應該是這一段造成的卡頓 不知道 有沒有大大可以告知小弟
該如何修改才不會卡頓

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import cv2 #OpenCV version: 4.5.3  print("OpenCV version:", cv2.__version__)
# import numpy as np
import math
import os

# 列出指定目錄中的所有檔案和子目錄。
def count_files(directory):
    total = 0
    for root, dirs, files in os.walk(directory):
        total += len(files) + len(dirs)
    return total

# 刪除的圖像
def delete_image(directory, filename):
    filepath = os.path.join(directory, filename)
    if os.path.isfile(filepath):
        os.remove(filepath)
        print(f"Deleted {filename}.")
    else:
        print(f"{filename} 不存在")

# 當左鍵按下並移動時繪製圖形,event可以查看移動,flag查看是否按下
# 定義 `draw_line()` 函數來處理滑鼠事件,例如在圖像上繪製線條
def draw_line(event, x, y, flags, param):
    if event == cv2.EVENT_FLAG_LBUTTON:  # 左鍵按下
        global start_point, isDrawing ,img1

        start_point = (x, y) # 拖曳滑鼠時,不斷記錄座標
        isDrawing = True
        print("左鍵按下")
        print(start_point)

    elif event == cv2.EVENT_MOUSEMOVE and isDrawing:  # 移動滑鼠時繪圖

        # end_point = (x,y) # 拖曳滑鼠時,不斷記錄座標
        end_point = (x,y) 
        print("移動滑鼠")
        print(end_point)

        directory = 'C:/VSC_PY/GUI_AI/sand_train/text_dir/'  # 目錄路徑
        total_items = count_files(directory)
        if total_items > 1:
            print("此目錄下的項數總計大於 2。")
            print(total_items)
            # 讀取照片
            img1 = cv2.imread('C:/VSC_PY/GUI_AI/sand_train/text_dir/TEST1.jpg') 
            #複製照片避免殘影現象 None   
            img1 = img1.copy()  
        else:
            print("此目錄下的項數總計不足 2。")
            img1 = img.copy()
            
        # P1 與 P2 是已知的點
        # P1 = (start_point[0], start_point[1])
        # P2 = (end_point[0], end_point[1])  
        P1 = (start_point[-2], start_point[-1])
        P2 = (end_point[-2], end_point[-1])  
        cv2.line(img1, P1, P2, (0, 255, 0), 3)
 
        # 計算直線長度公式為:length = sqrt((x2-x1)^2 + (y2-y1)^2)
        length = math.sqrt((P2[0] - P1[0])**2 + (P2[1] - P1[1])**2)
        # length = np.sqrt((P2[0]-P1[0])**2 + (P2[1]-P1[1])**2) # 計算直線的長度
        # 長度單位 micrometer(um)
        length = length * 1000

        # 顯示座標  cv2.threshold() strXY=str(x)+','+str(y)+"um"
        strXY=str(x)+','+str(y)+','+str(length)+"um"
        font=cv2.FONT_HERSHEY_PLAIN
        cv2.putText(img1,strXY,(x+10,y+10),font,1,(0,255,0),2)

        cv2.imshow("Image", img1)

    elif event == cv2.EVENT_LBUTTONUP:  # 左鍵釋放 True False
        isDrawing = False
        print("左鍵釋放")
        print(isDrawing)
        #保存照片
        cv2.imwrite('C:/VSC_PY/GUI_AI/sand_train/text_dir/TEST1.jpg', img1)
    
    elif event == cv2.EVENT_RBUTTONDOWN:
        print("右鍵按下")

        if __name__ == "__main__":
            directory = 'C:/VSC_PY/GUI_AI/sand_train/text_dir/'  # 目錄路徑
            filename = "TEST1.jpg" # 圖片的名稱
            delete_image(directory, filename) #刪除照片
        img3 = img.copy()
        cv2.imshow("Image", img3)     

# 載入圖片 'C:/VSC_PY/GUI_AI/sand_train/text_dir/TEST.jpg'
# 預設使用 cv2.IMREAD_COLOR 模式
img = cv2.imread('C:/VSC_PY/GUI_AI/sand_train/text_dir/TEST.jpg')

# cv2.imshow('oxxostudio', img) # 使用名為 oxxostudio 的視窗開啟圖片

# 建立一個視窗以顯示影像並設定事件監聽器
cv2.namedWindow("Image")
# 偵測指定視窗下的滑鼠事件
cv2.setMouseCallback("Image", draw_line)

# 啟動主迴圈以顯示和更新圖像,包括繪製的線條
while True:
    cv2.imshow("Image", img)       
    # 按下任意鍵停止 
    if cv2.waitKey(0):
        cv2.destroyAllWindows() # 結束所有圖片視窗
        break

我把你的問題丟給 ChatGPT,他的建議如下。我沒有裝 OpenCV,要麻煩你自己驗證囉。

有請他將修改處加註解。

我相信 OpenCV 會提供一些 sample code,如果是我,我會先執行他的 code,確定沒問題的話,再慢慢修改成我要的樣子。

由於 ChatGPT 很會唬爛,有可能根本就不對喔。他的說明我放在最下方,請參考,謝謝!

ChatGPT 的 code

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import cv2 # OpenCV version: 4.5.3
import math
import os

# 列出指定目錄中的所有檔案和子目錄
def count_files(directory):
    total = 0
    for root, dirs, files in os.walk(directory):
        total += len(files) + len(dirs)
    return total

# 刪除的圖像
def delete_image(directory, filename):
    filepath = os.path.join(directory, filename)
    if os.path.isfile(filepath):
        os.remove(filepath)
        print(f"Deleted {filename}.")
    else:
        print(f"{filename} 不存在")

# 當左鍵按下並移動時繪製圖形
def draw_line(event, x, y, flags, param):
    global start_point, isDrawing, img, img1

    # 修改1: 將 `cv2.EVENT_FLAG_LBUTTON` 更改為 `cv2.EVENT_LBUTTONDOWN`
    if event == cv2.EVENT_LBUTTONDOWN:  # 左鍵按下
        start_point = (x, y)
        isDrawing = True
        print("左鍵按下")
        print(start_point)
        
        # 修改2: 檢查目錄並讀取圖片放在左鍵按下事件中
        directory = 'C:/VSC_PY/GUI_AI/sand_train/text_dir/'
        total_items = count_files(directory)
        if total_items > 1:
            print("此目錄下的項數總計大於 2。")
            print(total_items)
            img1 = cv2.imread('C:/VSC_PY/GUI_AI/sand_train/text_dir/TEST1.jpg')
            img1 = img1.copy()  
        else:
            print("此目錄下的項數總計不足 2。")
            img1 = img.copy()

    # 修改3: 只在移動滑鼠時更新圖像
    elif event == cv2.EVENT_MOUSEMOVE and isDrawing:  # 移動滑鼠時繪圖
        end_point = (x, y)
        print("移動滑鼠")
        print(end_point)
        
        img_copy = img1.copy()  # 使用副本避免改變原圖像
        
        P1 = (start_point[-2], start_point[-1])
        P2 = (end_point[-2], end_point[-1])
        cv2.line(img_copy, P1, P2, (0, 255, 0), 3)
        
        length = math.sqrt((P2[0] - P1[0])**2 + (P2[1] - P1[1])**2)
        length = length * 1000
        
        strXY = f"{x},{y},{length:.2f}um"
        font = cv2.FONT_HERSHEY_PLAIN
        cv2.putText(img_copy, strXY, (x+10, y+10), font, 1, (0, 255, 0), 2)
        
        cv2.imshow("Image", img_copy)

    elif event == cv2.EVENT_LBUTTONUP:  # 左鍵釋放
        isDrawing = False
        print("左鍵釋放")
        cv2.imwrite('C:/VSC_PY/GUI_AI/sand_train/text_dir/TEST1.jpg', img1)

    elif event == cv2.EVENT_RBUTTONDOWN:  # 右鍵按下
        print("右鍵按下")
        directory = 'C:/VSC_PY/GUI_AI/sand_train/text_dir/'
        filename = "TEST1.jpg"
        delete_image(directory, filename)
        img3 = img.copy()
        cv2.imshow("Image", img3)

# 載入圖片
img = cv2.imread('C:/VSC_PY/GUI_AI/sand_train/text_dir/TEST.jpg')

cv2.namedWindow("Image")
cv2.setMouseCallback("Image", draw_line)

# 修改4: 主迴圈中調整 `cv2.waitKey()` 使用以確保持續運行
while True:
    cv2.imshow("Image", img)
    if cv2.waitKey(1) & 0xFF == 27:  # 按下 ESC 鍵退出
        break

cv2.destroyAllWindows()


ChatGPT 的說明

這些延遲可能是由於圖像複製、頻繁的檔案操作或 cv2.imshow() 的刷新率造成的。以下是一些可能的問題和建議:

  1. 頻繁的檔案操作

    • 每次滑鼠移動都會檢查目錄並讀取圖像,這會造成顯著的延遲,尤其是如果目錄中有很多檔案。
    • 每次滑鼠移動都會儲存圖像,這樣會影響效能。
  2. cv2.imshow() 和主迴圈問題

    • 在主迴圈中,cv2.imshow("Image", img) 被多次調用,但實際上顯示的是靜態圖像。這樣會造成不必要的重複刷新。
  3. 滑鼠事件處理

    • 滑鼠事件中,圖像複製 (img1 = img1.copy()) 和 cv2.imshow("Image", img1) 也可能會導致延遲。

優化建議:

  1. 優化檔案操作

    • 將檔案檢查和圖像讀取移到一開始,只需要在必要時重新讀取圖像。例如,只在左鍵按下時檢查目錄,並根據需要讀取圖像。
  2. 減少不必要的圖像複製

    • 只在滑鼠移動時更新圖像,不要在每次滑鼠移動時重新讀取和複製圖像。
  3. 調整 cv2.imshow() 的位置

    • 在主迴圈中只顯示一次圖像,更新圖像只在有變動時進行。
  4. 適當使用 cv2.waitKey()

    • 使用 cv2.waitKey(1) 以確保主迴圈持續運行,並能及時更新顯示。

謝謝 sky 幫忙 我試試看 ChatGPT 給的代碼不能用 >"< 不過我可以試試看他的建議 謝謝 sky 的回覆 :slight_smile:

1 Like

謝謝 sky大大 ChatGPT 的說明 成功了 :smiley:
我修改試了幾次 後來發現 只要
1.在global 全域變數裡 加入 img_copy 變數
2.在 左鍵釋放裡 儲存檔案 把 img1 改成 img_copy
3.把 if cv2.waitKey(1) & 0xFF == 27: # 按下 ESC 鍵退出 改成 cv2.waitKey(0) 就正常了 不會卡頓了 :grin: 真的 感謝 sky大大 的幫忙

1 Like