计算机视觉无痛入门及学习笔记

计算机视觉快速入门学习笔记

伟大的事业需要有伟大的理论来指导 没有理论的撸代码都是耍流氓

一. 人工智能概述

  • 图灵测试:1950年图灵提出图灵测试,即一个人在不接触对方的情况下,通过一种特殊的方式,和对方进行一系列的问答,如果在相当长时间内,他无法根据这些问题判断对方是人还是计算机,那么,就可以认为这个计算机具有同人相当的智力,即这台计算机是能思维的。

  • 通过图灵测试的机器称为具有“人类智能”,至今没有任何人工智能设备通过图灵测试。

  • 达特茅斯会议:1956年是人工智能元年。

  • 人工智能:如果机器和程序表现出了类似人学习或其他方面的能力,我们就可以称其实现了人工智能。

二. 机器学习

  • 机器学习:是从数据中自动分析获得模型,并利用模型对未知数据进行预测。(数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已

  • 机器学习工作流程:

    • 1.获取数据
    • 2.数据基本处理
    • 3.特征工程
    • 4.机器学习(模型训练)
    • 5.模型评估
      • 结果达到要求,上线服务
      • 没有达到要求,重新上面步骤
  • 机器学习与人工智能的关系:机器学习是实现人工智能的一个途径。

三. 图像学基础

  • 三原色光模式(RGB color model):又称RGB颜色模型或红绿蓝颜色模型,是一种加色模型,将红绿蓝三原色的色光以不同的比例相加,以合成产生各种色彩光。

    • (三原色的原理不是出于物理原因,而是由于生理原因造成的。)
  • 计算机显示模式:24比特模式(8,8,8)——使用三个8位无符号整数(0到255)表示红色、绿色和蓝色的强度。(对于OpenCV来说,三通道的排列顺序是BGR而非RGB)。

    • 计算机中像素点的表示:一个链表[0,0,255]
    • 计算机中线的表示:二维链表数组[[0,0,255],[0,0,255]]
    • 计算机中面的表示:三维链表数组[
      [[0,0,255],[0,0,255]], //红色
      [[255,0,0],[255,0,0]], //蓝色
      [[0,255,0],[0,255,0]], //绿色
      ]
  • 安装OpenCV库的代码:pip install opencv-python(如果太慢,可以更换国内镜像源,如豆瓣的可以使用命令 -i https://pypi.douban.com/simple

  • 导入OpenCV库的代码:import cv2

  • 计算机中彩色图片的表示:用一个三维的数组或者说列表就可以很简单地表示出计算机中的彩色图片。

  • 计算机中灰度图像的表示:用一个二维数组就可以表示灰度图像,使用灰度图像可以起到降维的作用,方便计算机快速处理。

四. 计算机视觉与物联网

  • 计算机视觉是使用计算机及相关设备对生物视觉的一种模拟。 它的主要任务就是通过对采集的图片或视频进行处理以获得相应场景的三维信息,就像人类和许多其他类生物每天所做的那样。 计算机视觉是一门关于如何运用照相机和计算机来获取我们所需的,被拍摄对象的数据与信息的学问。 形象地说,就是给计算机安装上眼睛(照相机)和大脑(算法),让计算机能够感知环境。
    • 简单来说,计算机视觉是一门研究如何使机器“看”并“理解”的科学,它的发展经历了从看到看懂再到识别和理解的这样一个过程。
  • 物联网(Internet of Things,简称IOT)是指通过 各种信息传感器、射频识别技术、全球定位系统、红外感应器、激光扫描器等各种装置与技术,实时采集任何需要监控、 连接、互动的物体或过程,采集其声、光、热、电、力学、化 学、生物、位置等各种需要的信息,通过各类可能的网络接入,实现物与物、物与人的泛在连接,实现对物品和过程的智能化感知、识别和管理。物联网是一个基于互联网、传统电信网等的信息承载体,它让所有能够被独立寻址的普通物理对象形成互联互通的网络。
    • 随着互联网和芯片技术的发展,很多的设备我们都可以给他装上芯片和软件系统然后连上网,这个其实就是所谓的物联网,是新瓶装旧酒的概念。

五. 人脸识别案例实战

1. 安装需要的包

  • pip install cmake
  • pip install scikit-image
  • pip install dlib
  • pip install face_recognition

如果安装中没有出现红色的报错就耐心等待吧,一般情况下如果你的网络是OK的都会安装成功的(安装dlib可能会有一些报错,自行百度解决吧,如果没有报错就多等等吧)。

2. 人脸识别实现的步骤

  1. 定位到人的脸部,并用绿色框把人的脸部框住;
  2. 读取数据库中的人名和面部特征;
  3. 匹配拍摄到人的脸部特征和数据库中的面部特征,并用用户姓名标识;
  4. 定位和锁定目标任务,改用红色框把目标任务的脸部框住。

3. 完整代码加注释

# 总体任务
# 一: 打开摄像头,读取摄像头拍摄到的画面
# 定位到画面中的人脸,并用绿色框框出,并标识姓名

# 二: 读取数据库中的人名和面部特征

# 三: 用拍摄到人的脸部特征和数据库中的面部特征匹配
# 在用户头像的绿框上标识出用户的姓名,未知用户统一用Unknown

# 四: 定位和锁定目标人物,并用红色框把目标人物的脸部框住

import os
import cv2

import face_recognition

# 1. 准备工作
face_dataset = 'dataset'
user_names = []  # 存用户姓名
user_faces_encodings = []  # 存用户面部特征向量(与姓名一一对应)
target_names = ['ZhangWei','zhangwei','Zhang Yida']
# 2. 正式工作
# 2.1 得到dataset文件夹下所有的文件名
files = os.listdir('dataset')
# 2.2 循环读取文件名进行进一步的处理
for image_name in files:
    # 2.2.1 截取文件名作为用户名存入user_names的列表中
    user_name, _ = os.path.splitext(image_name)
    user_names.append(user_name)
    # 2.2.2 读取图片文件中的面部特征信息存入users_faces_encodings列表中
    image_file_name = os.path.join(face_dataset, image_name)
    image_file = face_recognition.load_image_file(image_file_name)
    face_encoding = face_recognition.face_encodings(image_file)[0]
    user_faces_encodings.append(face_encoding)

# 1. 打开摄像头,获取摄像头对象
cap = cv2.VideoCapture(0)  # 0代表第一个摄像头
cap.set(5,30)
# 2. 循环不停地去获取摄像头拍摄到的画面,并做进一步的处理
while True:
    # 2.1 获取摄像头拍摄到的画面
    ret, img = cap.read()
    # 2.2 从拍摄到的画面中提取出人的脸部所在区域(可能会有多个)
    # 2.2.1 返回识别到的人脸的位置(对角线两点确定一个矩形)
    # face_locations = ['第一个人脸所在的区域', '第二个人脸所在的区域', ...]
    face_locations = face_recognition.face_locations(img)
    # 2.2.2 从所有人的头像所在区域提取面部特征
    # face_encodings = ['第一个人的面部特征', '第二个人的面部特征', ...]
    face_encodings = face_recognition.face_encodings(img, face_locations)
    # 2.2.3 定义用于存储拍摄到的用户的姓名的列表,并遍历face_encodings, 和之前数据库中面部特征做匹配
    # names = ['第一个人的姓名', '第二个人的姓名', ...]
    names = []
    for face_encoding in face_encodings:
        # compare_faces(['面部特征1', '面部特征2', ...], 未知的面部特征),返回一个数组,在匹配的地方是True,不匹配的地方是False
        matchs = face_recognition.compare_faces(user_faces_encodings, face_encoding)
        name = 'Unknown'
        for index, is_match in enumerate(matchs):
            if is_match:
                name = user_names[index]
                break
        names.append(name)
    # 2.3 循环遍历人的脸部所在的区域,并画框(颜色BGR格式,最后一个参数为线条粗细),标识人的姓名
    # zip (['第一个人的位置', '第二个人的位置',...], ['第一个人的姓名', '第二个人的姓名',...]),在遍历时会一一对应起来遍历
    for (top, right, bottom, left), name in zip(face_locations, names):
        color = (0, 255, 0)  # 默认绿色框加绿色标识
        if name in target_names:
            color = (0, 0, 255)  # 目标人物红色框加红色标识
        cv2.rectangle(img, (left, top), (right, bottom), color, 2)
        font = cv2.FONT_HERSHEY_DUPLEX
        cv2.putText(img, name, (left, top-10), font, 0.5, color, 1)
    # 2.4 通过OpenCV将拍摄到的并画了框的画面展示出来
    cv2.imshow("video_image", img)
    # 2.5 设定按下'q'键(英文输入法下的小写)退出循环,ord()返回对应字符的ASCII码
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
# 3. 退出程序的时候,释放摄像头并关闭所有窗口
cap.release()
cv2.destroyAllWindows()

4. 最终结果演示

可以实现摄像头实时显示,数据库中图片越多,特征越多,识别结果越准确。

计算机视觉无痛入门及学习笔记

六. 运动传感器案例实战

1. 利用差分法实现对运动物体的探测

差分法原理:

帧差法是最为常用的运动目标检测和分割方法之一,基本原理就是在图像序列相邻两帧或三帧间采用基于像素的时间差分通过闭值化来提取出图像中的运动区域。首先,将相邻帧图像对应像素值相减得到差分图像,然后对差分图像二值化,在环境亮度变化不大的情况下,如果对应像素值变化小于事先确定的阂值时,可以认为此处为背景像素:如果图像区域的像素值变化很大,可以认为这是由于图像中运动物体引起的,将这些区域标记为前景像素,利用标记的像素区域可以确定运动目标在图像中的位置。由于相邻两帧间的时间间隔非常短,用前一帧图像作为当前帧的背景模型具有较好的实时性,其背景不积累,且更新速度快、算法简单、计算量小。算法的不足在于对环境噪声较为敏感,阈值的选择相当关键,选择过低不足以抑制图像中的噪声,过高则忽略了图像中有用的变化。对于比较大的、颜色一致的运动目标,有可能在目标内部产生空洞,无法完整地提取运动目标。它仅仅适应于相机静止的情况。

2. 准备微信公众平台测试号并安装HTTP库 requests

  • (1)访问微信公众平台网站 https://mp.weixin.qq.com

  • (2)服务号开发文档

  • (3)开始开发->接口测试号申请->进入微信公众账号测试号申请系统

  • (4)扫码登录即可

  • (5)安装HTTP库 requests: pip install requests

3. 编写微信发送通知的代码模块

import json

import requests


class WxTools():

    def __init__(self, myappid, myappsecret):
        self.app_id = myappid
        self.app_secret = myappsecret

    # 1. 获取access_token
    # https请求方式: GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
    def get_access_token(self):
        url = f'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={self.app_id}&secret={self.app_secret}'
        resp = requests.get(url).json()
        access_token = resp.get('access_token')
        return access_token

    # 2. 利用access_token发送微信的通知
    # http请求方式: POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
    def send_wx_customer_msg(self, open_id, msg="有人闯进了你的家"):
        url = f'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={self.get_access_token()}'
        req_data = {
            "touser": open_id,
            "msgtype": "text",
            "text":
            {
                 "content": msg
            }
        }
        requests.post(url, data=json.dumps(req_data, ensure_ascii=False).encode('utf-8'))


if __name__ == "__main__":
    myAppId = 'wx810664b7b6822792'  # 请改成自己得到的appID
    myAppSecret = 'bda74affba1edb48bcc7b882fd2e4369'   # 请改成自己得到的appsecret
    myOpenID = 'o6h6p5xY9ZiPRH80GcgRzTIHXjk0'   # 请改成自己的openID
    wx_tools = WxTools(myAppId, myAppSecret)  # 实例化对象并初始化
    wx_tools.send_wx_customer_msg(myOpenID)  # 调用函数进行消息的发送,如果想发送不同的消息请加上参数msg的内容

4. 编写运动检测的代码

import cv2
import datetime

from wx_notice import WxTools


cap = cv2.VideoCapture(0)
background = None  # 指定背景,作为第一幅画面进行差分法的比较
es = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 4))
is_send_msg = False  # 定义一个标志,当已经发送过通知后就不再继续发送通知了,即探测到运动物体只发送一次通知

myAppId = 'wx810664b7b6822792'  # 请改成自己得到的appID
myAppSecret = 'bda74affba1edb48bcc7b882fd2e4369'   # 请改成自己得到的appsecret
myOpenID = 'o6h6p5xY9ZiPRH80GcgRzTIHXjk0'   # 请改成自己的openID

while True:
    ret, img = cap.read()

    # 将摄像头拍摄到的画面转换为灰度图像,并使用高斯滤波消除图像中的噪声
    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    gray_img = cv2.GaussianBlur(gray_img, (25, 25), 3)

    if background is None:
        background = gray_img
        continue

    diff = cv2.absdiff(background, gray_img)    # 应用差分法求背景图和灰度图的差
    diff = cv2.threshold(diff, 50, 255, cv2.THRESH_BINARY)[1]  # 设置阈值即50,差的小于50即算一样,255代表不同的部分以白色表示
    # cv2.dilate(src, kernel, iteration)
    # 参数说明: src表示输入的图片, kernel表示方框的大小, iteration表示迭代的次数
    # 膨胀操作原理:存在一个kernel,在图像上进行从左到右,从上到下的平移,如果方框中存在白色,那么这个方框内所有的颜色都是白色
    diff = cv2.dilate(diff, es, iterations=3)  # 做形态学膨胀使前景物体放大,即小的裂缝变小看上去会更加连续

    # cv2.findContours(image, mode, method, contours=None, hierarchy=None, offset=None)
    # image 代表输入的图片。注意输入的图片必须为二值图片。若输入的图片为彩色图片,必须先进行灰度化和二值化。
    # mode 表示轮廓的检索模式,有4种:
    # cv2.RETR_EXTERNAL 表示只检测外轮廓。
    # cv2.RETR_LIST 检测的轮廓不建立等级关系。
    # cv2.RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
    # cv2.RETR_TREE 建立一个等级树结构的轮廓。
    # method为轮廓的近似办法,有4种:
    # cv2.CHAIN_APPROX_NONE 存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1 - x2),abs(y2 - y1)) <= 1。
    # cv2.CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息。
    # cv2.CHAIN_APPROX_TC89_L1和cv2.CHAIN_APPROX_TC89_KCOS使用teh - Chinlchain近似算法。
    # 函数返回两个值,一个是轮廓本身contours,还有一个是每条轮廓对应的属性hierarchy。
    contours, hierarchy = cv2.findContours(diff.copy(), cv2.RETR_EXTERNAL,
                                           cv2.CHAIN_APPROX_SIMPLE)
    is_detected = False
    for c in contours:
        # 轮廓区域>2000才认为探测到动的物体
        if cv2.contourArea(c) < 2000:
            continue
        (x, y, w, h) = cv2.boundingRect(c)
        cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
        is_detected = True
        if not is_send_msg:
            is_send_msg = True
            wx_tools = WxTools(myAppId, myAppSecret)
            wx_tools.send_wx_customer_msg(myOpenID, '有人闯入了你的家,请留意')

    if is_detected:
        show_text = "Motion: Detected"
        show_color = (0, 0, 255)  # 探测到显示红色以及探测到
    else:
        show_text = "Motion: Undetected"
        show_color = (0, 255, 0)  # 未探测到显示绿色以及未探测到

    # 在左上角显示当前探测状态(图像、文本、坐标、字体、字体大小、颜色、线条宽度)
    cv2.putText(img, show_text, (10, 20),
                cv2.FONT_HERSHEY_SIMPLEX, 0.5, show_color, 2)
    # 在左下角显示当前时间(图像、文本、坐标、字体、字体大小、颜色、线条宽度)
    cv2.putText(img,
                datetime.datetime.now().strftime("%A %d %B %Y %I:%M:%S%p"),
                (10, img.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX,
                0.35, show_color, 1)

    cv2.imshow('video', img)
    cv2.imshow('diff', diff)

    key = cv2.waitKey(1) & 0xFFf
    if key == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

5. 运行效果演示

PC端:

计算机视觉无痛入门及学习笔记

手机端:

计算机视觉无痛入门及学习笔记

有一点需要注意,检测的背景图是刚开始摄像头采集到的照片,所以尽量保证最开始的照片不要有过亮或者过暗的情况,都会对后续的检测造成影响。

上一篇:[face_算法篇]:常见的二分、递归、选择、插入、冒泡、快排、归并


下一篇:P1563 [NOIP2016 提高组] 玩具谜题