不使用opencv的庫函數(shù),完全運(yùn)用numpy和線性代數(shù)知識,我們能在多大程度上處理圖像呢
多通道轉(zhuǎn)單通道
- 可以使用(r,g,b)=(1/3,1/3,1/3)來轉(zhuǎn)換
- 也可以使用(r,g,b)=(0.299,0.587,0.114)來轉(zhuǎn)換
原圖(pixiv id:490219):
兩種轉(zhuǎn)換方式結(jié)果對比
亮度、對比度
- x為像素矩陣
- 1為全1矩陣
- a越大,對比度越高
- b越大亮度越高
不同的a(0、0.4、0.8、1.2、1.6、2)情況下圖像的情況(不同b情況下略):
變換
-
水平縮放:對于m*n的原圖像,右乘k*m的矩陣A可以水平縮放為k*n的圖像。其中,A的縱列代表了縮放過程中對原圖像橫排每個(gè)像素的加權(quán),A的橫行代表了最終圖像橫排每個(gè)位的生成,原圖像的縱列代表了最終圖像縱列每個(gè)位的生成。在縮放過程中,最終圖像橫排每個(gè)位的生成次數(shù)應(yīng)該和原圖像不同,并且每次生成時(shí)應(yīng)該對原圖像橫排不同像素進(jìn)行加權(quán)
- 臨近插值放大一倍的矩陣A:
- 線性插值放大一倍的矩陣A:
- 臨近插值放大一倍的矩陣A:
-
垂直縮放:對于m*n的原圖像,左乘n*k的矩陣A可以垂直縮放為m*k的圖像。其中,A的橫行代表了縮放過程中對原圖像縱列每個(gè)像素的加權(quán),A的縱列代表了最終圖像縱列每個(gè)位的生成,原圖像的橫行代表了最終圖像橫行每個(gè)位的生成。在縮放過程中,最終圖像縱列每個(gè)位的生成次數(shù)應(yīng)該和原圖像不同,并且每次生成時(shí)應(yīng)該對原圖像縱列不同像素進(jìn)行加權(quán)
- 臨近插值放大一倍的矩陣A:
- 線性插值放大一倍的矩陣A:
- 臨近插值放大一倍的矩陣A:
旋轉(zhuǎn)、翻轉(zhuǎn):矩陣旋轉(zhuǎn)與轉(zhuǎn)置
偏移:切片與連接
常用圖層混合模式
設(shè)像素矩陣位于像素矩陣
之上且范圍比y小,坐標(biāo)
正常:
-
變暗系
- 變暗:
,分通道比較替換
- 正片疊底:
- 顏色加深:
- 線性加深:
- 深色:
,比較全通道數(shù)值
- 變暗:
-
變亮系
- 變亮:
,分通道比較替換
- 濾色:
- 顏色減淡:
- 線性減淡:
- 淺色:
,比較全通道數(shù)值
- 變亮:
- 疊加:根據(jù)y的數(shù)值來決定使用正片疊底還是濾色,盡量保留y的顏色
-
照射系
- 柔光:對x數(shù)值高的部分保留較好,
- 強(qiáng)光:根據(jù)x的數(shù)值來決定使用正片疊底還是濾色,盡量保留x的顏色
- 亮光:盡量保留x的顏色,x亮度高時(shí)圖像將被降低對比度并且變亮,x亮度低時(shí)圖像會(huì)被提高對比度并且變暗
- 線性光:根據(jù)x的數(shù)值來決定使用線性加深還是線性減淡,盡量保留x的顏色
- 柔光:對x數(shù)值高的部分保留較好,
模糊
生成一個(gè)的模糊用卷積核x,與原圖像y進(jìn)行卷積運(yùn)算,即
結(jié)果的每個(gè)ij位置都相當(dāng)于原圖像ij位置左上范圍內(nèi)的像素加權(quán)和,因此可以實(shí)現(xiàn)模糊
高斯核
生成原理很簡單,對于每個(gè)位置計(jì)算其與右下角橫縱距離,代入二維高斯函數(shù)即可。sigma越大模糊程度越大
def gaussian(u, v, sigma):
pi = 3.1416
intensity = 1 / (2.0 * pi * sigma * sigma) * math.exp(- 1 / 2.0 * ((u ** 2) + (v ** 2)) / (sigma ** 2))
return intensity
def gaussianKernel(r, sigma):
kernel = np.zeros([r, r])
for i in range(r):
for j in range(r):
kernel[i, j] = gaussian(r-i,r-j, sigma)
kernel /= np.sum(np.sum(kernel))
return kernel
運(yùn)動(dòng)模糊核
運(yùn)動(dòng)模糊的原理是一個(gè)方向上的圖像疊加,給出運(yùn)動(dòng)模糊的角度和程度,需要生成一個(gè)權(quán)重和為1的核。對于向右下、左下、右上、左上運(yùn)動(dòng),核分別越靠近左上、右上、左下、右下的權(quán)重最大,其中對角線上r個(gè)格子中有值
設(shè)首項(xiàng)等于公差,解方程,得
生成核時(shí),從根據(jù)不同的弧度情況,從左上、右上、左下、右下分別開始向著弧度對應(yīng)的方向?qū)ふ襯個(gè)格并在對應(yīng)點(diǎn)計(jì)算等差數(shù)列對應(yīng)項(xiàng),如果該格已經(jīng)有值,則在其旁邊一格設(shè)置項(xiàng)
def mbkernel(agl,r):
rad=math.radians(agl)
w=math.ceil(r*np.abs(np.cos(rad)))
h=math.ceil(r*np.abs(np.sin(rad)))
if h==0:
h+=1
if w==0:
w+=1
d=2/(r+r**2)
kernel=np.zeros((int(h),int(w)))
for i in range(0,r):
if np.sin(rad)>=0:
y=math.floor(h-i*np.sin(rad)-1)
else:
y=math.floor(-i*np.sin(rad))
if np.cos(rad)>=0:
x=math.floor(i*np.cos(rad))
else:
x=math.floor(w+i*np.cos(rad)-1)
if kernel[y][x]==0:
kernel[y][x]=(i+1)*d
else:
if np.sin(rad)>=0:
kernel[y+1][x]=(i+1)*d
else:
kernel[y-1][x]=(i+1)*d
return kernel
卷積
opencv提供了卷積函數(shù)
dst=cv2.filter2D(src,-1,kernel=k,anchor=(-1,-1)) #anchor默認(rèn)位于中間,深度為-1默認(rèn)保持和原圖像相同深度
dst=cv2.filter2D(src,-1,kernel=flip(k),anchor=(k.cols-1,k.rows-1)) #真正意義上的卷積,深度為-1默認(rèn)保持和原圖像相同深度
不使用opencv提供的卷積函數(shù),同樣可以手寫卷積,但運(yùn)行速度極慢
半徑(縱軸)分別為5、15、30下,(橫軸)分別為10、50、100下高斯模糊的結(jié)果
半徑(縱軸)分別為15、50、100下,角度(橫軸)分別為、
、
下運(yùn)動(dòng)模糊的結(jié)果
差分
水平差分
對于m*n的原圖像,右乘m*m的矩陣A可以進(jìn)行水平差分。其中,A的縱列代表了縮放過程中對原圖像橫排每個(gè)像素的加權(quán),A的橫行代表了最終圖像橫排每個(gè)位的生成,原圖像的縱列代表了最終圖像縱列每個(gè)位的生成。在差分過程中,最終圖像橫排每個(gè)位的生成次數(shù)應(yīng)該和原圖像相同,并且每次生成時(shí)應(yīng)該對原圖像橫排不同像素進(jìn)行加權(quán)(某個(gè)像素為1,其下一個(gè)像素為-1)
- 矩陣A:
垂直差分
對于m*n的原圖像,左乘n*n的矩陣A可以進(jìn)行垂直差分。其中,A的橫行代表了縮放過程中對原圖像縱列每個(gè)像素的加權(quán),A的縱列代表了最終圖像縱列每個(gè)位的生成,原圖像的橫行代表了最終圖像橫行每個(gè)位的生成。在縮放過程中,最終圖像縱列每個(gè)位的生成次數(shù)應(yīng)該和原圖像不同,并且每次生成時(shí)應(yīng)該對原圖像縱列不同像素進(jìn)行加權(quán)(某個(gè)像素為1,其下一個(gè)像素為-1)
- 矩陣A:
圖像復(fù)原
對我們的圖像添加10%椒鹽噪聲
base = cv2.imread("base.jpg",cv2.IMREAD_GRAYSCALE)
noise=np.random.randint(0,100,base.shape)
base=np.where(noise<5,0,base)
base=np.where(noise>=95,255,base)
添加前:
添加后:
均值濾波
設(shè)半徑為r,均值濾波核是一個(gè),每個(gè)位置值為
的方陣
kernel=np.zeros((r,r))
kernel+=1/r/r
分別使用半徑為3、5、15的核濾波結(jié)果
中值濾波
設(shè)半徑為r,中值濾波核在圖像上滑動(dòng),每個(gè)位置統(tǒng)計(jì)中值
def med(inputs,R):
H, W = inputs.shape
result = np.zeros(inputs.shape)
H, W = inputs.shape
for r in range(0, H - R + 1):
for c in range(0, W - R + 1):
cur = inputs[r:r + R,c:c + R]
val = np.median(cur)
result[r, c] = val
return result
分別使用半徑為3、5、15的核濾波結(jié)果