天天看點

一些常用腳本

文章目錄

      • 1. 讀取顯存剩餘占用顯示卡
        • 1.1 比較傻的方式
        • 1.2 更新版(讀取CPU+記憶體+顯存)
      • 2. 多模式占用1~4顯示卡腳本
      • 3. 定時運作腳本
      • 4. 浏覽器倍數播放
      • 5. 統計檔案大小
      • 6. 删除目錄
      • 7. 讀寫txt
      • 8. 壓縮檔案到zip
      • 9. 遠端檔案傳輸
      • 10. 格式化輸出
      • 11. conda虛拟環境
      • 12. 檢視python-class的屬性
      • 13. `numpy`不使用省略号和科學計數法表示
      • 14. `opencv`讀取中文路徑的圖檔報錯問題
      • 15. `opencv`監聽滑鼠點選繪制矩形

1. 讀取顯存剩餘占用顯示卡

深度學習常需要進行參數對比實驗,常見做法是記錄每次程式運作時間,然後手動調節并運作程式。這對于我這種經常忘記時間的人不是很友好,可以用下面的腳本自動監測顯存剩餘,然後程式自動循環周遊參數即可。

1.1 比較傻的方式

import os
import sys
import time
cmd = 'bash run.sh'

def gpu_info():
    # gpu_status = os.popen('nvidia-smi | grep %').read().split('|')
    gpu_status = os.popen('nvidia-smi').read().split('|')
    # get gpu memory info
    gpu0_info, gpu1_info = gpu_status[23], gpu_status[35]  # 不同顯示卡顯存資訊位置略有不同
    gpu0_info, gpu1_info = gpu0_info.strip(), gpu1_info.strip()
    gpu0_info, gpu1_info = gpu0_info.split(" / "), gpu1_info.split(" / ")
    # remove 'MiB'
    gpu0_use_mem, gpu0_total_mem = gpu0_info[0][:-3], gpu0_info[1][:-3]
    gpu1_use_mem, gpu1_total_mem = gpu1_info[0][:-3], gpu1_info[1][:-3]
    gpu0_remain_mem, gpu1_remain_mem = int(gpu0_total_mem) - int(gpu0_use_mem), int(gpu1_total_mem) - int(gpu1_use_mem)
    gpu_remain_mem = [gpu0_remain_mem, gpu1_remain_mem]
    return gpu_remain_mem


def narrow_setup(interval=2):
    gpu_remain_mem = gpu_info()
    i = 0
    #while max(gpu_remain_mem) < 20000:  # set waiting condition
    while gpu_remain_mem[0] < 10000:  # set waiting condition
        gpu_remain_mem = gpu_info()
        i = i % 5
        symbol = 'monitoring: ' + '>' * i + ' ' * (10 - i - 1) + '|'
        gpu_memory_str = 'Remaining gpu memory: {} MiB |'.format(gpu_remain_mem)
        sys.stdout.write('\r' + gpu_memory_str + ' ' + ' ' + symbol)
        sys.stdout.flush()
        time.sleep(interval)
        i += 1
    gpu_index = gpu_remain_mem.index(max(gpu_remain_mem))
    sys.stdout.write("GPU Max Remain: {}".format(max(gpu_remain_mem)) + ' In GPU Number {}'.format(gpu_index))
    os.environ["CUDA_VISIBLE_DEVICES"] = "{}".format(gpu_index)
    print('\n' + cmd)
    os.system(cmd)


if __name__ == '__main__':
    narrow_setup()
           

這樣隻需要等待上一個程式跑完顯存充足後自動運作下一個程式,在指定顯示卡上運作直接設定waiting condition為指定顯示卡即可,而多卡則可以設定檢測到的顯存空餘最多的顯示卡,傳回卡号并添加到cmd中執行即可。

1.2 更新版(讀取CPU+記憶體+顯存)

import psutil
import pynvml
import os
import re

def getMemCpuGPU():
    # 記憶體
    memory = psutil.virtual_memory()
    totalMemory = memory.total  # 總記憶體,機關為byte
    totalMemory = totalMemory * 1.0 / 1024 / 1024 / 1024  # 機關為G
    freeMemory = memory.available  # 可用記憶體
    freeMemory = freeMemory * 1.0 / 1024 / 1024 / 1024
    usedMemory = memory.used  # 使用的記憶體
    usedMemory = usedMemory * 1.0 / 1024 / 1024 / 1024
    print('記憶體剩餘情況: {:0.2f}G/{:0.2f}G'.format(freeMemory, totalMemory))
    # CPU
    CPUPercent = psutil.cpu_percent()
    print('CPU使用率: {}%'.format(CPUPercent))
    # GPU
    pynvml.nvmlInit()
    handle = pynvml.nvmlDeviceGetHandleByIndex(0)  # 這裡的0是GPU id
    meminfo = pynvml.nvmlDeviceGetMemoryInfo(handle)
    totalGPUMemory = meminfo.total * 1.0 / 1024 / 1024 / 1024
    freeGPUMemory = meminfo.free * 1.0 / 1024 / 1024 / 1024
    usedGPUMemory = meminfo.used * 1.0 / 1024 / 1024 / 1024
    print('顯存剩餘情況: {:0.2f}G/{:0.2f}G'.format(freeGPUMemory, totalGPUMemory))
    return freeMemory, totalMemory, CPUPercent, freeGPUMemory, totalGPUMemory
           

如果需要監控其他顯示卡,隻需要将

pynvml.nvmlDeviceGetHandleByIndex(0)

中的

換成其他顯示卡序号即可;若監控多塊顯示卡,加一個循環調用即可。

2. 多模式占用1~4顯示卡腳本

# author: ChronousZ
# contact: [email protected]
import os
import sys
import time
 
cmd = 'python ./test_gpu.py'

 
def gpu_info():
    gpu_status = os.popen('nvidia-smi | grep %').read().split('|')
    # get gpu memory info
    gpu_status_mem = []
    gpu_remain_mem = []
    for gpu_status_item in gpu_status:
        if 'MiB' in gpu_status_item:
            gpu_status_mem.append(gpu_status_item)
    # print(gpu_status_mem)
    for gpu_status_mem_item in gpu_status_mem:
        gpu_used_mem = int(gpu_status_mem_item.split(" / ")[0].split("MiB")[0])
        gpu_total_mem = int(gpu_status_mem_item.split(" / ")[1].split("MiB")[0])
        gpu_remain_mem.append(gpu_total_mem - gpu_used_mem)
    return gpu_remain_mem
 
 
def narrow_setup(watchMethod, which_gpu, memory_th=6000, interval=2):
    gpu_remain_mem = gpu_info()
    # print(gpu_remain_mem)
    i = 0
    watching = True
    while watching:
        if watchMethod == 'single':  # set waiting condition
            if max(gpu_remain_mem) >= memory_th:
                watching = False
                gpu_index = str(gpu_remain_mem.index(max(gpu_remain_mem)))
                show_gpu_mem = [max(gpu_remain_mem)]
        elif watchMethod == 'double':
            if sorted(gpu_remain_mem, reverse=True)[1] >= memory_th:
                watching = False
                gpu_index = ''
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[0])) + ',' # max remain gpu
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[1])) # second max remain gpu
                show_gpu_mem = sorted(gpu_remain_mem, reverse=True)[:2]
        elif watchMethod == 'triple':
            if sorted(gpu_remain_mem, reverse=True)[2] >= memory_th:
                watching = False
                gpu_index = ''
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[0])) + ',' # max remain gpu
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[1])) + ',' # second max remain gpu
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[2])) # third max remain gpu
                show_gpu_mem = sorted(gpu_remain_mem, reverse=True)[:3]
        elif watchMethod == 'quadra':
            if sorted(gpu_remain_mem, reverse=True)[3] >= memory_th:
                watching = False
                gpu_index = ''
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[0])) + ',' # max remain gpu
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[1])) + ',' # second max remain gpu
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[2])) + ',' # third max remain gpu
                gpu_index += str(gpu_remain_mem.index(sorted(gpu_remain_mem, reverse=True)[3])) # fourth max remain gpu
                show_gpu_mem = sorted(gpu_remain_mem, reverse=True)[:4]
        elif watchMethod == 'special_gpu':
            if gpu_remain_mem[which_gpu] >= memory_th:
                watching = False
                gpu_index = str(which_gpu)
                show_gpu_mem = [gpu_remain_mem[which_gpu]]
        gpu_remain_mem = gpu_info()
        i = i % 5
        symbol = 'monitoring: ' + '>' * i + ' ' * (10 - i - 1) + '|'
        gpu_memory_str = 'Remaining gpu memory: {} MiB |'.format(gpu_remain_mem)
        sys.stdout.write('\r' + gpu_memory_str + ' ' + ' ' + symbol)
        sys.stdout.flush()
        time.sleep(interval)
        i += 1
    sys.stdout.write("\r" + "Watching Method: {}, GPU Memory Remain {} MiB in GPU Number {}".format(watchMethod, show_gpu_mem, gpu_index))
    os.environ["CUDA_VISIBLE_DEVICES"] = gpu_index
    # Here set the visiable GPU, direct set '0, 0,1, 0,1,3' etc in runing program
    print('\n' + cmd)
    os.system(cmd)
 
 
if __name__ == '__main__':
    watchMethod = 'double'
    # single gpu, double gpu, triple gpu, quadra gpu
    assert watchMethod in ['single', 'double', 'triple', 'quadra', 'special_gpu']
    # if you set watchMethod as 'special_gpu', then you must give the index of gpu for watching
    which_gpu = 0
    narrow_setup(watchMethod=watchMethod, which_gpu=which_gpu)

           

3. 定時運作腳本

做reid的同學可能會遇到多個程式同時在最終檢索時計算距離矩陣導緻記憶體爆炸的情況,而這個計算時間往往會占據很長的時間(query和gallery比較大的情況),是以多個程式必須錯峰運作,為了自動進行可以使用下面的腳本。

#!/bin/bash
MIN_Value=`echo $1 | awk -F : '{print $1}'`
SEC_Value=`echo $1 | awk -F : '{print $2}'`
All_Sec=$[$MIN_Value*60+$SEC_Value]
for ((;All_Sec>=0;All_Sec--))
do
        MIN=$[All_Sec/60]
        SEC=$[All_Sec%60]
        echo  -ne "\rAfter  $MIN:$SEC is end!!"
        sleep 1
done
bash run.sh
           

運作時執行

bash myContab.sh 180:02

即可,:前是分鐘數,後面為秒數

4. 浏覽器倍數播放

浏覽器空白處右擊,選擇

檢查

, 然後

console

,輸入下面代碼後回車即可6倍數播放視訊。

5. 統計檔案大小

對于

window

下有時可能經常需要移動或者拷貝檔案,如果遇上檔案很大很多時可能導緻速度很慢,尤其是與

CV+NLP

碰撞時,可能有一個隐藏得很深的目錄下儲存了很多模型或者沒啥用的中間檔案(比如某個目錄下有幾萬張圖像),可能這些檔案并不重要,是可以删除的~。這時,讓我們從中找出那些可以删除的檔案其實很難(目錄太多,太深)。于是,我們可以統計一下每個檔案和目錄下的檔案大小,然後找到大的,檔案多的删除掉即可。腳本如下:

import os
f_dir = os.path.abspath(os.path.dirname(__file__))
def get_dir_size(filePath):
    dir_size = 0
    if os.path.isfile(filePath):
        dir_size = os.path.getsize(filePath)
    elif os.path.isdir(filePath):
        for root, dirs, files in os.walk(filePath):
            dir_size += sum([os.path.getsize(os.path.join(root, name)) for name in files])
    else:
        pass
    return dir_size
def get_dir_file_num(filePath, file_num):
    if os.path.isfile(filePath):
        file_num += 1
    elif os.path.isdir(filePath):
        for _ in os.listdir(filePath):
            new_path = os.path.join(filePath, _)
            file_num = get_dir_file_num(new_path, file_num)
    else:
        pass
    return file_num
if __name__ == '__main__':
    basePath = r"D:\Users\Desktop\*"
    total_size = 0
    total_dir_file_num = 0
    total_sizs_dict = []
    for path in os.listdir(basePath):
        path = os.path.join(basePath, path)
        size = get_dir_size(path) / 1024 / 1024
        dir_file_num = get_dir_file_num(path, 0)
        total_sizs_dict.append([size, path, dir_file_num])
        print('{} 占用記憶體為 {:0.2f} M, 共有 {} 個檔案'.format(path, size, dir_file_num))
        total_size += size
        total_dir_file_num += dir_file_num
    print("-" * 80)
    total_sizs_dict = sorted(total_sizs_dict, key=lambda x: x[0], reverse=True)  # 按照size大小排序
    topK = 10
    print("記憶體占用前 {} 的檔案為:".format(topK))
    for i in range(min(len(total_sizs_dict), topK)):
        print('Top-{} {} 占用記憶體為 {:0.2f} M, 共有 {} 個檔案'.format(i+1, total_sizs_dict[i][1], total_sizs_dict[i][0], total_sizs_dict[i][2]))
    print("-" * 80)
    print("{} 總共占用記憶體為 {:0.2f} M, 共有 {} 個檔案".format(basePath, total_size, total_dir_file_num))


           

basePath

填入需要統計的目錄即可,比如我的一個目錄如下:

一些常用腳本

大約11G,而且有一個目錄下有55w檔案,肯定是哪個資料集解壓了~

此外,還會給出前K大的目錄,友善使用者清理~

6. 删除目錄

有了上面統計某個目錄下有很多小檔案需要處理時(比如删除),對于windows來說,實在是一場災難,會出奇的慢。而linux相對來說就快很多,可以直接

rm -rf folder

解決,但其實多到一定程度,對linux也很難處理,是以才會有大資料進行中的海量小檔案處理的優化。我們這裡拉回win下删除小檔案,可以如下處理:

import shutil
import os

basePath = r"D:\Users\Desktop\*\reID"
for path in os.listdir(basePath):
    path = os.path.join(basePath, path)
    if os.path.isdir(path):
        print("正在删除 {}".format(path))
        shutil.rmtree(path)
    elif os.path.isfile(path):
        pass
    else:
        pass
           
一些常用腳本

其中,隻需要在

basePath

中填入需要處理的目錄即可,比如上圖這裡填入

.../reID

,就可以删除

Duke

Market

兩個目錄,而壓縮包不會删除。雖然以上腳本處理的速度也還是挺慢的,但相對win的操作已經快很多了,因為這個慢是大資料面臨的海量小檔案的問題。而且是腳本是和linux那樣把這塊記憶體變成可寫入 ,而不是像win那樣放進資源回收筒(沒有删除,記憶體并沒有空出來,隻有清除資源回收筒才會将這塊記憶體變成可寫入)

7. 讀寫txt

## 1. 一次性讀全部内容:一次性讀取文本中全部的内容,以字元串的形式傳回結果
with open("test.txt", "r") as f:  # 打開檔案
    data = f.read()  # 讀取檔案
    print(data)
    
## 2. 讀取第一行内容:隻讀取文本第一行的内容,以字元串的形式傳回結果
with open("test.txt", "r") as f:
    data = f.readline()
    print(data)

## 3. 清單:讀取文本所有内容,并且以數列的格式傳回結果,一般配合for in使用
with open("test.txt", "r") as f:
    for line in f.readlines():
        line = line.strip('\n')  # readlines會讀到換行符,去掉清單中每一個元素的換行符
        print(line)

## 4. 寫入txt文本
with open("test.txt","w") as f:
    f.write("這是個測試!")  # 自帶檔案關閉功能,不需要再寫f.close()
    
## 讀寫模式
"""
r :   讀取檔案,若檔案不存在則會報錯
w:   寫入檔案,若檔案不存在則會先建立再寫入,會覆寫原檔案
a :   寫入檔案,若檔案不存在則會先建立再寫入,但不會覆寫原檔案,而是追加在檔案末尾
rb,wb:  分别與r,w類似,但是用于讀寫二進制檔案
r+ :   可讀、可寫,檔案不存在也會報錯,寫操作時會覆寫
w+ :   可讀,可寫,檔案不存在先建立,會覆寫
a+ :  可讀、可寫,檔案不存在先建立,不會覆寫,追加在末尾
"""
           

8. 壓縮檔案到zip

import os

def writeAllFileToZip(filePath, zipFile):
    # 定義一個函數,遞歸讀取filePath檔案夾中所有檔案,并塞進zipFile檔案中,參數absDir表示檔案夾的絕對路徑
    for f in os.listdir(filePath):
        file = os.path.join(filePath, f)
        print(file, "壓縮完成")
        if os.path.isdir(file):  # 判斷是檔案夾,繼續深度讀取
            zipFile.write(file)  # 在zip檔案中建立檔案夾
            writeAllFileToZip(file, zipFile)  # 遞歸操作
        else:  # 判斷是普通檔案,直接寫到zip檔案中
            zipFile.write(file)
    return
           

9. 遠端檔案傳輸

import paramiko  # 用于調用scp指令
from scp import SCPClient

def fileTransfer(local_path, remote_path, direction):
    """
    進行兩台機器之間的檔案傳輸
    :param local_path: 本地檔案路徑
    :param remote_path: 遠端伺服器檔案路徑
    :param direction: ['get', 'put'] 前者表示将remote_path傳輸到local_path, 後者則是将local_path傳輸到remote_path
    :return:
    """
    # 參數
    host = "*"  # 遠端linux伺服器ip位址
    port = * # 端口号
    username = "*"  # 遠端伺服器使用者名
    password = "*"  # 遠端伺服器密碼

    # 執行個體化SSHClient
    ssh_client = paramiko.SSHClient()
    # 自動添加政策,儲存伺服器的主機名和密鑰資訊,如果不添加,那麼不再本地know_hosts檔案中記錄的主機将無法連接配接
    ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    # 連接配接SSH服務端,以使用者名和密碼進行認證
    ssh_client.connect(host, port, username, password)

    # 傳輸檔案/檔案夾
    scpclient = SCPClient(ssh_client.get_transport(), socket_timeout=60.0)
    assert direction in ['get', 'put']
    try:
        if direction == 'put':
            scpclient.put(local_path, remote_path, True)
        else:
            scpclient.get(remote_path, local_path, True)
    except FileNotFoundError:
        if direction == 'put':
            print("上傳失敗:" + local_path)
        else:
            print("下載下傳失敗:" + remote_path)
    else:
        if direction == 'put':
            print("上傳成功:" + local_path)
        else:
            print("下載下傳成功:" + remote_path)

    # # 執行cmd裡的指令,stdout 為正确輸出,stderr為錯誤輸出
    # cmd = "touch ok.py;chmod +x ok.py"
    # stdin, stdout, stderr = ssh_client.exec_command(cmd)
    # print(stdout.read().decode("utf-8"))

    # 關閉SSHClient
    ssh_client.close()
           

10. 格式化輸出

a = 2.63
b = 2
print("{:02d} {:03d} {:6.2f} {:6.3f} {:3.1f}".format(b, b, a, a, a))

"""
分别表示: 
  - 兩位整數(不足用0補充)
  - 三位整數(不足用0補充)
  - 六位小數(小數點後占兩位,小數點前不足用空格補充,小數點後不足用0做精确)
  - 六位小數(小數點後占三位,小數點前不足用空格補充,小數點後不足用0做精确)
  - 三位小數(小數點後占一位,小數點前不足用空格補充,小數點後不足用0做精确)
"""

# 輸出:
"02 002 __2.63 _2.630 2.6"
# 注意其中_表示格式化輸出占據的空格
           

11. conda虛拟環境

# 檢視目前存在的虛拟環境
conda env list   # 方法1
conda info -e    # 方法2
# 建立虛拟環境
conda create -n ENV_NAME python=X.X    # ENV_NAME是環境名, X.X是python版本
# 激活指定虛拟環境
source activate ENV_NAME  # Linux
activate ENV_NAME         # Win
# 關閉虛拟環境
source deactivate ENV_NAME   # Linux
deactivate ENV_NAME          # Win
# 删除虛拟環境
conda remove -n ENV_NAME --all
           

12. 檢視python-class的屬性

有時我們調用第三方

py

包的時候會遇到不知道其封裝的某個類有哪些屬性,恰好有無法檢視其源碼(如

pyd, pyc

等檔案)而且

class

還沒寫

__doc__

幫助文檔時,可以用下面的方法檢視其屬性。

# -------------------------------
# 假設這部分源碼不可見哈.......
class testClass(object):
    class_var = 1
    def __init__(self):
        self.name = 'xy'
        self.age = 2

    @property
    def num(self):
        return self.age + 10

    def fun(self):
    	pass
    def static_f():
    	pass
    def class_f(cls)
    	pass
# -------------------------------


# 方法1: `__dict__`
testClassInstance = testClass()
print(testClassInstance.__dict__)   #{'age': 2, 'name': 'xy'}   執行個體的__dict__屬性
print(testClass.__dict__)    
"""
# 類testClass的__dict__屬性
{
	'__dict__': <attribute '__dict__' of 'A' objects>, # 這裡如果想深究的話檢視參考連結
	'__module__': '__main__',               # 所處子產品
	'num': <property object>,               # 特性對象 
	'class_f': <function class_f>,          # 類方法
	'static_f': <function static_f>,        # 靜态方法
	'class_var': 1, 'fun': <function fun >, # 類變量
	'__weakref__': <attribute '__weakref__' of 'A' objects>, 
	'__doc__': None,                        # class說明字元串
	'__init__': <function __init__ at 0x0000000003451AC8>}
"""

# 方法2: `dir()`
print(dir(testClass))
"""
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'class_f', 'class_var', 'fun', 'level1', 'level2', 'name', 'num', 'static_f']
"""

# 兩者差別(`testClass.__dict__` vs `dir(testClass)`):
# 1. 執行個體的`__dict__`僅存儲與該執行個體相關的執行個體屬性,每個執行個體的執行個體屬性互不影響
# 2. 類的`__dict__`存儲所有執行個體共享的變量和函數(類屬性,方法等),類的__dict__并不包含其父類的屬性
# 3. `dir()`函數會自動尋找一個對象的所有屬性(包括其父類`object`的),`__dict__`中的屬性是`dir()`的子集
           

13.

numpy

不使用省略号和科學計數法表示

  • 一般在

    debug

    的時候可能用的比較多
  • 解決方法:
    • 一般在數組次元較大時,顯示時就會使用省略号代替中間結果。正确顯示的解決方法是:

      np.set_printoptions(threshold = np.inf)

    • 還有一個問題是數組裡面的數值會以科學計數法表示。正常顯示的解決方法是:

      np.set_printoptions(suppress = True)

14.

opencv

讀取中文路徑的圖檔報錯問題

img = cv2.imdecode(np.fromfile(img_path, dtype=np.uint8), -1)  # 讀取
cv2.imencode('.png', img)[1].tofile(img_path)  # 儲存
           

15.

opencv

監聽滑鼠點選繪制矩形

import cv2
import os
import numpy as np


def OnMouseAction(event, x, y, flags, param):
    global img, file, position1, position2
    if event == cv2.EVENT_LBUTTONDOWN:  # 按下左鍵
        position1 = (x, y)
        position2 = None
    elif event == cv2.EVENT_MOUSEMOVE and flags == cv2.EVENT_FLAG_LBUTTON:  # 按住左鍵拖曳不放開
        position2 = (x, y)
    elif event == cv2.EVENT_LBUTTONUP:  # 放開左鍵
        position2 = (x, y)
        if position1 is not None and position2 is not None:
            cv2.rectangle(img, position1, position2, (0, 0, 255), 1, 4)
            x1, y1, x2, y2 = position1[0], position1[1], position2[0], position2[1]
            img[y1:y2, x1:x2, :] = np.concatenate([np.zeros((y2-y1, x2-x1, 3)), np.ones((y2-y1, x2-x1, 1))*255], axis=-1)  # 繪制區域塗黑(channel用4通道是因為我讀入的是png圖像)
        cv2.imshow('image', img)


if __name__ == '__main__':
    cv2.namedWindow('image')
    position1, position2 = None, None
    cv2.setMouseCallback('image', OnMouseAction)
    img = cv2.imread(img_path)
    cv2.imshow('image', img)
    cv2.waitKey()
    cv2.destroyAllWindows()
           

繼續閱讀