不會(huì)吧?不會(huì)吧?不會(huì)吧?不會(huì)有人忘記我還會(huì)寫(xiě)圖像處理的代碼吧?別說(shuō)了,我知道你忘了,沒(méi)關(guān)系,我會(huì)在這篇文章寫(xiě)一些很簡(jiǎn)短的代碼實(shí)現(xiàn)常見(jiàn)的圖像處理工作。
如何提取深度圖像的邊緣信息?
Sobel算子:Sobel算子是一種基于圖像梯度的邊緣檢測(cè)算法,可以在x方向和y方向上計(jì)算圖像的梯度,然后將兩個(gè)梯度值合并成一個(gè)邊緣強(qiáng)度值。通常可以使用Sobel算子來(lái)檢測(cè)深度圖像中的水平和垂直邊緣。 Scharr算子是一種改進(jìn)的Sobel算子,它使用了更大的卷積核來(lái)平滑圖像,并在計(jì)算梯度時(shí)使用更準(zhǔn)確的權(quán)重。Scharr算子在處理低對(duì)比度圖像時(shí)表現(xiàn)更好。 Laplacian算子:Laplacian算子是一種基于二階微分的邊緣檢測(cè)算法,可以檢測(cè)出深度圖像中的較強(qiáng)的邊緣。該算子計(jì)算圖像的拉普拉斯變換,并尋找其中的極值點(diǎn)作為邊緣點(diǎn)。 深度邊緣檢測(cè)算法:除了基于梯度或微分的算法,還有一些專門針對(duì)深度圖像的邊緣檢測(cè)算法。這些算法通常利用深度圖像的信息來(lái)檢測(cè)物體表面的變化,例如深度跳變或斜率變化等。 Canny算子是一種廣泛使用的邊緣檢測(cè)算法,它采用了多步驟的邊緣檢測(cè)過(guò)程。首先,使用高斯濾波器平滑圖像,然后計(jì)算圖像的梯度和梯度方向。接下來(lái),應(yīng)用非極大值抑制和雙閾值處理來(lái)提取邊緣。最后,通過(guò)連接具有強(qiáng)度邊緣的像素來(lái)獲得完整的邊緣。Canny算子在抑制噪聲和保留真實(shí)邊緣方面表現(xiàn)良好,通常被認(rèn)為是一種比Sobel算子更優(yōu)秀的邊緣檢測(cè)算法。 如何使用Python實(shí)現(xiàn)一個(gè)抽幀算法? 為啥會(huì)有這種東西?原因就是因?yàn)閳D像幀太多又不需要都處理~
import cv2 def extract_frames(video_path, interval): # 打開(kāi)視頻文件 cap = cv2.VideoCapture(video_path) # 計(jì)算視頻總幀數(shù)和幀率 frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) fps = cap.get(cv2.CAP_PROP_FPS) # 計(jì)算抽幀間隔 interval_frames = int(interval * fps) # 初始化幀計(jì)數(shù)器和關(guān)鍵幀列表 count = 0 frames = [] # 逐幀遍歷視頻 while True: ret, frame = cap.read() if not ret: break count += 1 # 如果是關(guān)鍵幀,將其添加到關(guān)鍵幀列表中 if count % interval_frames == 0: frames.append(frame) # 關(guān)閉視頻文件 cap.release() ????return?frames? 照指定的時(shí)間間隔從視頻中抽取關(guān)鍵幀 上述代碼中,extract_frames()函數(shù)接受視頻文件路徑和抽幀間隔作為輸入參數(shù),返回一個(gè)包含關(guān)鍵幀的列表。在函數(shù)內(nèi)部,首先使用cv2.VideoCapture()函數(shù)打開(kāi)視頻文件,并使用cv2.CAP_PROP_FRAME_COUNT和cv2.CAP_PROP_FPS獲取視頻總幀數(shù)和幀率。然后,根據(jù)指定的抽幀間隔計(jì)算需要保留的關(guān)鍵幀,在逐幀遍歷視頻時(shí)根據(jù)幀計(jì)數(shù)器來(lái)判斷當(dāng)前幀是否為關(guān)鍵幀,如果是,則將其添加到關(guān)鍵幀列表中。最后,使用cap.release()函數(shù)關(guān)閉視頻文件。 可以使用以下代碼調(diào)用extract_frames()函數(shù)來(lái)從視頻文件中抽取關(guān)鍵幀:
frames = extract_frames('video.mp4', 1) # 抽取間隔為1秒的關(guān)鍵幀 for frame in frames: cv2.imshow('frame', frame) cv2.waitKey(0) cv2.destroyAllWindows()? 上述代碼將抽取間隔設(shè)置為1秒,然后遍歷返回的關(guān)鍵幀列表,使用cv2.imshow()函數(shù)顯示每個(gè)關(guān)鍵幀,并在用戶按下鍵盤后繼續(xù)顯示下一個(gè)關(guān)鍵幀。最后,使用cv2.destroyAllWindows()函數(shù)關(guān)閉所有顯示窗口。 讓我們使用一個(gè)算子來(lái)提取深度圖像的邊緣信息的函數(shù): Sobel算子是一種常用的邊緣檢測(cè)算子,它利用圖像的灰度值變化來(lái)檢測(cè)邊緣。
import cv2 import numpy as np def extract_depth_edges(depth_img): # 計(jì)算Sobel算子的卷積核 sobelx = cv2.Sobel(depth_img, cv2.CV_64F, 1, 0, ksize=3) sobely = cv2.Sobel(depth_img, cv2.CV_64F, 0, 1, ksize=3) # 計(jì)算梯度幅值和方向 grad_mag = np.sqrt(sobelx ** 2 + sobely ** 2) grad_dir = np.arctan2(sobely, sobelx) # 將梯度幅值歸一化到0-255之間 grad_mag_norm = cv2.normalize(grad_mag, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U) # 將梯度方向轉(zhuǎn)換為角度 grad_dir_deg = (grad_dir * 180 / np.pi) % 180 # 應(yīng)用非極大值抑制 grad_mag_nms = cv2.Canny(grad_mag_norm, 100, 200) ????return?grad_mag_nms? 上述代碼中,extract_depth_edges()函數(shù)接受深度圖像作為輸入?yún)?shù),返回提取的邊緣信息。在函數(shù)內(nèi)部,首先使用cv2.Sobel()函數(shù)計(jì)算x和y方向上的Sobel算子的卷積核,然后計(jì)算梯度幅值和方向。接下來(lái),將梯度幅值歸一化到0-255之間,并將梯度方向轉(zhuǎn)換為角度。最后,應(yīng)用非極大值抑制(Canny邊緣檢測(cè)算法)來(lái)提取邊緣信息,并返回結(jié)果。 可以使用以下代碼調(diào)用extract_depth_edges()函數(shù)來(lái)提取深度圖像的邊緣信息:
depth_img = cv2.imread('depth_image.png', cv2.IMREAD_GRAYSCALE) edges = extract_depth_edges(depth_img) cv2.imshow('depth_edges', edges) cv2.waitKey(0) cv2.destroyAllWindows()? 上述代碼將讀取深度圖像并將其作為輸入?yún)?shù)傳遞給extract_depth_edges()函數(shù),然后顯示提取的邊緣信息。 有時(shí)候會(huì)有這樣的需求,把提取的圖像邊緣保存在一個(gè)txt文件中: 假設(shè)我們已經(jīng)提取了深度圖像的邊緣信息,存儲(chǔ)在名為edge_img的NumPy數(shù)組中,邊緣值的范圍在0到255之間。
import numpy as np # 假設(shè)我們已經(jīng)提取了深度圖像的邊緣信息,存儲(chǔ)在名為edge_img的NumPy數(shù)組中 # 將邊緣值縮放到0到1之間 edge_img = edge_img / 255.0 # 將邊緣信息轉(zhuǎn)換為字符串格式 edge_str = np.array2string(edge_img, separator=',', formatter={'float_kind':lambda x: "%.5f" % x}) # 將字符串寫(xiě)入txt文件 with open('edge_info.txt', 'w') as f: ????f.write(edge_str)在上面的代碼中,我們將邊緣值縮放到0到1之間,并將其轉(zhuǎn)換為字符串格式。我們使用NumPy的array2string函數(shù)將數(shù)組轉(zhuǎn)換為字符串,并使用逗號(hào)作為分隔符。我們還設(shè)置了formatter參數(shù),將浮點(diǎn)數(shù)的小數(shù)位數(shù)限制為5位。最后,我們將字符串寫(xiě)入名為edge_info.txt的txt文件中。 請(qǐng)注意,在讀取txt文件時(shí),需要使用適當(dāng)?shù)拇a將字符串轉(zhuǎn)換回NumPy數(shù)組格式。 雖然一直寫(xiě)不了長(zhǎng)代碼,但是不妨礙我寫(xiě)在一起: 接下來(lái)把抽幀算法和保存邊緣到txt的函數(shù)寫(xiě)在一起
import cv2 import numpy as np def extract_edge(frame, threshold): # 使用高斯模糊平滑圖像 blurred = cv2.GaussianBlur(frame, (3, 3), 0) # 轉(zhuǎn)換為灰度圖像 gray = cv2.cvtColor(blurred, cv2.COLOR_BGR2GRAY) # 使用Canny算法提取邊緣 edges = cv2.Canny(gray, threshold, threshold * 2) return edges def save_edges_to_txt(edges, filename): # 將邊緣值縮放到0到1之間 edges = edges / 255.0 # 將邊緣信息轉(zhuǎn)換為字符串格式 edge_str = np.array2string(edges, separator=',', formatter={'float_kind':lambda x: "%.5f" % x}) # 將字符串寫(xiě)入txt文件 with open(filename, 'w') as f: f.write(edge_str) # 讀取深度圖像 depth_img = cv2.imread('depth_img.png') # 指定抽幀間隔 interval = 10 # 提取深度圖像邊緣 edges = extract_edge(depth_img, 50) # 抽幀,保留每隔interval個(gè)像素 sampled_edges = edges[::interval, ::interval] # 將邊緣信息保存到txt文件中 save_edges_to_txt(sampled_edges,?'edge_info.txt')在上面的代碼中,我們定義了一個(gè)extract_edge函數(shù)來(lái)提取深度圖像的邊緣,該函數(shù)使用高斯模糊平滑圖像并使用Canny算法提取邊緣。我們還定義了一個(gè)save_edges_to_txt函數(shù),將邊緣信息保存到txt文件中。 在主函數(shù)中,我們首先讀取深度圖像,然后指定抽幀間隔。我們使用extract_edge函數(shù)提取深度圖像邊緣,并使用抽幀算法保留每隔interval個(gè)像素。最后,我們使用save_edges_to_txt函數(shù)將提取的邊緣信息保存到txt文件中。 應(yīng)該是可以直接運(yùn)行的,如果運(yùn)行不了你再改改? 上面鄙人已經(jīng)教了你把圖像轉(zhuǎn)換成txt的文件,如何把保存在txt文件里面的邊緣信息恢復(fù)成圖像呢? 你會(huì)不?
import cv2 import numpy as np def load_edges_from_txt(filename, shape): # 從txt文件中讀取邊緣信息 edge_str = np.loadtxt(filename, delimiter=',') # 創(chuàng)建全零數(shù)組 edges = np.zeros(shape) # 將邊緣信息復(fù)制到全零數(shù)組的對(duì)應(yīng)位置上 np.put(edges, np.arange(shape[0]*shape[1]), edge_str) # 對(duì)全零數(shù)組進(jìn)行插值操作 edges = cv2.resize(edges, (shape[1], shape[0])) # 對(duì)插值后的邊緣圖像進(jìn)行二值化處理 ret, edges = cv2.threshold(edges, 0, 255, cv2.THRESH_BINARY) return edges # 讀取深度圖像 depth_img = cv2.imread('depth_img.png') # 獲取深度圖像大小 height, width = depth_img.shape[:2] # 從txt文件中加載邊緣信息,并恢復(fù)成圖像 edges = load_edges_from_txt('edge_info.txt', (height//10, width//10)) # 顯示原始深度圖像和恢復(fù)的邊緣圖像 cv2.imshow('depth_img', depth_img) cv2.imshow('edges', edges) cv2.waitKey(0) cv2.destroyAllWindows()? ? 在上面的代碼中,我們定義了一個(gè)load_edges_from_txt函數(shù),該函數(shù)從txt文件中加載邊緣信息,并將其恢復(fù)成圖像。該函數(shù)首先使用numpy.loadtxt函數(shù)從文件中加載數(shù)據(jù),并將其轉(zhuǎn)換為NumPy數(shù)組。然后,該函數(shù)根據(jù)指定的圖像大小創(chuàng)建一個(gè)全零數(shù)組,并使用numpy.put函數(shù)將邊緣信息數(shù)組的值復(fù)制到全零數(shù)組的對(duì)應(yīng)位置上。接下來(lái),該函數(shù)對(duì)全零數(shù)組進(jìn)行插值操作,并使用cv2.threshold函數(shù)對(duì)插值后的邊緣圖像進(jìn)行二值化處理,生成二值圖像。 最后一個(gè)代碼,把1000x1000的圖像信息轉(zhuǎn)換到10x10的圖像里面,應(yīng)該怎么做? 使用圖像縮放操作。可以使用OpenCV中的cv2.resize函數(shù)對(duì)原始圖像進(jìn)行縮放操作。該函數(shù)的輸入?yún)?shù)包括原始圖像、目標(biāo)圖像大小和插值方法等。
import cv2 # 讀取原始圖像 img = cv2.imread('original_image.png') # 縮放圖像 new_img = cv2.resize(img, (10, 10), interpolation=cv2.INTER_AREA) # 顯示原始圖像和縮放后的圖像 cv2.imshow('original', img) cv2.imshow('new', new_img) cv2.waitKey(0) cv2.destroyAllWindows()在上面的代碼中,我們使用cv2.imread函數(shù)讀取原始圖像,然后使用cv2.resize函數(shù)對(duì)原始圖像進(jìn)行縮放操作,將其縮放為10x10的圖像。在cv2.resize函數(shù)中,我們將目標(biāo)圖像大小設(shè)置為(10, 10),并將插值方法設(shè)置為cv2.INTER_AREA。最后,我們使用cv2.imshow函數(shù)顯示原始圖像和縮放后的圖像。 ?
?
?
評(píng)論
查看更多