最近在學(xué)習(xí)OpenCV,在自己實(shí)現(xiàn)rober,LOG等算子卷積的時候,遇到了一些坑,先上代碼
import cv2
import numpy as np
import os
import copy
# 邊緣檢測
filename = os.path.abspath('第二次作業(yè)/lena.jpg')
lena = cv2.imread(filename)
# 實(shí)現(xiàn)robert,Sobel和LOG邊緣提取
gray = cv2.imread(os.path.abspath('第二次作業(yè)/bear.jpg'),0)
gray = cv2.cvtColor(lena,cv2.COLOR_BGR2GRAY)
w,h = gray.shape
robert_img = gray.copy()
sobel_img = gray.copy()
log_img = gray.copy()
robertx,roberty = np.array([-1,-1,1,1]),np.array([0,1,-1,0])
sobel = np.array([1,0,-1,2,0,-2,1,0,-1])
LOG = np.array([-2,-4,-4,-4,-2,-4,0,8,0,-4,-4,8,24,8,-4,-4,0,8,0,-4,-2,-4,-4,-4,-2])
for i in range(w-2):
for j in range(h-2):
origin1 = np.array(gray[i:i+2,j:j+2]).flatten()
origin2 = np.array(gray[i:i+3,j:j+3]).flatten()
origin3 = np.array(gray[i:i+5,j:j+5]).flatten()
value = abs(origin1.dot(robertx))
robert_img[i][j] = value
if value != robert_img[i,j]:
print(value,'-----',robert_img[i,j])
sobel_img[i][j] = origin2.dot(sobel)
if(i<w-4 and j<h-4):
value = origin3.dot(LOG)
log_img[i][j] = value
cv2.imshow('origin',gray)
cv2.imshow('robert',robert_img)
cv2.imshow('sobel',sobel_img)
cv2.imshow('LOG',log_img)
cv2.imshow('sobelopen',cv2.Sobel(gray,cv2.CV_64F,1,0,ksize=3))
cv2.waitKey()
cv2.destroyAllWindows()
在建立新的圖片用于存儲計算好的結(jié)果時,我使用了如下方法
robert_img = gray.copy()
sobel_img = gray.copy()
log_img = gray.copy()
結(jié)果顯示如下:

可以看到LOG算子的計算結(jié)果明顯不對,使用如下方式創(chuàng)建圖像:
robert_img = np.empty(gray.shape)
sobel_img = np.empty(gray.shape)
log_img = np.empty(gray.shape)
結(jié)果如下:

再換成如下方式:
robert_img = np.empty_like(gray)
sobel_img = np.empty_like(gray)
log_img = np.empty_like(gray)

結(jié)果和第一個一樣的
只是改變了創(chuàng)建圖片的方式,怎么結(jié)果就這么不一樣呢?
經(jīng)過仔細(xì)的調(diào)試,發(fā)現(xiàn),由于不同算子經(jīng)過卷積計算后,結(jié)果的值不一定在[0,255]之間,所以在將結(jié)果賦值給創(chuàng)建的數(shù)據(jù)時,會將結(jié)果自動轉(zhuǎn)成數(shù)據(jù)的類型,而轉(zhuǎn)換的結(jié)果并不是我們想要的,所以出現(xiàn)了錯誤。
這里就提一下np.empty()和np.empty_like()的區(qū)別,前者如果沒有指定數(shù)據(jù)類型(參數(shù)dtype進(jìn)行設(shè)置),那么會默認(rèn)為浮點(diǎn)類型,而后者需要傳入一個存在的數(shù)據(jù),所以新建的數(shù)據(jù)會設(shè)定成這個數(shù)據(jù)的類型。
那么初始化為浮點(diǎn)的類型,最后結(jié)果為什么顯示是錯誤的呢?這里就是imshow函數(shù)的一個坑,我們先看看這個函數(shù)在python中的注釋:

大概翻譯一下,就是說如果數(shù)據(jù)類型是uint8,那么將直接顯示,如果是16位或者32位整型,那么將該值除以256,得到[0,255]的范圍進(jìn)行顯示,如果是一個浮點(diǎn)數(shù),那么將其乘以255,那么[0,1]就轉(zhuǎn)換成[0,255]的值進(jìn)行顯示。
所以,就出現(xiàn)了之前代碼出現(xiàn)的問題。但是這里的描述仍然不夠詳盡,比如16位整型值是負(fù)的怎么處理,浮點(diǎn)數(shù)大于1的怎么處理,查了很多地方都沒有看看,如果有誰知道,麻煩告訴我一下。