OpenCV入门

概述

本文整理自BiliBli的《孔工码字》, 这是一个很好的视频号。讲的非常好,整理在这里,自己学习。
他的Gitee地址:https://gitee.com/kongfanhe

OpenCV

OpenCV是Intel公司开源的计算机视觉工具,它实现了绝大多数图像处理和计算机视觉的算法,它轻量,高效,开源的特点,让它成为了业界最广泛的计算机视觉工具

安装openvc

pip install opencv-python

素材

本文用到下列素材:
bookpage.jpg
opencv_logo.jpg
plane.jpg
poker.jpg

案例1:图片的维度与通道

代码

import cv2,os

print(cv2.getVersionString())

# 读取图片文件
dir = os.path.dirname(os.path.abspath(__file__))
path = os.path.join(dir, "opencv_logo.jpg")
image = cv2.imread(path)
# 打印出加载图片的维度,例如:(250,250,3)
# 250,250: 图片的横行和纵列
# 3:三原色彩色通道
print(image.shape)


cv2.imshow("image", image)
cv2.waitKey()

补充

image是加载的图片,它有三个维度,分别是

案例2:图像的彩色通道

概述

计算机对图像色彩的描述,普遍使用了RGB三原色原理。任何颜色,用RGB按一定比例混合而成。
对OPCV来说,存储一张彩色图片,等同于存储三张灰度图。他们被存储在OpenCV数据的第三个维度上,灰度范围是0~255.
OpenCV对颜色的存储顺序是BGR, 与常见的RGB相反。
当显示器渲染图片时,计算机会取出三张灰度图,分别投影到显示器的蓝色,绿色和红色的LED芯片上,渲染出彩色画面

代码

import cv2,os

dir = os.path.dirname(os.path.abspath(__file__))
path = os.path.join(dir, "opencv_logo.jpg")
image = cv2.imread(path)
# 使用 索引,分别读取三张灰度图
# 分别显示在3各窗口中
cv2.imshow("blue", image[:, :, 0])
cv2.imshow("green", image[:, :, 1])
cv2.imshow("red", image[:, :, 2])
# OpenCV提供一种彩色图片的灰度变换算法, 把三个彩色通道的图像做平方和加权平均
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# gray描绘了BGR三原色的平均,也描述的图片的明暗分布
# 如果是相机CMOS获得的图像,也可以说是CMOS芯片上接收光子的分布图
# 在计算机视觉领域,我们把变幻后的gray成为灰度图, 大量图像算法,是基于灰度图进行操作的
cv2.imshow("gray", gray)

cv2.waitKey()

执行结果

案例3:图像的裁剪

代码

import cv2,os

dir = os.path.dirname(os.path.abspath(__file__))
path = os.path.join(dir, "opencv_logo.jpg")
image = cv2.imread(path)
# 使用图像的索引号,取出图像的一部分
# 索引顺序:先横行,后纵列
crop = image[10:170, 40:200]

cv2.imshow("crop", crop)
cv2.waitKey()

执行结果

OpenCV索引顺序:先横行,后纵列

案例4:使用OpenCV的绘制功能

代码

使用numpy数据包

import cv2
import numpy as np
# 创建一个黑色画布
image = np.zeros([300, 300, 3], dtype=np.uint8)
# 画一个线段:起始点坐标,终结点坐标,颜色,粗细
cv2.line(image, (100, 200), (250, 250), (255, 0, 0), 2)
# 画一个矩形框:
cv2.rectangle(image, (30, 100), (60, 150), (0, 255, 0), 2)
# 画一个圆: 圆心坐标,半径,颜色,粗心
cv2.circle(image, (150, 100), 20, (0, 0, 255), 3)
# 添加文字:
cv2.putText(image, "hello", (100, 50), 0, 1, (255, 255, 255), 2, 1)

cv2.imshow("image", image)
cv2.waitKey()

执行结果

案例5:均值滤波

概述

案例中使用的图片plane.jpg,噪点比较严重,
使用均值滤波器处理图片中的噪点

代码

import cv2,os

dir = os.path.dirname(os.path.abspath(__file__))
path = os.path.join(dir, "plane.jpg")
image = cv2.imread(path)

# 使用高斯滤波器: 
# 高斯内核设置为5各像素
# sigmaY设置为0, sigma由内核大小来决定
# 噪点减少,也破坏了一些图像细节
gauss = cv2.GaussianBlur(image, (5, 5), 0)

# 使用中值滤波器
# 设置内核为5各像素
# 噪点进一步减少
median = cv2.medianBlur(image, 5)

cv2.imshow("image", image)
cv2.imshow("gauss", gauss)
cv2.imshow("median", median)

cv2.waitKey()

执行结果

正常情况下,会遇到干净环境中,少数几个噪点,使用均值滤波消除噪点, 方便后面的图像处理操作

案例6:图像特征的提取

代码

import cv2,os

dir = os.path.dirname(os.path.abspath(__file__))
path = os.path.join(dir, "opencv_logo.jpg")
image = cv2.imread(path)
# 把彩色图像,转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 获取图像中的特征点
# 最多返回500个点
# 点的质量优于0.1
# 特征点之间的距离,大于10个像素
corners = cv2.goodFeaturesToTrack(gray, 500, 0.1, 10)

# 找到特征点后,把每个特征点标记出来
# 当前图片,识别出来的都是图像的转角
# 转角是最简单的图像特征
# 提出算法,非常高效,有广泛的应用
# 例如: 立体相机三位重建时,需要大量特征提取和匹配
for corner in corners:
    x, y = corner.ravel()
    cv2.circle(image, (int(x), int(y)), 3, (255, 0, 255), -1)

cv2.imshow("corners", image)
cv2.waitKey()

执行结果

案例7:模板匹配

概述

本案例目标是匹配扑克牌中的菱形
找出图片中的9个菱形
图片中的菱形

代码


import cv2,os
import numpy as np

dir = os.path.dirname(os.path.abspath(__file__))
path = os.path.join(dir, "poker.jpg")
image = cv2.imread(path)

# 把彩色图转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 选取图像的一个区域作为匹配模板, 这个区域刚好包含一个菱形
template = gray[75:105, 235:265]

# 传入待检测图像gray和模板template
# 使用标准相关匹配算法
# 即: 把图像和模板各自标准化,再来计算匹配度,可以保证匹配结果,不受光照强度的影响
match = cv2.matchTemplate(gray, template, cv2.TM_CCOEFF_NORMED)
# 找出匹配系数大于0.9的匹配点
locations = np.where(match >= 0.9)

# 求出模板图案的长和宽,方便作图
w, h = template.shape[0:2]
# 循环遍历匹配点,画出矩形框
for p in zip(*locations[::-1]):
    x1, y1 = p[0], p[1]
    x2, y2 = x1 + w, y1 + h
    cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)

cv2.imshow("image", image)
cv2.waitKey()

执行结果

运行效果:

补充

当前的匹配算法对图像大小敏感,小点的菱形,没有匹配出来。
想匹配不同大小的图片,可以放大,缩小图像, 匹配多次

案例8:图像的梯度算法

概述

特征点的提取和匹配背后,都使用了图像的梯度算法
图像梯度就是图像的明暗变化
例如:我们可以分别计算图像按水平和垂直方向的明暗度变化,再取这两个变化的平方和, 就得到了梯度

它和地面的梯度是一样的,只不过,地面的高低起伏变成了图像的明暗变化。

代码


import cv2,os

dir = os.path.dirname(os.path.abspath(__file__))
path = os.path.join(dir, "opencv_logo.jpg")
gray = cv2.imread(path, cv2.IMREAD_GRAYSCALE)

# 梯度算法接收灰度图
# 先来计算图像的Laplac算值,大致对应图像的二阶导数
# Laplac算值给出图像的明暗变化趋势
# 比如均一的背景区域变成了黑色,
# 有明暗变化的部分,比如边缘等,变成了白色
# 一个几何图形的边缘,往往有剧烈的明暗变化
# 所以梯度算法也常常用来检测边缘
laplacian = cv2.Laplacian(gray, cv2.CV_64F)

# Canny边缘检测算法
# 使用梯度区间来定义边缘,
# 比如梯度区间100~200, 
# 如果某个像素的地图大于200,可以确定它是一个边缘
# 因为它周围的明暗变化足够强烈
# 反过来,如果梯度小于100, 可以确定不是边缘,因为周围没有明暗变化
# 如果梯度在100~200之间,需要看这个像素,是否和已知的边缘像素相连
# 如果相连,判断为边缘,否则,不是, 这个过程有点像扫雷
# 使用Canny算法检测边缘,可以看见边缘被完美的检测出来
canny = cv2.Canny(gray, 100, 200)

cv2.imshow("gray", gray)
cv2.imshow("laplacian", laplacian)
cv2.imshow("canny", canny)

cv2.waitKey()

执行结果

补充:

Canny边缘检测算法,使用梯度区间来定义边缘,
比如梯度区间100~200,
如果某个像素的地图大于200,可以确定它是一个边缘,因为它周围的明暗变化足够强烈
反过来,如果梯度小于100, 可以确定不是边缘,因为周围没有明暗变化
如果梯度在100~200之间,需要看这个像素,是否和已知的边缘像素相连,如果相连,判断为边缘,否则,不是

这个过程有点像扫雷

先确定显而易见的像素,剩下的像素由确定好的像素辅助判断。
使用Canny算法检测边缘,可以看见边缘被完美的检测出来

案例9:阈值算法

概述:

阈值算法也叫二值化算法,它把灰度图像分为灰与白
阈值的阈是门槛的意思,通俗的讲,门槛下面是黑色,门槛上面是白色
在阈值算法的世界里,非黑即白。
我们的案例图片中,有明有暗。
bookpage.jpg

代码

import cv2,os

dir = os.path.dirname(os.path.abspath(__file__))
path = os.path.join(dir, "bookpage.jpg")
gray = cv2.imread(path, cv2.IMREAD_GRAYSCALE)

# 首先定义一个固定阈值10,最大灰度255
# 可以取得类似photoshop类似的效果
ret, binary = cv2.threshold(gray, 10, 255, cv2.THRESH_BINARY)
# 当前图片光照分布不均,难以确定固定阈值, 把所有文字分离出来
# OpenCV提供了一种自适应阈值算法, 把图片分成很多区域, 每个区域独立计算阈值
# 光照强的阈值大,光照弱的阈值小
binary_adaptive = cv2.adaptiveThreshold(
    gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 115, 1)
# 大金算法(OPSU):
# 该算法不需要认为确定阈值,会自动计算恰当的阈值
# 使得分离出的两个灰度分布差异最大化, 它实际上是一个聚类分析算法
# 在很多场景里,该算法很有效
ret1, binary_otsu = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

cv2.imshow("gray", gray)
cv2.imshow("binary", binary)
cv2.imshow("adaptive", binary_adaptive)
cv2.imshow("otsu", binary_otsu)

cv2.waitKey()

执行结果

补充

当前图片光照分布不均,难以确定固定阈值, 把所有文字分离出来
OpenCV提供了一种自适应阈值算法, 把图片分成很多区域, 每个区域独立计算阈值,光照强的阈值大,光照弱的阈值小

大金算法(OTSU):
该算法不需要认为确定阈值,会自动计算恰当的阈值
使得分离出的两个灰度分布差异最大化, 它实际上是一个聚类分析算法
在很多场景里,该算法很有效

案例10:图像的形态学算法(腐蚀和膨胀)

概述

图像的形态学算法有很多,本节主要实现腐蚀(Erosion)和膨胀(Dilation)

代码

import cv2, os
import numpy as np

dir = os.path.dirname(os.path.abspath(__file__))
path = os.path.join(dir, "opencv_logo.jpg")
gray = cv2.imread(path, cv2.IMREAD_GRAYSCALE)

# 本节操作针对二值化图像,需要做阈值处理,使用反向阈值
# 讲白色背景黑色图案修改为黑色背景白色图案
_, binary = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
# 定义一个操作核, 是5*5像素的正方形
kernel = np.ones((5, 5), np.uint8)

# 使用kernel腐蚀binary图像
# 图标变瘦了, 就像使用腐蚀性液体把图案的边缘腐蚀掉了,这也是腐蚀名称的由来
erosion = cv2.erode(binary, kernel)
# 与腐蚀对应的是膨胀,使用同样kernel
# 看到图标变胖,这也是膨胀名称的由来
# 腐蚀和膨胀对清理图像的边缘细节很有帮助
# 如果交替使用腐蚀和膨胀,可以获得更多的形态学变化
# 比如封闭后打开图案的空腔等
dilation = cv2.dilate(binary, kernel)

cv2.imshow("binary", binary)
cv2.imshow("erosion", erosion)
cv2.imshow("dilation", dilation)

cv2.waitKey()

执行结果

案例11:调用电脑中的摄像头

代码

import cv2

# 获取摄像头的指针,需要传入摄像头序号
capture = cv2.VideoCapture(0)

while True:
    ret, frame = capture.read()
    cv2.imshow("camera", frame)
    key = cv2.waitKey(1)
    if key != -1:
        break
# 释放摄像头指针
capture.release()
posted @   荣--  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器
点击右上角即可分享
微信分享提示