Python-OpenCV教程之图像的位运算详解


Posted in Python onJune 21, 2021

1、按位取反bitwise_not()

按位取反就是将数值根据每个bit位1变0,0变1,比如0xf0按位取反就变成了0x0f,如果是uint8类型的数据,取反前后的数据相加结果为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)

运行结果:

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的像素点[161,199] B通道的值为109(0110 1101),取反后的值为146(1001 0010),转换为二进制后观察到每个bit为如果为0就变成1、如果为1就变成0:

Python-OpenCV教程之图像的位运算详解

Python-OpenCV教程之图像的位运算详解

上图中左侧lena这种图像是不是有种似曾相识的感觉?能回忆起来是啥子东西的朋友就要暴露年龄了,二十年前流行的胶片相机,洗出来的底片就是这个样子的。

取反还有很多应用的地方,比如做OCR文字识别的时候,因为一般的书籍是白纸黑字,背景是白色,而要分析识别的字却是黑色,在做完二值化之后要识别的字是黑色的,如果直接做图像切割,分离出来的就是背景“白纸”而不是目标对象“黑字”了,而做完取反处理后就能达到切割目标白色文字的效果。下图是”白纸黑字“取反前后的对比:

Python-OpenCV教程之图像的位运算详解

bitwise_not()的入参中只有1个图像实例作为输入,而接下来介绍的与、或、异或等其他几种逻辑运算则需要2个图像实例(numpy数组)或者1个图像实例和1个标量数据。和图像的加减乘除运算一样,当涉及到2个图像实例时,也要求图像的行列数一致。

2、按位与bitwise_and()、或bitwise_or()、异或bitwise_xor()

按位与、或、异或操作需要2个图像对象、或者1个图像对象和1个标量数据相互作用,接口形式如下:

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个图像按位与、或、异或的例子:

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)

运行结果:

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]

Python-OpenCV教程之图像的位运算详解

2个图像的按位操作和算术运算一样,也要求2个图像的大小一样,通道数一样。不同于算术运算数据类型不一样时通过dtype声明新生成图像的数据类型,按位运算的接口中根本就没有dtype参数,所以位运算中2个图像的数据类型也必须一致。

同样地按位运算也可以是一个图像和1个标量,如果是标量数据类型,可以是1个单独的数值或者是包含4个数值的四元组,这点和算术运算类似。和算术运算不同的是,如果是3通道的图像,还可以用一个包含了3个数值的三元组和这个图像做标量的位运算。

 下面的例子是一个3通道图像和四元组、三元组、单个数值进行位运算的例子:

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)

运行结果:

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]

但是当图像包含4通道时,因为处理标量数据时不会自动填充第4通道为0而直接报错了,所以在处理4通道图像时则必须使用四元组。一个好的编程习惯是不管图像是多少通道的都使用四元组表示这个标量,如果不想对某些通道进行位运算,则用相应的全0或全f代替,比如一个3通道的uint8类型的图像,只需要对2通道和0x33相与,构造的四元组就是(0xff,0x33,0xff,0xff)。

3、浮点类型图像的位运算

前面的例子中我们都是以uint8类型为例进行说明的,当然16位、32位整型数据类型的图像处理方法类似,但是如果一个图像的数据类型是浮点类型时,位运算之后的结果会怎样呢?

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)

运行结果:

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]]

从上面的运行结果看,浮点数按位取反后的数据和原始数据间对比,比较明显的是符号发生了改变,但是二者的数值看起来已经没有了明显的对应关系了。

这是由浮点数据在内存中的存储方式决定的,单精度32位浮点数按照1个符号位S+8个指数位E+23个有效数值位M构成。以0.7为例,在Python中用float.hex(0.7)计算的结果为0x1.6666666666666p-1,这样取23个有效数值位M=0110 0110 0110 0110 0110 011,指数E=127-1=0x7E=0b 0111 1110,符号位S=0,所以完整的数值为0b 0 0111 1110 0110 0110 0110 0110 0110 011=0x3f333333,取反后就为0xc0cccccc,然后再反过来计算浮点数的值就为-6.3999996,同样的方法可以计算其他浮点数二进制表示方法。

因为在Python中没有直接将浮点数的二进制数值打印显示的方法,我们可以用C语言中指针类型强制转换的方式观察、转换浮点数的二进制值。下面这个例子中我们先定义了一个float型的数组,然后在循环中依次处理数组中的每个元素:先将该数值做(int*)强制类型转换,再对该int类型的数据做取反操作,最后对取反得到后的int类型再做(float*)强制转换为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 }; //定义浮点数数组
    int arr_i[7];               //存储浮点数转换后的int型
    int arr_i_not[7];           //存储int型的按位取反
    float arr_f_not[7];         //arr_i_not转换为浮点数
    for (int i = 0; i < 7; i++) {
        int *t_i = (int*)(arr_f + i); //指针类型转换为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;
}

运行结果如下,第1列为原始数据,最后一列是按位取反后的数值,和OpenCV的bitwise_not()计算的结果一样:

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内部对浮点类型的位运算实际上也是按照二进制数值进行的转换,不过这种转换方法没有非常明确的图像学含义,所以一般浮点类型的位运算几乎很少使用。

到此这篇关于OpenCV-Python教程之图像的位运算详解的文章就介绍到这了,更多相关OpenCV-Python图像的位运算内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python制作简易注册登录系统
Dec 15 Python
python使用arcpy.mapping模块批量出图
Mar 06 Python
关于Python如何避免循环导入问题详解
Sep 14 Python
python3中的md5加密实例
May 29 Python
使用python 3实现发送邮件功能
Jun 15 Python
Python生成器generator用法示例
Aug 10 Python
python实现简单五子棋游戏
Jun 18 Python
python3 实现的对象与json相互转换操作示例
Aug 17 Python
python通过链接抓取网站详解
Nov 20 Python
python PyAUtoGUI库实现自动化控制鼠标键盘
Sep 09 Python
python实现图像随机裁剪的示例代码
Dec 10 Python
解决pytorch 模型复制的一些问题
Mar 03 Python
Python中的套接字编程是什么?
教你如何使用Python开发一个钉钉群应答机器人
详解Python requests模块
Jun 21 #Python
Python pandas读取CSV文件的注意事项(适合新手)
python简单验证码识别的实现过程
Python pygame实现中国象棋单机版源码
Python并发编程实例教程之线程的玩法
Jun 20 #Python
You might like
PHP实现文件安全下载
2006/10/09 PHP
PHP随机数 C扩展随机数
2016/05/04 PHP
利用PHP抓取百度阅读的方法示例
2016/12/18 PHP
javascript 显示当前系统时间代码
2009/12/28 Javascript
jquery 学习之二 属性 文本与值(text,val)
2010/11/25 Javascript
元素未显示设置width/height时IE中使用currentStyle获取为auto
2014/05/04 Javascript
JS根据年月获得当月天数的实现代码
2014/07/03 Javascript
jQuery中:submit选择器用法实例
2015/01/03 Javascript
基于JavaScript代码实现微信扫一扫下载APP
2015/12/30 Javascript
js 动态添加元素(div、li、img等)及设置属性的方法
2016/07/19 Javascript
使用get方式提交表单在地址栏里面不显示提交信息
2017/02/21 Javascript
实例解析ES6 Proxy使用场景介绍
2018/01/08 Javascript
React Router V4使用指南(精讲)
2018/09/17 Javascript
Vue.js 中的 v-show 指令及用法详解
2018/11/19 Javascript
Antd的Table组件嵌套Table以及选择框联动操作
2020/10/24 Javascript
element el-table表格的二次封装实现(附表格高度自适应)
2021/01/19 Javascript
python实现给数组按片赋值的方法
2015/07/28 Python
django之常用命令详解
2016/06/30 Python
Python实现的读取电脑硬件信息功能示例
2018/05/30 Python
Python3.5内置模块之random模块用法实例分析
2019/04/26 Python
pandas DataFrame索引行列的实现
2019/06/04 Python
python使用if语句实现一个猜拳游戏详解
2019/08/27 Python
详解从Django Allauth中进行登录改造小结
2019/12/18 Python
Python调用SMTP服务自动发送Email的实现步骤
2021/02/07 Python
利用CSS3实现开门效果实例源码
2016/08/22 HTML / CSS
中国专业的音频分享平台:喜马拉雅
2019/05/24 全球购物
Fox Racing英国官网:越野摩托车和山地自行车服装
2020/02/26 全球购物
大学生职业生涯规划书范文
2014/01/14 职场文书
高二物理教学反思
2014/02/08 职场文书
给校长的一封建议书
2014/03/12 职场文书
法学自荐信
2014/06/20 职场文书
水污染治理工程专业自荐信
2014/06/21 职场文书
2014党员学习兰辉先进事迹思想汇报
2014/09/17 职场文书
2014年高中教师工作总结
2014/12/19 职场文书
Python实战之大鱼吃小鱼游戏的实现
2022/04/01 Python
Python使用BeautifulSoup4修改网页内容
2022/05/20 Python