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学习之asyncore模块用法实例教程
Sep 29 Python
基于asyncio 异步协程框架实现收集B站直播弹幕
Sep 11 Python
python编写暴力破解zip文档程序的实例讲解
Apr 24 Python
python 获取url中的参数列表实例
Dec 18 Python
python ddt数据驱动最简实例代码
Feb 22 Python
Python paramiko模块使用解析(实现ssh)
Aug 30 Python
softmax及python实现过程解析
Sep 30 Python
python读取多层嵌套文件夹中的文件实例
Feb 27 Python
如何理解python中数字列表
May 29 Python
Python调用ffmpeg开源视频处理库,批量处理视频
Nov 16 Python
pytorch 如何使用amp进行混合精度训练
May 24 Python
python基础之类方法和静态方法
Oct 24 Python
Python中的套接字编程是什么?
教你如何使用Python开发一个钉钉群应答机器人
详解Python requests模块
Jun 21 #Python
Python pandas读取CSV文件的注意事项(适合新手)
python简单验证码识别的实现过程
Python pygame实现中国象棋单机版源码
Python并发编程实例教程之线程的玩法
Jun 20 #Python
You might like
详解关于php的xdebug配置(编辑器vscode)
2019/01/29 PHP
jquery入门—数据删除与隔行变色以及图片预览
2013/01/07 Javascript
javascript利用apply和arguments复用方法
2013/11/25 Javascript
jquery+ajax+C#实现无刷新操作数据库数据的简单实例
2014/02/08 Javascript
javascript中几个容易混淆的概念总结
2015/04/14 Javascript
JavaScript实现为指定对象添加多个事件处理程序的方法
2015/04/17 Javascript
AngularJS入门示例之Hello World详解
2017/01/04 Javascript
javascript验证香港身份证的格式或真实性
2017/02/07 Javascript
EasyUI中的dataGrid的行内编辑
2017/06/22 Javascript
完美实现js拖拽效果 return false用法详解
2017/07/28 Javascript
动态加载JavaScript文件的3种方式
2018/05/05 Javascript
Vue之mixin全局的用法详解
2018/08/22 Javascript
javascript判断一个变量是数组还是对象
2019/04/10 Javascript
详解vue v-model
2020/08/31 Javascript
解决nuxt页面中mounted、created、watch执行两遍的问题
2020/11/05 Javascript
一文秒懂nodejs中的异步编程
2021/01/28 NodeJs
python连接MySQL、MongoDB、Redis、memcache等数据库的方法
2013/11/15 Python
python爬虫入门教程--正则表达式完全指南(五)
2017/05/25 Python
python3爬取淘宝信息代码分析
2018/02/10 Python
Django文件存储 自己定制存储系统解析
2019/08/02 Python
python web框架Flask实现图形验证码及验证码的动态刷新实例
2019/10/14 Python
如何在mac环境中用python处理protobuf
2019/12/25 Python
详解Python Opencv和PIL读取图像文件的差别
2019/12/27 Python
Python下利用BeautifulSoup解析HTML的实现
2020/01/17 Python
基于Python爬取爱奇艺资源过程解析
2020/03/02 Python
Python远程方法调用实现过程解析
2020/07/28 Python
HTML5在手机端实现视频全屏展示方法
2020/11/23 HTML / CSS
美国网上订购鲜花:FTD
2016/09/23 全球购物
城市观光通行证:The Sightseeing Pass
2018/04/28 全球购物
十一个高级MySql面试题
2014/10/06 面试题
就业协议书范本
2014/04/11 职场文书
医学生求职自荐书
2014/06/12 职场文书
应届生找工作求职信
2014/06/24 职场文书
文案策划岗位职责
2015/02/11 职场文书
介绍信格式样本
2015/05/05 职场文书
2016优秀班主任个人先进事迹材料
2016/02/26 职场文书