python里使用opencv打开图片,并传输到串口,让51单片机显示

如果不会用python,可以看看“北京尚学堂”的python视频,b站就有高清的

软件为Pycharm,内置python 3.7解释器         

需要用pyserial,opencv-python这两个模块,模块名可直接复制,均为小写

第一步:使用opencv打开图片

import cv2    # 引入opencv模块
import serial # 串口模块

def ON_Figure():
    img = cv2.imread('E:/figure/ph.png')    # 加载E盘figure文件夹下的ph.png图片
                                            # 后缀要有,img变量名任取
    cv2.imshow('figure', img)    # 显示图片,窗口名:figure,数据来源为img
    print(type(img))             # 打印img的类型:numpy.ndarry
    print(img)                   # 打印img内的数据
    cv2.waitKey(0)               # 无限延时,如果出问题就,(x)就留空,x大于0,就延时x毫秒

cv2.waitKey()放在最后

不了解numpy.ndarray这个类型,只用过数组,但用起来到时差不多,但列表无法被opencv显示,只能显示numpy.ndarray

实际上打开图片只用到函数下面的两行

img内的数据三个一组,RGB的数据,至于是不是R,G,B这么排的我也不知道

第二步:等比例缩放图片

    dst = cv2.resize(img, (333, 1000))    # cv2.resize(img,(width,high))调整大小
    cv2.imshow('figure2', dst)
    cv2.waitKey(0)    # 无限延时,使图片能显示

延时放到最后。width为宽,high为高,只能为整数,图片可以比原来大,也可小,也可变形

知道怎么用了以后,获取图片信息

imginfo = img.shape  # 读取信息
print(imginfo)    # 得到数据,(h,w,x)h为高,w为宽,x为3维——RGB三种颜色,每个像素都由RGB组成

得到的数据类型为元组,就是只读列表

计算高度为64时的等比例宽度

va1 = imginfo[0] / 64                # 计算高是64的几倍,带小数,元组的读取方式和列表一样
high = int(imginfo[0] / va1)         # 计算变化后的高
width = int(imginfo[1] / va1)        # 宽
print(width,high)
dst = cv2.resize(img, (width, high))  # 宽和高只能取整数,不支持浮点,而且像素只可能是整数
cv2.imshow('resize', dst)

假如,高为128,除以64等于2,那么缩小后的高就用128除以2,会得到64。128/60=?,?x 60=128

用int转换,把浮点数转成整数,int会直接砍掉小数,没有4舍5入

第三步:把图转成黑白两色

gray_lwpCV =cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY) # 转灰度图,gray_lwpCV为变量名
print(gray_lwpCV)
cv2.imshow('figure3', gray_lwpCV)

这个是转成灰度图,转完后就剩下每个像素的灰度值,RGB信息没有了,灰度是0~255,黑——灰——白,值越大越亮

得到灰度后,判断其值是否大于127,大于127就置255(白),否则置0(黑)

for i in range(high):   
    for j in range(width):
        if gray_lwpCV[i, j] > 127:  # gray_lwpCV[高, 宽]
            gray_lwpCV[i, j] = 255
        else:
            gray_lwpCV[i, j] = 0
cv2.imshow('figure4', gray_lwpCV)

这个时候再看图片的时候就是黑白色了

但是这时候的数据很多,64x128个的数据普通的单片机处理慢,何况数据是快速刷新的

需要转成8x128,显示黑白两色只要0和1

oled = [[0 for i in range(128)] for i in range(8)]  # 创建[8][128]的列表。
for i in range(high):
    Ic = i % 8    # 0~7循环,共循环8次,原数据有64行
        for j in range(width):
            if gray_lwpCV[i, j] < 128:  
                oled[i // 8][j] |= 0x01 << Ic    # i//8就是0~7,共8行。这里用0x01<>Ic跟据你用单片机控制的屏幕有关

这里我用的屏幕显示是普通的12864,每8个点为一次数据,8个像素为竖向排列,低位在上,高位在下

我用0x01<

这段程序的作用就是把原来8行的数据转为1行,原来8个纵向的点分别用0和255来表示,,用到8x8bit,现在只要8bit就能代表8个点。建议用用Pycharm的调试,看看数据都是怎么排的

这种,[[0] * 3] * 3创建列表不能用,给一个赋值,其他的都会赋值,不知道为啥,希望有人说说

至此,数据处理基本完成,但是串口发送可是个大问题,跟着寥寥无几的资料和根本不算教程的教程摸索,头大。有人直接贴出代码,啥说明没有,连最起码的注释都不写,复制过来都不知道怎么用

ser = serial.Serial('com2', 19200, timeout=None)    # 必须用com1这种格式,57600bps,超时:无
print(ser)                       # 打印串口的各种设置状态
print(ser.isOpen())              # 检查串口是否打开
ser.write(b'hello!')              # 发送hello!

一般只需要设置这三个,超时时间意思是接收到数据后就开始计时,如果超过时间就返回数据,没超过就等待下一个数据,每收到一帧数据刷新计时,时间单位为秒,如timeout=0.5。None就是一直在接收

关键就是'b'这个东西,在Python3里新加的,原来2里直接用字符串就行了。3进步了,但是write函数没进步

b的作用就是把字母换成对应的16进制,ASCII码里的,0~255的范围,如果要发送的不在ASCII码里的要用到编码转换,比如中文,这里就不说了

只发送字母倒没啥,但是发送16进制,呵呵

ser.write(b'\x30')发送的就是0x30,\x是python的16进制识别符,类似于0x,但是\x不能写在普通字符串里,发送多个就不行了

hvol = 48    # 这里是10进制
hhex = '%02x' % hvol    # 以16进制表示,不带前面的‘0x’,输出一个字符串,不足两个字符前面补0如,int类型的48,转成str类型的'30'
ser.write(bytes.fromhex(hhex))  # bytes.fromhex(hhex)返回一个 b'n'字符串,如b'0',n为该16进制对应的ascii码,hhex为字符串,hhex='30'
            

如果要发送多个,把hvol换成列表,用循环发送

必须经过这种转换,或者用utf-8编码转换,但涉及到字符串的更改,不好使。

8x128的数据有了,发送10进制的方法有了,就简单了,循环发送就可以了

for i in range(8):
    #str1 = b'start' + (bytes.fromhex('%02x' % i))  # 字符串(str类型,就是Unicode解码16进制出来的)编码成bytes(就是ascii码),把被解码的16进制重新编码回去。b'0'被转成\x30发送,\x是python的16进制标识码,和0x一样
    #ser.write(str1)    # 只能发送str
    for j in range(128):
        hhex = '%02x' % oled[i][j]  # 以16进制表示,不带前面的‘0x’,输出一个字符串,不足两个字符前面补0如,int类型的48,转成str类型的'30'
        ser.write(bytes.fromhex(hhex))  # bytes.fromhex(hhex)返回一个 b'n'字符串,n为该16进制对应的ascii码,如b'0',hhex为字符串,hhex='30'
        '''if j == 127:
            while True:
                ll = ser.read(5)
                if ll == b'hello':  # 读到的是字母的16进制,对比时也要用16进制对比
                    break'''

只需用到没注释的地方,注释的地方也很重要,但这里不是它发挥的地方

关于51的程序我只贴有用的,我用的时89c52,19200bps,测试正常

u8 ix=0,iy=0
void Interrupt4() interrupt 4	
{
	if(RI)
	{
		RI=0;	
        DPByte(ix,iy,SBUF);    // 每次写一个字节
	    ix++;
        if(ix==128)ix=0,iy++;    // 换行
    }
}
       

先开51,后运行python

会在下一篇里说怎么用51显示视频,跟这个显示图片差不多

https://pan.baidu.com/s/1I39Y1mU-_L19WgbwNeINMg

你可能感兴趣的:(Python)