的档案

[学习笔记]树莓派

[TOC]

树莓派

常用指令

  • 关机
sudo shutdown -h now
  • 重启
sudo reboot
  • 删除文件(即这个文件被删除)

    • 单个删除:rm -f + 文件名
    rm -f  2018_12_26.stderrout.log.060121612
    
    • 批量删除:rm -f + 文件关键字
    rm -f  rm stderrout.log
    # 执行完成后即将所有的名称包括stderrout.log的文件都删除(前面后面的*按照实际需要运用,这个*表示前面不管有任何东西都可以
    
  • CPU温度

vcgencmd measure_temp
  • 文件传输

    • 指令传输

      scp C:\Users\20254\Downloads\assimp.whl laoair@192.168.137.222:/home/laoair/Download
      
    • VNC传输

      • pc -> pi

        • pc 的 vnc 窗口
      • pi -> pc

        • pi 的 vnc 图标
        • file transfer...
  • gpio

gpio readall

开始配置

  • 树莓派烧录时设置为ssh密码登录

  • WIFI连接获取IP地址

    • 使用CN信道,否则无法连接5G网络
  • 拓展文件

    • 首先终端输入

      sudo raspi-config
      
    • Advanced Option -> Expand Filesystem

    • 最后重启

      sudo reboot
      
  • SSH登录冲突

    • 原因:这个ip在过去使用公钥进行登录,也就是在电脑日志中出现ip冲突

    • 将相应的ip公钥清除即可

      ssh-keygen -R 172.20.10.2
      
  • SSH免密登录

    • 打开公钥授权登录

      • 修改/etc/ssh/sshd_config
      • 改为PubkeyAuthentication yes
    • 制作密钥

      ssh-keygen -t rsa 
      ~/.ssh/id_rsa
      
    • 在vscode终端里 发送公钥 name.pub

      cat C:/Users/20254/.ssh/id_rsa.pub | ssh laoair@192.168.137.151 'sudo cat >> .ssh/authorized_keys'
      
    • 或者直接创建新文件 .ssh/authorized_keys , 复制密钥内容进入

    • 这是用户与用户之间的协议,也就是没有密钥的用户无法免密

      • 给多个用户发送,一劳永逸
    • 文件权限

      • 为了让私钥文件和公钥文件能够在认证中起作用, 需要确保权限的正确性

      • 对普通用户, 建议设置成600权限:

        chmod 600 authorized_keys id_rsa id_rsa.pub
        
      • 对root用户, 建议设置成644权限:

        chmod 644 authorized_keys id_rsa id_rsa.pub.
        
  • SSH 使用 Root 权限

    sudo vim /etc/ssh/sshd_config
    增加一行:PermitRootLogin yes
    
    sudo passwd root
    
    最后
    sudo service ssh restart
    
  • VNC连接

    • 树莓派原装的VNC驱动有问题,导致突然黑屏

    • 建议卸载重装

      sudo apt-get remove realvnc-vnc-server realvnc-vnc-viewer
      sudo apt-get update
      sudo apt-get install realvnc-vnc-server realvnc-vnc-viewer
      
    • 下载Download VNC Viewer | VNC® Connect (realvnc.com)

    • 关于cannot currently show the desktop问题

      • 打开 /boot/config.txt 添加以下内容

        framebuffer_width=800
        framebuffer_height=480
        hdmi_mode=87
        hdmi_cvt 800 480 60 6 0 0 0
        
    • 树莓派默认分辨率720×480

  • 换源(极其重要!源错了软件也会全部装错!

  • 静态IP(最好别用,免得换电脑后无法连接wifi)

    • Windows终端输入 ipconfig 查找 默认网关

    • 树莓派当前 IP 后加上 /24

    • 端口改为 wlan0

    interface wlan0
    static ip_address=192.168.137.151/24
    static routers=192.168.84.71
    static domain_name_servers= 192.168.84.71
    
  • 如果要编译 , 需要开启交换空间加速

    • sudo nano /etc/dphys-swapfile
      
    • 编辑 CONF_SWAPSIZE 变量为2048或1024

  • opencv-python的选择

关于开发

串口通信

  • 树莓派默认映射关系

    • laoair@raspberrypi:~ $ ls -l /dev
      lrwxrwxrwx 1 root root 7 8月 13 07:34 serial0 -> ttyS0
      lrwxrwxrwx 1 root root 5 8月 13 07:34 serial1 -> ttyAMA0
      
    • serial0 是GPIO引脚对应的串口,默认使用的是ttyS0 (mini串口)

    • serial1 是蓝牙对应的串口,默认使用的是ttyAMA0(硬件串口)

  • 修改映射关系

    • sudo nano /boot/config.txt

    • 在文件的末尾加上一行代码

      • dtoverlay=miniuart-bt
  • 此时,映射关系更改为

    • lrwxrwxrwx 1 root root 7 8月 13 07:34 serial0 -> ttyAMA0
      lrwxrwxrwx 1 root root 5 8月 13 07:34 serial1 -> ttyS0
      
  • 禁用串口的控制台功能

    • sudo systemctl stop serial-getty@ttyAMA0.service
      
    • sudo systemctl disable serial-getty@ttyAMA0.service
      
    • sudo nano /boot/cmdline.txt
      
      • 在文档中删除语句 console=serial0,115200
  • 编写代码

    • # -*- coding: utf-8 -*
      #! /usr/bin/env python
      import serial
      import time
      ser = serial.Serial('/dev/ttyAMA0', 115200)  # 设置端口和波特率
      if ser.isOpen == False:  # 检查串口是否打开
          ser.open()                # 打开串口
      ser.write(b"Raspberry pi is ready")
      try:
          while True:
              size = ser.inWaiting()               # 获得缓冲区字符
              if size != 0:
                  response = ser.read(size)        # 读取内容并显示
                  print(response)        
                  ser.flushInput()                 # 清空接收缓存区
                  time.sleep(0.5)                  # 软件延时
      except KeyboardInterrupt:
          ser.close()
      

GIT仓库

  • 参考资料1:Git教程 - (liaoxuefeng.com)

  • 参考资料2:树莓派使用Git - 知乎 (zhihu.com)

  • 全局设置

    git config --global user.name "raspberrypi_laoair"
    git config --global user.email "2025406529@qq.com"
    
  • 克隆外部仓库

    git clone (URL)
    
  • 本地使用

    • 对应源码目录初始化,仓库本体位于.git

      git init
      
    • git 保存

      git add .
      
    • git 提交新版本

      git commit -m "注释"
      
    • git 回溯版本

      git reset --hard HEAD^
      git reset --hard HEAD~100
      git reset --hard (commit-id)
      
    • git 日志查询

      git log
      git log --pretty=oneline
      git reflog
      
  • 云端使用

    • void

END

[学习笔记]opencv

[学习笔记]OpenCV

基于Python,By Laoair

基础操作

图片读取

# 一个是打开图片文件,一个是打开图片数据类型的对象
def readImg(src,code): ## code:0,grey;1,color
    img = cv2.imread('./img/1.jpg', code)
    cv2.imshow('test', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
def showImg(img):
    cv2.imshow('test', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
void readImg(src,code=1)
{
    Mat img = imread(src,code);
    imshow(winName,img);
    waitKey(0);
	destroyAllWindows();
}
void showImg(Mat img,string winName="test")
{
    imshow(winName,img);
    waitKey(0);
	destroyAllWindows();
}

视频读取

def readVideo(src,code):## code:6,grey;1,color
    vc = cv2.VideoCapture(src)
    if vc.isOpened():
        open,frame = vc.read()
    else:
        open = False
    while open:
        ret, frame = vc.read()
        if frame is None:
            break
        if ret == True:
            value = cv2.cvtColor(frame,code)
            cv2.imshow('result',value)
            if cv2.waitKey(10) & 0xFF ==27:
                break
    vc.release()
    cv2.destroyAllWindows()
VideoCapture cap(src);
Mat frame;
while (1)
{
	cap >> frame;
    //cap.read(frame)
    if (img.empty())break;
    imshow("img", img);
    if (27 == waitKey(20))break;
}
cap.release();//释放资源

摄像头帧率

  • 实时计算
def fpsShow(src):
    cap = cv2.VideoCapture(src)  # 导入的视频所在路径
    start_time = time.time()
    counter = 0
    fps = cap.get(cv2.CAP_PROP_FPS)  # 视频平均帧率
    while cap.isOpened():
        ret, frame = cap.read()

        counter += 1  # 计算帧数
        if (time.time() - start_time) != 0:  # 实时显示帧数
            cv2.putText(frame, "FPS {0}".format(float('%.1f' % (counter / (time.time() - start_time)))), (20, 50),
                        cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255),
                        3)
            cv2.imshow('frame', frame)
            print("FPS: ", counter / (time.time() - start_time))
            counter = 0
            start_time = time.time()
        time.sleep(1 / fps)  # 按原帧率播放

        # 键盘输入空格暂停,输入q退出
        key = cv2.waitKey(1) & 0xff
        if key == ord(" "):
            cv2.waitKey(0)
        if key == 27:
            break

    cap.release()
    cv2.destroyAllWindows()
  • 读取摄像头自身属性
vc = cv2.VideoCapture(0)
fps = vc.get(cv2.CAP_PROP_FPS)
print(fps)

图片截取

def cropImg(img,x1,x2,y1,y2):
    cimg=img[x1:x2,y1:y2]
    return cimg

颜色提取

def splitColor(img):
    b,g,r=cv2.split(img)
    return b,g,r
vector<Mat>channels;
split(img,channels);//通道分离
Mat blue=channels.at(0);
Mat green=channels.at(1);
Mat red=channels.at(2);

颜色设置

def imgResetColor(img,color):# color:0,b;1,g;2,r
    img[:,:,color]=0
    return img

图层合并

def imgMerge(b,g,r):
    img=cv2.merge((b,g,r))
    return img
merge(channels,dst);

边界填充

def imgFillBorder(img,top,bottom,left,right,type):# type 是 已被定义的宏
    cv2.copyMakeBorder(img,top,bottom,left,right,type)
    return img

数值计算

  • 直接相加

    • 255溢出
  • 函数相加 :cv2.add(imga,imgb)

    • 255不溢出
  • 函数权重add : cv2.addWeighted(imga,powera,imgb,powerb)

addWeighted(img1,0.5,img2,0.5,0,dst);

dst=5*img1;//增加曝光
dst=img1/5;//降低曝光
bitwise_and(img1,img2,dst);//逻辑与,求交集
bitwise_or(img1,img2,dst);//逻辑或,求并集
bitwise_not(img1,dst);//逻辑非,求补集
bitwise_xor(img1,img2,dst);//异或,相同为0,相异为1

ROI

img = img(Range(30,400),Range(30,400));
img = img(Rect(10,20,300,500));

图片尺寸

resize(src,dst,Size(300,300));

形态学

应用

卷积核

Mat element=getStructuringElement(MORPH_RECT,Size(5,5)); 
Mat element2=getStructuringElement(MORPH_CROSS,Size(5,5));
Mat element3=getStructuringElement(MORPH_ELLIPSE,Size(5,5));
  • element

    • 11111
    • 11111
    • 11111
    • 11111
    • 11111
  • element2

    • 00100
    • 00100
    • 11111
    • 00100
    • 00100
  • element3

    • 01110
    • 11111
    • 11111
    • 11111
    • 01110

腐蚀

def imgErosiion(img):
    kernel = np.ones( (5,5) ,np.uint8 )
    erosion = cv2.erode(img,kernel,iterations=1) # iterations 操作次数
    return erosion
erode(src,dst,element,Point(-1,-1),2);//腐蚀两次 (-1,-1)默认中心位置

膨胀

def imgDilate(img):
    kernel = np.ones((5, 5), np.uint8)
    dilate = cv2.dilate(img, kernel, iterations=1)
    return dilate
dilate(src,dst,element,Point(-1,-1),1); // 膨胀一次

开运算与闭运算

  • 开运算: 先腐蚀再膨胀,相当于 去除毛刺

    • 开操作是一般使对象的轮廓变得光滑,断开狭窄的间断和消除细的突出物

    • def imgOpening(img):
          kernel = np.ones((5, 5), np.uint8)
          opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
          return opening
      
      morphologyEx(srcImg, dstImg, MORPH_OPEN, element);  //开运算
      
  • 闭运算: 线膨胀再腐蚀,相当于 放大毛刺

    • 闭操作可使轮廓线更光滑,通常消弥狭窄的间断和长细的鸿沟,消除小的空洞,并填补轮廓线中的断裂。

    • def imgClosing(img):
          kernel = np.ones((5, 5), np.uint8)
          closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
          return closing
      
      morphologyEx(srcImg, dstImg, MORPH_CLOSE, element);  //闭运算
      

梯度运算

  • 梯度=膨胀-腐蚀, 得到 图片轮廓信息
def imgGradient(img):
    kernel = np.ones((7,7), np.uint8)
    gradient = cv2.morphologyEx(img,cv2.MORPH_GRADIENT,kernel)
    return gradient
morphologyEx(srcImg, dstImg, MORPH_GRADIENT, element);  //形态学梯度运算

礼(顶)帽与黑帽

  • 礼(顶)帽 :原始输入-开运算结果;突出明亮区域

    • def imgTophat(img):
          kernel = np.ones((7, 7), np.uint8)
          tophat = cv2.morphologyEx(img,cv2.MORPH_TOPHAT,kernel)
          return tophat
      
      morphologyEx(srcImg, dstImg, MORPH_TOPHAT, element);  //顶帽运算
      
  • 黑帽 :闭运算结果-原始输入

    • def imgBlackhat(img):
          kernel = np.ones((7, 7), np.uint8)
          blackhat = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT,kernel);
          return blackhat
      
      morphologyEx(srcImg, dstImg, MORPH_BLACKHAT, element);  //黒帽运算
      

Sobel算子

  • 计算图像梯度,并呈现再图片上
def imgSobel(img,dx,dy):
    sobel=cv2.Sobel(img,cv2.CV_64F,dx,dy,ksize=3)
    sobel=cv2.convertScaleAbs(sobel) # 取绝对值
    return sobel
Sobel(src, dst_x, CV_16S, 1, 0, 3);
Sobel(src, dst_y, CV_16S, 0, 1, 3);
convertScaleAbs(dst_x, abs_grad_x);
convertScaleAbs(dst_y, abs_grad_y);
addWeighted(abs_grad_x 0.5, abs_grad_y, 0.5, 0, dst);

Scharr算子

  • 相对于Sobel更敏感
def imgScharr(img,dx,dy):
    scharr = cv2.Scharr(img,cv2.CV_64F,dx,dy)
    scharr = cv2.convertScaleAbs(scharr)
    return scharr

Laplacian算子

  • 相对于Sobel更迟钝
def imgLaplacian(img):
    laplacian = cv2.Laplacian(img,cv2.CV_64F)
    laplacian = cv2.convertScaleAbs(laplacian)
    return laplacian
Laplacian(src, dst, CV_16S, 3);
convertScaleAbs(dst, abs_dst);

平滑处理

  • 用于处理 去除噪音点,滤波操作

均值滤波

# 当normalize=True时,二者完全按相同;溢出会归一化
def imgBlur(img,dx,dy):
    blur = cv2.blur(img,(dx,dy))
    return blur
def imgBox(img,dx,dy):
    box = cv2.boxFilter(img,-1,(dx,dy),normalize=True)# 第二个参数指定-1为与源数据相同;第四个参数是是否取均值,不取均值可能导致数据溢出
    return box
boxFilter(src,dst,-1,Size(3,3),Point(-1,-1),true);
blur(src,dst,Size(5,5));

高斯滤波

  • 首选,稳定,好使

  • cv2.GaussianBlur(src, ksize[,sigmaX, sigmaY[,, borderType]])-> dst

    • src输入图像
    • dst输出图像的大小和类型与src相同。
    • ksize高斯内核大小。
    • sigmaX X方向上的高斯核标准偏差
    • sigmaY Y方向上的高斯核标准差
def imgAussian(img,dx,dy):
    aussian = cv2.GaussianBlur(img,(dx,dy),1)# 第三个参数 高斯滤波器的尺寸
    return aussian
GaussianBlur(src,dst,Size(5,5),1);

中值滤波

  • 用于去除椒盐噪
def imgMedian(img,ksize):
    median = cv2.medianBlur(img,ksize)
    return median
medianBlur(src,dst,5);

双边滤波

  • 保留边框,去除内容的噪点,磨皮
bilateralFilter(src,dst,5,10.0,2.0);

阈值(二值化)

  • 阈值操作 并不能 绝对二值化

  • Canny 边缘处理后 为 绝对二值图

  • res , dst = cv2.threshold(img,thresh,maxval,type)

    • res : True或False,代表是否读到图片

    • img: 输入图,只能输入单通道图像,通常来说为灰度图

    • dst : 输出图

    • thresh:阈值

    • maxval:当像素值达到了一定条件,所赋予的值

    • type:操作类型,见下

      • cv2.THRESH_BINARY:超过阈值部分取maxval,否则取0
      • cv2.THRESH_BINARY_INV: 对上一个的反转
      • cv2.THRESH_TRUNC:大于阈值部分取maxval,否则不变-----------更多空白,降低亮度
      • cv2.THRESH_TOZERO:大于阈值部分不变,否则取0
      • cv2.THRESH_TOZERO_INV 对上一个的反转
  • dst = cv2.inRange(src,lowerb,upperb]

    • src:输入图片,Mat类型
    • lowerb:一个三通道颜色,比如(0,0,255);图像中低于这个lower的值,图像值变为0
    • upperb:一个三通道颜色,比如(0,0,255);图像中高于这个upper的值,图像值变为0
    • dst:二值化图片
threshold(src,dst,100,255,CV_THRESH_BINARY);
adaptiveThreshold(src,dst,255,CV_ADAPTIVE_THRESH_MEAN_C,CV_THRESH_BINARY,11,5); //求均值
adaptiveThreshold(src,dst,255,CV_ADAPTIVE_THRESH_GAUSSIAN_C,CV_THRESH_BINARY,11,5); // 加权求平均

Canny边缘检测

  • 1)去噪:使用高斯滤波器,以平滑图像,滤除噪声

  • 2)计算梯度:计算图像中每个像素点的梯度强度和方向

    • 强度计算 G = S*A ; S是权值,A是实际值
    • 方向计算 θ = arctan( G_y / G_x )
  • 3)应用非极大值抑制(NMS),以消除边缘检测带来的杂散响应

    • 线性插值法
    • 简化计算:直接比较方向上各点的值进行抑制
  • 4)应用双阈值检测来确定真实的和潜在的边缘

    • 阈值以上为边界
    • 阈值以内判断是否联通
    • 阈值以下忽略
  • 5)通过抑制鼓励的若边缘最终完成边缘检测

edges = cv2.Canny( image, threshold1, threshold2[, apertureSize[, L2gradient]])
'''
--------------param---------------
image: 图片源,只能是单通道图像
threshold1:低阈值
threshold2:高阈值
apertureSize:算子大小
L2gradient:为计算图像梯度幅度的标识。其默认值为 False。如果为 True,则使用更精确的 L2 范数进行计算(即两个方向的导数的平方和再开方),否则使用 L1 范数(直接将两个方向导数的绝对值相加)。
edges: 二值图
----------------------------------

---------------note---------------
----注意阈值越高,对边界的要求就越高----
----降低阈值可以得到更精细的边界图片----
----------------------------------
'''
Canny(srcImg, dstImg, 30, 80);

轮廓

  • 在黑色背景中找白色物体

  • 得到轮廓信息的函数

  • (binary ,)contours, hierarchy = cv2.findContours(img,mode,method)

    • binary : 等效img ; (新版本不返回这个值)

    • contours:轮廓列表

    • hierarchy:层级,了解即可

    • img:二值图,该函数只接受二值图,否则报错

    • mode:轮廓检索模式

      • cv2.RETR_EXTERNAL:只检索最外面的轮廓
      • cv2.RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中
      • cv2.RETR_CCOMP:检索所有的轮廓,并将它们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界
      • cv2.RETR_TREE(一般情况下):检索所有的轮廓,并重构嵌套轮廓的整个层次
    • method:轮廓逼近方法(以下为常用)

      • cv2.CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列):相当于除了点还有线
      • cv2.CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分;相当于只画顶点
img = cv2.imread(imgSrc2)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) # 转灰度图片
ret, thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 转二位图,提高精确度 
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) # 轮廓检测

# 绘制轮廓 
drawImg = img.copy()
res = cv2.drawContours(drawImg,contours,-1,(0,0,255),2)
showImg(res)

# 轮廓近似
cnt = contours[2] # 取出某一个轮廓信息
epsilon = 0.15*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon ,True)
drawImg = img.copy()
res = cv2.drawContours(drawImg,[approx],-1,(0,0,255),2)
# showImg(res)

# 轮廓边框
x,y,w,h = cv2.boundingRect(cnt)
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
# showImg(img)
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(srcImg, contours, hierarchy,RETR_LIST, CHAIN_APPROX_SIMPLE ); //查找所有轮廓
drawContours(tempImg, contours,-1, Scalar(0, 255, 0),2);  //绘制轮廓:-1代表绘制所有轮廓

// 遍历轮廓每一个点
for(int i=0; i<contours.size(); i++)
    for(int j=0; j<contours[i].size(); j++)
		circle(temp,Point(contours[i][j].x,contours[i][j].y),3,Scalar(0,255,0),2,8); 
  • 一些相关的东西
# 轮廓 本质上是 点集
# 而 opencv 有很多专门处理 点集 的函数
----------------1--------------------
area = cv2.contourArea(cnt)
# 计算轮廓面积
----------------2--------------------
perimeter = cv2.arcLength(cnt,True)
# 轮廓周长
# 第二个参数用来指定对象的形状是闭合(True)还是打开(False)
----------------3--------------------
epsilon = 0.1*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)
# 轮廓近似
# epsilon表示从原始轮廓到近似轮廓的最大距离,它是一个准确度参数
# epsilon越小,近似图像越复杂,也就越契合源图像
# 第三个参数True为闭合
# approxPolyDP的返回值也为 点集 , 但是近似图形的 顶点列表 
# 近似图像 点与点直接肯定是图像 ,所以返回顶点列表即可
-----------------4---------------------
(x, y, w, h) = cv2.boundingRect(cnt)
# 获得一个图像的最小矩形边框一些信息
# 返回一个元组,包含 左上角顶点的(x,y)和矩形的宽高

矩(Moments)

  • Moments moments(InputArray array, bool binnaryImage = false)

    • array,一个点集,轮廓

    • binnaryImage,是否为二值图像。默认为 false。若此值为 true,则所有非零像素均为 1,需注意的是,此参数仅对图像使用。

    • 返回值为 Moments 类型对象(矩),在python中是字典

      • 例如,{'m00': 0.0, 'm10': 0.0, 'm01': 0.0, 'm20': 0.0, 'm11': 0.0,.........}
  • 计算 几何中心

    • Cx = M10/M00
    • Cy = M01/M00
img = cv2.imread(imgSrc2,0)
thresh = cv2.threshold(img,110,255,cv2.THRESH_BINARY_INV)[1]
cnts= (cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE))[0]

M = cv2.moments(cnts[0])
cx,cy=int(M['m10']/M['m00']),int(M['m01']/M['m00'])
cv2.circle(img,(cx,cy),4,(130,170,0),3)
showImg(img)

直线拟合

  • output = cv2.fitLine(InputArray points, distType, param, reps, aeps)

    • InputArray Points:必须是nparray类型的点集(一般的数列直接报错噢)

    • distType: 距离类型。fitline为距离最小化函数,拟合直线时,要使输入点到拟合直线的距离和最小化。

      • cv2.DIST_USER : User defined distance
      • cv2.DIST_L1: distance = |x1-x2| + |y1-y2|
      • cv2.DIST_L2: 欧式距离,此时与最小二乘法相同;常用
      • cv2.DIST_C:distance = max(|x1-x2|,|y1-y2|)
      • cv2.DIST_L12:L1-L2 metric: distance = 2(sqrt(1+x*x/2) - 1))
      • cv2.DIST_FAIR:distance = c^2(|x|/c-log(1+|x|/c)), c = 1.3998
      • cv2.DIST_WELSCH: distance = c2/2(1-exp(-(x/c)2)), c = 2.9846
      • cv2.DIST_HUBER:distance = |x|<c ? x^2/2 : c(|x|-c/2), c=1.345
    • param: 距离参数,跟所选的距离类型有关

      • 值可以设置为0。
      • 实际上,写0的话,系统会自动给你用最优的参数。
    • reps, aeps: 第5/6个参数用于表示拟合直线所需要的径向和角度精度,通常情况下两个值均被设定为1e-2.

    • output : 对于二维直线,输出output为4维,前两维代表拟合出的直线的方向,后两位代表直线上的一点。(即通常说的点斜式直线)

# 注意!此代码的 几何中心 有误差!
#--------------------------------------------------------------------#
img = cv2.imread(imgSrc5)
# img = img[ int(0.3*img.shape[0]):int(0.7*img.shape[0]) ,:]
# 可以只截取一部分来拟合,提高精确度
showImg(img)
img_width = img.shape[1]
img_height = img.shape[0]

gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
thres = cv2.threshold(gray,100,255,cv2.THRESH_BINARY_INV)[1]
element=np.ones((4,4), np.uint8)
thres = cv2.morphologyEx(thres, cv2.MORPH_ERODE, element)
element=np.ones((2,2), np.uint8)
thres=cv2.morphologyEx(thres, cv2.MORPH_DILATE, element)
thres=cv2.morphologyEx(thres, cv2.MORPH_ERODE, element)
v = cv2.Canny(thres, 30, 80)

# 获取点集
points = []
for y in range(img_height):
    for x in range(img_width):
        if v[y,x]==255 :
            points.append([x,y])
points = np.array(points)

output = cv2.fitLine(points, cv2.DIST_L2, 0, 0.01, 0.01)
k = output[1] / output[0]
b = output[3] - k * output[2]
p1 = ( 0 , int(k*(0-output[2])+output[3]) )
p2 = ( int(img_width-1) , int(k*(img_width-1-output[2])+output[3]) )  #
cv2.line(img,p1,p2,(0,0,255),2)

M = cv2.moments(points,True)
cx,cy = int(M['m10']/M['m00']) , int(M['m01']/M['m00'])
cv2.circle(img,(cx,cy),4,(130,170,0),-1,8)

showImg(img)

模板匹配

  • res = cv2.matchTemplate(img,template,method)

    • img:原图

    • template:要匹配的模板

    • method

      • cv2.TM_SQDIFF:计算平方不同,结果越小,相关性越大
      • cv2.TM_CCORR:计算相关性,结果越大,相关性越大
      • cv2.TM_CCOEFF:计算相关系数,结果越大,相关性越大
      • cv2.TM_SQDIFF_NORMED:计算归一化平方不同
      • cv2.TM_CCORR_NORMED:计算归一化相关性
      • cv2.TM_CCOEFF_NORMED:计算归一化相关系数
img = cv2.imread(imgSrc4)
tmp = cv2.imread(imgSrc5)
h,w = tmp.shape[:2]
imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
tmpGray = cv2.cvtColor(tmp,cv2.COLOR_BGR2GRAY)

res = cv2.matchTemplate(imgGray,tmpGray,cv2.TM_CCOEFF_NORMED)

# 单个匹配
minval,maxvak,minloc,maxloc = cv2.minMaxLoc(res)
top_left = minloc
bottom_right = (top_left[0]+w,top_left[1]+h)
cv2.rectangle(img,top_left,bottom_right,(0,0,255),2)
showImg(img)

# 多个匹配
threshold = 0.8
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]):
    bottom_right = (pt[0]+w,pt[1]+h)
    cv2.rectangle(img , pt , bottom_right, (0,0,255), 1)
showImg(img)
matchTemplate(src,temp,result,CV_TM_CCOEFF_NORMED);
normalize(result,result,0,1,NORM_MINMAX,-1);//归一化到0-1范围
// 单个匹配
minMaxLoc(result,&minValue,&maxValue,&minLoc,&maxLoc);
rectangle(dst,maxLoc,Point(maxLoc.x+temp.cols,maxLoc.y+temp.rows),Scalar(0,255,0),2,8);
// 多个匹配
if(maxValue>=0.7)
	rectangle(showImg, maxLoc, Point(maxLoc.x + templateImg.cols, maxLoc.y + templateImg.rows), Scalar(0, 255, 0), 2);

Trackbar

  • 方便实时调整参数,找到合适的阈值

  • cv.namedWindow(winName)

    • 创建名字为 winName 一个窗口
  • createTrackbar(const String& trackbarname, const String& winname, int *value**, int count, TrackbarCallback onChange = 0, void *userdata** = 0)

    • trackbarname:跟踪栏名称,创建的轨迹栏的名称。

    • Winname:窗口的名字,表示这个轨迹条会依附到哪个窗口上,即对应namedWindow()创建窗口时填的窗口名。

    • value:指向整数变量的可选指针,该变量的值反映滑块的初始位置。

    • count:表示滑块可以达到的最大位置的值,最小位置始终为0。

    • onChange:指向每次滑块更改位置时要调用的函数的指针,有默认值0。此函数的原型应为void XXX (int, void *); ,其中第一个参数是轨迹栏位置,第二个参数是用户数据(请参阅下一个参数)。如果回调是NULL指针,则不调用任何回调,而仅更新值。

    • userdata:传递给回调的用户数据,有默认值0。它可以用于处理轨迹栏事件,而无需使用全局变量。如果使用的第三个参数value实参是全局变量的话,完全可以不去管这个userdata参数。

  • value= cv2.getTrackbarPos(trackbarName,winName)

    • trackbarName:条状进度条的名字
    • winName:进度条的名字
    • value:得到trackbar当前的值
# 二值化调试
def update(x):
    thres = cv2.getTrackbarPos('Threshold','test')
    res=cv2.threshold(img,thres,255,cv2.THRESH_BINARY)[1]
    cv2.imshow('test',res)

img = cv2.imread(imgSrc1,0)
cv2.namedWindow('test')
thres = 100
cv2.createTrackbar('Threshold','test', thres ,255,update)
update(0)
cv2.waitKey(0)
cv2.destroyAllWindows()


# HSV 调试
def onTrackbar(x):
    minh, mins, minv = cv2.getTrackbarPos('minh', 'test'), \
                       cv2.getTrackbarPos('mins', 'test'), \
                       cv2.getTrackbarPos('minv', 'test')
    maxh, maxs, maxv = cv2.getTrackbarPos('maxh', 'test'), \
                       cv2.getTrackbarPos('maxs', 'test'), \
                       cv2.getTrackbarPos('maxv', 'test')
    color1 = (minh,mins,minv)
    color2 = (maxh,maxs,maxv)
    print(color1,color2)
    res = cv2.inRange(img, color1, color2)
    cv2.imshow('test',res)

orig = cv2.imread(imgSrc4)
img = cv2.cvtColor(orig,cv2.COLOR_BGR2HSV)
showImg(img)
minh,mins,minv = 0,0,0
maxh,maxs,maxv = 255,255,255

color1 = (minh,mins,minv)
color2 = (maxh,maxs,maxv)

cv2.namedWindow('test')
cv2.createTrackbar('minh','test', minh ,255,onTrackbar)
cv2.createTrackbar('mins','test', mins ,255,onTrackbar)
cv2.createTrackbar('minv','test', minv ,255,onTrackbar)
cv2.createTrackbar('maxh','test', maxh ,255,onTrackbar)
cv2.createTrackbar('maxs','test', maxs ,255,onTrackbar)
cv2.createTrackbar('maxv','test', maxv ,255,onTrackbar)

onTrackbar(0)
cv2.waitKey(0)
cv2.destroyAllWindows()
void onChange(int, void* param)
{
    Mat img = *(Mat *)param;
    Mat dst;
    threshold(img,dst,thr,255,THRESH_BINARY);
    imshow("test",dst);
}
int main()
{
    string src = "img/1.jpg";
    Mat img = imread(src,1);
    namedWindow("test",WINDOW_NORMAL);
    createTrackbar("Threshold","test",&thr,255,onChange,&img);
    imshow("test",img);
    waitKey(0);
}

绘图

  • matplotlib.pyplot模块
plt.plot(x, y, format_string, kwargs)
'''
普通画图
x:x轴数据,列表或数组,可选
y:y轴数据,列表或数组
format_string:控制曲线的格式字符串,可选,由颜色字符、风格字符和标记字符组成。
'''
plt.xlim(x,y) 
'''
设置直方图显示的x坐标范围
显示的是x轴的作图范围,右端点为开区间
也可以 赋值列表
'''
plt.hist(
x, bins=10, range=None, normed=False,
weights=None, cumulative=False, bottom=None,
histtype=u'bar', align=u'mid', orientation=u'vertical',
rwidth=None, log=False, color=None, label=None, stacked=False,
hold=None, **kwargs)
'''
画图,可以一次性把各参数设置好
x:数据 ,n维数组
bin:箱子的个数
粗暴的使用就是赋值数据即可
'''
plt.imshow(img,'gray')
'''
在 plt 窗口中显示一张图片
'''
  • cv2自带

    • cv2.circle(img, center, radius, color[, thickness[, lineType[, shift]]])

      • img:输入的图片data
      • center:圆心位置
      • radius:圆的半径
      • color:圆的颜色
      • thickness:圆形轮廓的粗细(如果为正)。负厚度表示要绘制实心圆。
      • lineType: 圆边界的类型。
      • shift:中心坐标和半径值中的小数位数。
    • cv2.line(img, pt1, pt2, color[, thickness[, lineType[, shift]]])

      • img:源图像
      • pt1,pt2:直线的两个端点
      • color:需要传入的颜色
      • thickness:线条的粗细,默认值是1
      • linetype:线条的类型,8 连接,抗锯齿等。默认情况是 8 连接。cv2.LINE_AA 为抗锯齿,这样看起来会非常平滑。
    • cv2.rectangle(img, pt1, pt2, color[, thickness[, lineType[, shift]]])

      • img:图片
      • pt1,pt2:互为对角线的两个端点
      • color:线条颜色
      • thickness:线条的粗细,默认值是1
      • linetype:线条的类型
      • shift:
    • putText(img, text, org, fontFace, fontScale, color[, thickness[, lineType[, shift]]])

      • img:图片

      • text:要添加的文字

      • org:文字添加到图片上的位置

      • fontFace:字体的类型

        • cv2.FONT_HERSHEY_SIMPLEX
      • fontScale:字体大小

        • 推荐1
      • color:字体颜色

      • thickness:字体粗细

img = cv2.imread(imgSrc0)
cv2.circle(img,(50,50),20,(0,0,255))
cv2.line(img,(30,90),(90,30),(0,255,0))
cv2.rectangle(img,(20,80),(80,300),(255,0,0))
cv2.putText(img,"Hello",(30,30),cv2.FONT_HERSHEY_SIMPLEX,1,(255,255,0));
showImg(img)
circle(img,Point(200,200),100,(0,255,0),5,8);
circle(img,Point(200,200),100,(0,255,0),-1,8);//-1画实心圆
line(img,Point(10,10),Point(100,250),Scalar(0,0,255),5,8);
rectangle(img,Point(300,300),Point(500,600),Scalar(255,0,0),3,8);
rectangle(img,Rect(500,500,100,100),Scalar(0,0,255),3,8);
putText(img,"china",Point(100,100),FONT_HERSHEY_COMPLEX,1,Scalar(0,0,255),1);

直方图

绘制直方图

  • hist = cv2.calcHist(img,channels,mask,histSize,ranges)

    • hist:一个 n*1 的二维矩阵
    • img
    • channels:指定哪个通道 ;img为彩色则为0-2,灰度图为01
    • mask:掩码,类似ROI,指定区域进行计算
    • histSize:bin的数目,箱子的个数;入[256]表示直方图的箱子数255
    • range:告知 的 像素范围 , 直方图的max_y值 ;如[0,256]表示区间[0,256)
    img = cv2.imread(imgSrc4,0)
    showImg(img)
    hist = cv2.calcHist([img],[0],None,[256],[0,256])
    plt.hist(img.ravel(),256)
    plt.show()
    
    histr = cv2.calcHist([img],[0],None,[256],[0,256])
    plt.plot(histr,color = 'b')
    plt.xlim([0,256])
    plt.show()
    
  • img.ravel()

    • 将img拉成一维数组 ,相当于变成直方图

mask 操作

  • cv2.bitwise_and(img1,img2,mask=None)

    • 图像位运算
    • img1,img2:参与运算的两个图像,可以输入图像或标量,标量可以为单个数值或一个四元组
    • mask:掩膜
img = cv2.imread(imgSrc1,0)
mask = np.zeros(img.shape[:2],np.uint8) # 与原图大小保持一致
mask[100:300,100:400] = 255
showImg(mask)

masked_img = cv2.bitwise_and(img,img,mask=mask) # 两个img与运算 然后只输出掩膜部分
showImg(masked_img)

均衡化

  • 原理:计算每一种像素值的概率,求出每个像素值的累积概率,再映射到 累积概率*255

  • 效果:令图像高亮,使得图像内各组分差异变大;缺点是会丢失一些细节信息

  • 技巧:不全局做均衡化,而是局部做均衡化。也就是,自适应均衡化

  • equ = cv2.equalizeHist(img)

    • 返回一个均匀化后的图像
equ = cv2.equalizeHist(img)
plt.hist(equ.ravel(),256)
plt.show()

自适应均衡化

  • cv2.createCLAHA(clipLimit, titleGridSize)

    • clipLimit:颜色对比度的阈值
    • titleGridSize:进行像素均衡化的网格大小,即在多少网格下进行直方图的均衡化操作
img = cv2.imread(imgSrc1,0)

# 自适应均衡化
clahe = cv2.createCLAHE(clipLimit=2.0,tileGridSize=(8,8)) # 默认设置
res_clahe = clahe.apply(img)

# 全局均衡化
equ = cv2.equalizeHist(img)

res = np.hstack((res_clahe,equ))
showImg(res)

多线程

  • 互斥锁 需要threading模块

  • lock = threading.Lock()

    • 互斥锁对象,全局唯一
  • lock.acquire()

    • 获取锁。未获取到会阻塞程序,直到获取到锁才会往下执行
    • 和release配套使用,否则程序堵塞就寄了
  • lock.release()

    • 释放锁,归还锁,其他人可以拿去用了
import threading
lock = threading.Lock()

lock.acquire()
v1 = readVideo(0)
lock.release()

lock.acquire() # 拿到锁了才会继续执行 ,拿不到就卡在这里
v2 = readVideo(1)
lock.release()

Zbar

  • barcodes = pyzbar.decode(img)

    • 对图片进行解码 并直接返回解码内容

    • data:二维码存储的信息,注意需要解码为utf-8格式

    • type:二维码类型

    • rect:二维码外接矩形(x,y,w,h)

    • polygon:二维码四点坐标

    • quality:

    • orientation:二维码方向

      • up: 正面
# barcodes内容
barcodes =  [Decoded(data=b'Hello Python',
                     type='QRCODE',
                     rect=Rect(left=430, top=30, width=340, height=340),
                     polygon=[Point(x=430, y=30),
                              Point(x=430, y=368),
                              Point(x=770, y=370),
                              Point(x=768, y=30)],
                     quality=1,
                     orientation='UP'),
             Decoded(data=b'Hello Zbar',
                     type='QRCODE',
                     rect=Rect(left=30, top=30, width=340, height=340),
                     polygon=[Point(x=30, y=30),
                              Point(x=30, y=368),
                              Point(x=370, y=370),
                              Point(x=368, y=30)],
                     quality=1,
                     orientation='UP')]
# 示范代码
import pyzbar.pyzbar as pyzbar
img1 = cv2.imread("img/Hello+Zbar.png")
img2 = cv2.imread("img/Hello+Python.png")
img = np.hstack((img1,img2))
showImg(img)

barcodes = pyzbar.decode(img)
for barcode in barcodes:
    data = barcode.data.decode("utf-8")
    type = barcode.typy
    text = "data:{}\ntype:{}".format(data,type)
    print(text)
for barcode in barcodes:
    points = barcode.polygon # 左上角 左下角 右下角 右上角
    k = 5
    point0 = (points[0][0]-k,points[0][1]-k)
    point1 = (points[1][0]-k,points[1][1]+k)
    point2 = (points[2][0]+k,points[2][1]+k)
    point3 = (points[3][0]+k,points[3][1]-k)
    cv2.circle(img, point0, 2, (0, 0, 255))
    cv2.circle(img, point1, 2, (0, 0, 255))
    cv2.circle(img, point2, 2, (0, 0, 255))
    cv2.circle(img, point3, 2, (0, 0, 255))
showImg(img)

傅里叶变换

  • 基本概念

    • 高频:变化剧烈的灰度分量,例如边界
    • 低频:变化缓慢的灰度分类,例如大海
img = cv2.imread(imgSrc2,0)
showImg(img)
img_float32 = np.float32(img)

dft = cv2.dft(img_float32,flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)

magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))
magnitude_spectrum = (magnitude_spectrum ).astype(np.uint8) # 转换为 图像数据 的存储格式 uint8
showImg(magnitude_spectrum)

plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.show()
  • 滤波

    • 低通滤波器:只保留低频,会使得图像模糊
    • 高通滤波器:只保留高频,会使得图像细节增强
img = cv2.imread(imgSrc2,0)
showImg(img)
img_float32 = np.float32(img)

# 傅里叶变换
# cv2.dft() 返回的结果是双通道的 ( 实部,虚部 ),通常还需要转换成图像格式才能展示(0,255)像素值
dft = cv2.dft(img_float32,flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)

rows,cols = img.shape
crow,ccol = int(rows/2),int(cols/2) # 得到中心位置

# 低通滤波
mask_low = np.zeros((rows,cols,2),np.uint8) # 注意是三维的!
mask_low[crow-30:crow+30,ccol-30:ccol+30]=1 # 只保留中心周围的点

# 高通滤波
mask_high = np.ones((rows,cols,2),np.uint8) # 注意是三维的!
mask_high[crow-30:crow+30,ccol-30:ccol+30]=0 # 只保留中心周围的点

mask = mask_low # 选择滤波器

# 傅里叶逆变换
fshift = dft_shift * mask # mask 的取值是0或1
f_ishift = np.fft.ifftshift(fshift) # 将中心位置的频谱还原
img_back = cv2.idft(f_ishift)
img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1])# 还需要双通道合并
# img_back = 20*np.log(img_back)
# 对两个通道进行转换才能得到图像形式表达,由于转换后的值为非常小的数值,因此还要转换到 0-255 之间

plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Input Image'), plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(img_back,cmap='gray')
plt.title('Result'),plt.xticks([]),plt.yticks([])
plt.show()

showImg(img_back)

角点检测

  • cornerHarris更集成化,操作简单,但自由度低;速度快效果一般

  • goodFeaturesToTrack参数复杂,但是更自由

  • dst = cv2.cornerHarris(img,blockSize,ksize,k)

    • 数学原理:死去的数论开始攻击我,再加一个此时此刻劳资没学线代,白给三个小时
    • dst:处理后得到的每个像素点的自相似性,与原图大小一致的矩阵; 一般认为 *dst>0.01dst.max()** 为角点,系数越大越严格
    • img:数据类型为float32的图像
    • blockSize:移动窗口的大小
    • ksize:sobel算子的大小
    • k:取值参数为[0.04,0.06],一般取 0.04 ;原因涉及数学原理,开摆!
img = cv2.imread(imgSrc3)
showImg(img)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
dst = cv2.cornerHarris(gray,2,3,0.04)
img[dst>0.09*dst.max()] = (0,0,255)
showImg(img)
  • corners=cv.goodFeaturesToTrack(image, maxCorners, qualityLevel, minDistance, mask, blockSize, gradientSize[, corners[, useHarrisDetector[, k]]])

    • image: 输入图像,是八位的或者32位浮点型,单通道图像,所以有时候用灰度图
    • maxCorners: 返回最大的角点数,是最有可能的角点数,如果这个参数不大于0,那么表示没有角点数的限制
    • qualityLevel: 图像角点的最小可接受参数,质量测量值乘以这个参数就是最小特征值,小于这个数的会被抛弃
    • minDistance: 返回的角点之间最小的欧式距离
    • mask: 检测区域。如果图像不是空的(它需要具有CV_8UC1类型和与图像相同的大小),它指定检测角的区域
    • blockSize: 用于计算每个像素邻域上的导数协变矩阵的平均块的大小
    • useHarrisDetector:选择是否采用Harris角点检测,默认是false
    • k: Harris检测的自由参数

霍夫检测

  • lines = cv2.HoughLines(image_edge, rho, theta, threshold[, lines[, srn[, stn[, min_theta[, max_theta]]]]])

    • image_edge:二值图;之所谓为edge是因为最好经过边缘或阈值处理

    • rho:像素之间的距离

    • theta:直线角度范围,2pi/(pi/180) = 360°

    • threshold:一条预选直线上的最少像素点个数,不满足会被筛出

    • lines:(rho, theta)的列表

      • 如果距离是1,180个像素即可生成直线,如果距离是2,至少360个像素才可以生成直线
    • 原理详见,(写得极好!) 图解cv2.HoughLines() 和 cv2.HoughLinesP()原理和代码

    • 霍夫逆变换 : 将(rho,theta)转化为(x,y)

      • a = cos(theta)
      • b = sin(theta)
      • x0 = a * rho , y0 = b * rho
      • x1 = x0 + 1000 * (-b) , y1 = y0 + 1000 * a
      • x2 = x0 - 1000 * (-b) , y2 = y0 -1000 * a
# 霍夫线检测
orig = cv2.imread(imgSrc4)
img = cv2.cvtColor(orig,cv2.COLOR_BGR2HSV)

minh , mins , minv = 22 ,10,22
maxh , maxs , maxv = 136,150,121
img = cv2.inRange(img,(minh,mins,minv),(maxh,maxs,maxv))

element = cv2.getStructuringElement(cv2.MORPH_RECT, (3,6))
img = cv2.morphologyEx(img, cv2.MORPH_ERODE, element)
element = cv2.getStructuringElement(cv2.MORPH_RECT, (2,2))
img = cv2.morphologyEx(img, cv2.MORPH_DILATE, element)
img = cv2.morphologyEx(img, cv2.MORPH_ERODE, element)

after_canny = cv2.Canny(img,100,200,3)
showImg(after_canny)

lines = cv2.HoughLines(after_canny, 1, np.pi/180, 160)

for line in lines:
    rho , theta = line[0][0] , line[0][1] # line 是一个数组的数组
    a , b = np.cos(theta) , np.sin(theta)
    x0 , y0 = a*rho , b*rho
    point1 = ( int(x0+1000*(-b)) , int(y0+1000*a) )
    point2 = ( int(x0-1000*(-b)) , int(y0-1000*a) )
    cv2.line(orig,point1,point2,(55,100,195),2,8)

showImg(orig)
vector<Vec2f> lines;  //定义矢量结构lines用于存放得到的线段矢量集合
HoughLines(src, lines, 1, CV_PI/180, 150);//超过150的线段才被检测到
//依次在图中绘制出每条线段
for (size_t i = 0; i < lines.size(); i++)
{
	float rho=lines[i][0],theta=lines[i][1];
    Point pt1,pt2;
    double a=cos(theta),b=sin(theta);
    double x0=a*rho,y0=b*rho;
    pt1.x=cvRound(x0+1000*(-b));//cvRound(double value) 函数:对一个double型数字四舍五入,返回一个整数
    pt1.y=cvRound(y0+1000*(a));
    pt2.x = cvRound(x0 - 1000*(-b));  
    pt2.y = cvRound(y0 - 1000*(a));  
    line(dst,pt1,pt2,Scalar(55,100,195),2,8);
}
  • circles = cv2.HoughCircles(image, method, dp, minDist, [circles=None], param1=None, param2=None, minRadius=None, maxRadius=None)

    • image 原始图像
    • method 目前只支持cv2.HOUGH_GRADIENT
    • dp 图像解析的反向比例。1为原始大小,2为原始大小的一半
    • minDist 圆心之间的最小距离。过小会增加圆的误判,过大会丢失存在的圆
    • param1 Canny检测器的高阈值 , 所以似乎是自带一个Canny?
    • param2 检测阶段圆心的累加器阈值。越小的话,会增加不存在的圆;越大的话,则检测到的圆就更加接近完美的圆形
    • minRadius 检测的最小圆的半径,就是检测到的圆半径取值为[minRadius,maxRadius]
    • maxRadius 检测的最大圆的半径
    • circles 和霍夫线一样是一个float数组的数组,例如 [[306.5,222.5,193.4]]
# 霍夫圆检测
orig = cv2.imread(imgSrc1)
img = cv2.GaussianBlur(orig,(7,7),2)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
thres = cv2.threshold(gray, 25, 255, cv2.THRESH_BINARY)[1]
thres = cv2.GaussianBlur(thres,(7,7),2)

circles = cv2.HoughCircles(thres,cv2.HOUGH_GRADIENT,1,20,100,80,50,0)

for circle in circles:
    center = ( int(circle[0][0]) , int(circle[0][1]) )
    radius = int(circle[0][2])
    cv2.circle(orig,center,radius,(0,0,255),2)
showImg(orig)
vector<Vec3f> circles;
HoughCircles(src, circles, CV_HOUGH_GRADIENT,1.5, 10, 200, 100);
for(size_t i = 0; i<circles.size(); i++)
{
    Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
    int radius = cvRound(circles[i][2]);
    circle(dst, center, 3, Scalar(0, 0, 255), -1, 8,0);//设置为-1时,画实心圆
    circle(dst, center, radius, Scalar(0, 255, 0), 3, 8,0);//画空心圆
}

尺度空间

  • void

特征匹配

  • void

图像金字塔

高斯金字塔

  • 向下取样法(变小)
up = cv2.pyrUp(img)
  • 向上取样法(变大)
up = cv2.pyrUp(img)

拉普拉斯金字塔

  • L = G - PyrUp( PyrDown(G) )
  • = 原图 - 向下在向上
img = cv2.imread(imgSrc1,1)
down = cv2.pyrDown(img)
up = cv2.pyrUp(down)
l = img-up
res = np.hstack((img,l))
showImg(res)

Tesseract-OCR 的安装及配置

Tesseract-OCR 的安装及配置

引言

就单纯想做个文字提取

结果给我整出一堆红字

error_code!

问题分析

可以看到,编译器提示 tesseract 没有安装,或者没有找到文件

问题解决

  • 1)配置环境变量:没有将tesseract 添加进全局变量

  • 2)windows路径歧义:

    • 在python解释器目录下的项目文件夹中

找到site-packages/tesseract目录下的 pytesseract.py

将cmd路径改为绝对路径,记得加上防转义!

cmd

最后

可以看到识别效果非常的好哇!

doc

code