1、按位取反bitwise_not()
按位取反就是將數(shù)值根據(jù)每個(gè)bit位1變0,0變1,比如0xf0按位取反就變成了0x0f,如果是uint8類型的數(shù)據(jù),取反前后的數(shù)據(jù)相加結(jié)果為0xff(255)。下面的例子將lena.jpg和opencv-logo.png分別按位取反:
import cv2
print('cv2.__version__:',cv2.__version__)
img1 = cv2.imread('..\\lena.jpg')
img2 = cv2.imread('..\\opencv-logo.png' )
img_ret1 = cv2.bitwise_not(img1)
print('img1[161,199]: ',img1[161,199])
print('img_ret1[161,199]:',img_ret1[161,199])
cv2.imshow('lena-not-juzicode',img_ret1)
img_ret2 = cv2.bitwise_not(img2)
print('img2[100,200]: ',img2[100,200])
print('img_ret2[100,200]:',img_ret2[100,200])
cv2.imshow('logo-not-juzicode',img_ret2)
cv2.waitKey(0)
運(yùn)行結(jié)果:
cv2.__version__: 4.5.2
img1[161,199]: [109 105 201]
img_ret1[161,199]: [146 150 54]
img2[100,200]: [ 0 0 255]
img_ret2[100,200]: [255 255 0]
比如lena.jpg的像素點(diǎn)[161,199] B通道的值為109(0110 1101),取反后的值為146(1001 0010),轉(zhuǎn)換為二進(jìn)制后觀察到每個(gè)bit為如果為0就變成1、如果為1就變成0:
上圖中左側(cè)lena這種圖像是不是有種似曾相識(shí)的感覺?能回憶起來是啥子?xùn)|西的朋友就要暴露年齡了,二十年前流行的膠片相機(jī),洗出來的底片就是這個(gè)樣子的。
取反還有很多應(yīng)用的地方,比如做OCR文字識(shí)別的時(shí)候,因?yàn)橐话愕臅前准埡谧郑尘笆前咨?,而要分析識(shí)別的字卻是黑色,在做完二值化之后要識(shí)別的字是黑色的,如果直接做圖像切割,分離出來的就是背景“白紙”而不是目標(biāo)對(duì)象“黑字”了,而做完取反處理后就能達(dá)到切割目標(biāo)白色文字的效果。下圖是”白紙黑字“取反前后的對(duì)比:
bitwise_not()的入?yún)⒅兄挥?個(gè)圖像實(shí)例作為輸入,而接下來介紹的與、或、異或等其他幾種邏輯運(yùn)算則需要2個(gè)圖像實(shí)例(numpy數(shù)組)或者1個(gè)圖像實(shí)例和1個(gè)標(biāo)量數(shù)據(jù)。和圖像的加減乘除運(yùn)算一樣,當(dāng)涉及到2個(gè)圖像實(shí)例時(shí),也要求圖像的行列數(shù)一致。
2、按位與bitwise_and()、或bitwise_or()、異或bitwise_xor()
按位與、或、異或操作需要2個(gè)圖像對(duì)象、或者1個(gè)圖像對(duì)象和1個(gè)標(biāo)量數(shù)據(jù)相互作用,接口形式如下:
dst = cv2.bitwise_or(src1, src2[, dst[, mask]] )
dst = cv2.bitwise_or(src1, src2[, dst[, mask]] )
dst = cv2.bitwise_or(src1, src2[, dst[, mask]] )
下面是2個(gè)圖像按位與、或、異或的例子:
import cv2
print('cv2.__version__:',cv2.__version__)
img1 = cv2.imread('..\\lena.jpg' )[0:300,0:300]
img2 = cv2.imread('..\\messi5.jpg' )[0:300,0:300]
img_ret1 = cv2.bitwise_and(img1,img2)
print('img1[161,199]: ',img1[161,199])
print('img2[161,199]: ',img2[161,199])
print('img_ret1[161,199]:',img_ret1[161,199])
cv2.imshow('and-juzicode',img_ret1)
img_ret2 = cv2.bitwise_or(img1,img2)
print('img_ret2[161,199]:',img_ret2[161,199])
cv2.imshow('or-juzicode',img_ret2)
img_ret3 = cv2.bitwise_xor(img1,img2)
print('img_ret3[161,199]:',img_ret3[161,199])
cv2.imshow('xor-juzicode',img_ret3)
cv2.waitKey(0)
運(yùn)行結(jié)果:
cv2.__version__: 4.5.2
img1[161,199]: [109 105 201]
img2[161,199]: [105 43 32]
img_ret1[161,199]: [105 41 0]
img_ret2[161,199]: [109 107 233]
img_ret3[161,199]: [ 4 66 233]
2個(gè)圖像的按位操作和算術(shù)運(yùn)算一樣,也要求2個(gè)圖像的大小一樣,通道數(shù)一樣。不同于算術(shù)運(yùn)算數(shù)據(jù)類型不一樣時(shí)通過dtype聲明新生成圖像的數(shù)據(jù)類型,按位運(yùn)算的接口中根本就沒有dtype參數(shù),所以位運(yùn)算中2個(gè)圖像的數(shù)據(jù)類型也必須一致。
同樣地按位運(yùn)算也可以是一個(gè)圖像和1個(gè)標(biāo)量,如果是標(biāo)量數(shù)據(jù)類型,可以是1個(gè)單獨(dú)的數(shù)值或者是包含4個(gè)數(shù)值的四元組,這點(diǎn)和算術(shù)運(yùn)算類似。和算術(shù)運(yùn)算不同的是,如果是3通道的圖像,還可以用一個(gè)包含了3個(gè)數(shù)值的三元組和這個(gè)圖像做標(biāo)量的位運(yùn)算。
下面的例子是一個(gè)3通道圖像和四元組、三元組、單個(gè)數(shù)值進(jìn)行位運(yùn)算的例子:
import cv2
print('cv2.__version__:',cv2.__version__)
img1 = cv2.imread('..\\lena.jpg' )[0:300,0:300]
img_ret1 = cv2.bitwise_and(img1,(0x3f,0x3f,0x3f,0))
print('img1[161,199]: ',img1[161,199])
print('img_ret1[161,199]:',img_ret1[161,199])
cv2.imshow('and-juzicode',img_ret1)
img_ret2 = cv2.bitwise_or(img1,(0x0f,0x0f,0x0f))
print('img_ret2[161,199]:',img_ret2[161,199])
cv2.imshow('or-juzicode',img_ret2)
img_ret3 = cv2.bitwise_xor(img1,0xf0)
print('img_ret3[161,199]:',img_ret3[161,199])
cv2.imshow('xor-juzicode',img_ret3)
cv2.waitKey(0)
運(yùn)行結(jié)果:
cv2.__version__: 4.5.2
img1[161,199]: [109 105 201]
img_ret1[161,199]: [45 41 9]
img_ret2[161,199]: [111 111 207]
img_ret3[161,199]: [157 105 201]
但是當(dāng)圖像包含4通道時(shí),因?yàn)樘幚順?biāo)量數(shù)據(jù)時(shí)不會(huì)自動(dòng)填充第4通道為0而直接報(bào)錯(cuò)了,所以在處理4通道圖像時(shí)則必須使用四元組。一個(gè)好的編程習(xí)慣是不管圖像是多少通道的都使用四元組表示這個(gè)標(biāo)量,如果不想對(duì)某些通道進(jìn)行位運(yùn)算,則用相應(yīng)的全0或全f代替,比如一個(gè)3通道的uint8類型的圖像,只需要對(duì)2通道和0x33相與,構(gòu)造的四元組就是(0xff,0x33,0xff,0xff)。
3、浮點(diǎn)類型圖像的位運(yùn)算
前面的例子中我們都是以u(píng)int8類型為例進(jìn)行說明的,當(dāng)然16位、32位整型數(shù)據(jù)類型的圖像處理方法類似,但是如果一個(gè)圖像的數(shù)據(jù)類型是浮點(diǎn)類型時(shí),位運(yùn)算之后的結(jié)果會(huì)怎樣呢?
import numpy as np
import cv2
print('cv2.__version__:',cv2.__version__)
img1 = np.array([0.7,0.8,0.9,1.0,1.1,1.2,1.3],dtype=np.float32).reshape(1,7)
img_ret1 = cv2.bitwise_not(img1)
print('img1:',img1)
print('img_ret1:',img_ret1)
運(yùn)行結(jié)果:
cv2.__version__: 4.5.2
img1: [[0.7 0.8 0.9 1. 1.1 1.2 1.3]]
img_ret1: [[-6.3999996 -5.5999994 -4.7999997 -3.9999998 -3.7999997 -3.5999997
-3.3999999]]
從上面的運(yùn)行結(jié)果看,浮點(diǎn)數(shù)按位取反后的數(shù)據(jù)和原始數(shù)據(jù)間對(duì)比,比較明顯的是符號(hào)發(fā)生了改變,但是二者的數(shù)值看起來已經(jīng)沒有了明顯的對(duì)應(yīng)關(guān)系了。
這是由浮點(diǎn)數(shù)據(jù)在內(nèi)存中的存儲(chǔ)方式?jīng)Q定的,單精度32位浮點(diǎn)數(shù)按照1個(gè)符號(hào)位S+8個(gè)指數(shù)位E+23個(gè)有效數(shù)值位M構(gòu)成。以0.7為例,在Python中用float.hex(0.7)計(jì)算的結(jié)果為0x1.6666666666666p-1,這樣取23個(gè)有效數(shù)值位M=0110 0110 0110 0110 0110 011,指數(shù)E=127-1=0x7E=0b 0111 1110,符號(hào)位S=0,所以完整的數(shù)值為0b 0 0111 1110 0110 0110 0110 0110 0110 011=0x3f333333,取反后就為0xc0cccccc,然后再反過來計(jì)算浮點(diǎn)數(shù)的值就為-6.3999996,同樣的方法可以計(jì)算其他浮點(diǎn)數(shù)二進(jìn)制表示方法。
因?yàn)樵赑ython中沒有直接將浮點(diǎn)數(shù)的二進(jìn)制數(shù)值打印顯示的方法,我們可以用C語言中指針類型強(qiáng)制轉(zhuǎn)換的方式觀察、轉(zhuǎn)換浮點(diǎn)數(shù)的二進(jìn)制值。下面這個(gè)例子中我們先定義了一個(gè)float型的數(shù)組,然后在循環(huán)中依次處理數(shù)組中的每個(gè)元素:先將該數(shù)值做(int*)強(qiáng)制類型轉(zhuǎn)換,再對(duì)該int類型的數(shù)據(jù)做取反操作,最后對(duì)取反得到后的int類型再做(float*)強(qiáng)制轉(zhuǎn)換為float型。
#include "stdio.h"
int main(void)
{
float arr_f[7] = { 0.7,0.8,0.9,1.0,1.1,1.2,1.3 }; //定義浮點(diǎn)數(shù)數(shù)組
int arr_i[7]; //存儲(chǔ)浮點(diǎn)數(shù)轉(zhuǎn)換后的int型
int arr_i_not[7]; //存儲(chǔ)int型的按位取反
float arr_f_not[7]; //arr_i_not轉(zhuǎn)換為浮點(diǎn)數(shù)
for (int i = 0; i 7; i++) {
int *t_i = (int*)(arr_f + i); //指針類型轉(zhuǎn)換為int型
arr_i[i] = *t_i;
arr_i_not[i] = ~arr_i[i];
float *t_f = (float*)(arr_i_not + i);
arr_f_not[i] = *t_f;
}
for (int i = 0; i 7;i++) {
printf("%0.7f ", arr_f[i]);
printf("%x ", arr_i[i]);
printf("%x ", arr_i_not[i]);
printf("%0.7f \n", arr_f_not[i]);
}
return 0;
}
運(yùn)行結(jié)果如下,第1列為原始數(shù)據(jù),最后一列是按位取反后的數(shù)值,和OpenCV的bitwise_not()計(jì)算的結(jié)果一樣:
0.7000000 3f333333 c0cccccc -6.3999996
0.8000000 3f4ccccd c0b33332 -5.5999994
0.9000000 3f666666 c0999999 -4.7999997
1.0000000 3f800000 c07fffff -3.9999998
1.1000000 3f8ccccd c0733332 -3.7999997
1.2000000 3f99999a c0666665 -3.5999997
1.3000000 3fa66666 c0599999 -3.3999999
在OpenCV內(nèi)部對(duì)浮點(diǎn)類型的位運(yùn)算實(shí)際上也是按照二進(jìn)制數(shù)值進(jìn)行的轉(zhuǎn)換,不過這種轉(zhuǎn)換方法沒有非常明確的圖像學(xué)含義,所以一般浮點(diǎn)類型的位運(yùn)算幾乎很少使用。
到此這篇關(guān)于OpenCV-Python教程之圖像的位運(yùn)算詳解的文章就介紹到這了,更多相關(guān)OpenCV-Python圖像的位運(yùn)算內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:- python移位運(yùn)算的實(shí)現(xiàn)
- 簡單了解python的一些位運(yùn)算技巧
- 基礎(chǔ)的十進(jìn)制按位運(yùn)算總結(jié)與在Python中的計(jì)算示例
- 初步認(rèn)識(shí)Python中的列表與位運(yùn)算符
- 解析Python中的二進(jìn)制位運(yùn)算符
- 詳細(xì)介紹Python語言中的按位運(yùn)算符
- 關(guān)于Python 位運(yùn)算防坑指南