python视觉快速入门(OpenCV)4 画图与文字 5基本函

2022-11-27  本文已影响0人  python测试开发

画图与文字

画图

import cv2 as cv
import numpy as np

blank = np.zeros((512,512,3), np.uint8)
blank[:] = 0,255,0
img = cv.line(blank, (100,150), (300,300), (0,0,255),4) # 4像素粗的红色斜线
blank = cv.rectangle(blank, (250,30), (450,200), (255,0,0), 5) # 5像素粗的蓝色矩形
# 用黄色填充 可以用-1代替
blank = cv.rectangle(blank, (50,50), (80,100), (255,255,255), thickness=cv.FILLED) 
blank = cv.rectangle(blank, (50,300), (100, 500), (0,255,255), -1)
blank = cv.rectangle(blank, (100,100), (blank.shape[1]//4, blank.shape[0]//4), (0,255,255), -1)
# cv.circle(img: Mat, center: Any, radius: Any, color: Any, thickness: ... = ..., lineType: ... = ..., shift: ... = ...)
blank = cv.circle(blank, (400, 250), 40, (0,0,255), 3)
cv.imshow('Green', blank) 
cv.waitKey(0)

在图像上写文字

import cv2 as cv
import numpy as np

blank = np.zeros((512,1024,3), np.uint8)
cv.putText(blank, "wechat or dingtalk: pythontesting", (0, 200), cv.FONT_HERSHEY_TRIPLEX, 1.0, (0,255,0), 2,)
cv.imshow('Text', blank)
cv.waitKey(0)

cv.putText的参数:

字体列表。

FONT_HERSHEY_SIMPLEX = 0
FONT_HERSHEY_PLAIN = 1
FONT_HERSHEY_DUPLEX = 2
FONT_HERSHEY_COMPLEX = 3
FONT_HERSHEY_TRIPLEX = 4
FONT_HERSHEY_COMPLEX_SMALL = 5
FONT_HERSHEY_SCRIPT_SIMPLEX = 6
FONT_HERSHEY_SCRIPT_COMPLEX = 7

注意输出中文需要字体支持,后续会进行介绍。

基本函数

将图像转换为灰度

import cv2 as cv

img = cv.imread('Pictures/tiger.jpg', cv.IMREAD_UNCHANGED)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
cv.imshow('Gray', gray)
cv.waitKey(0)
cv.destroyAllWindows()

模糊图像

模糊图像本质上是去除图像中存在的一些噪音。例如,图像中可能有一些额外的元素,因为拍摄图像时光线不好,或者可能是相机传感器的一些问题等等。

我们可以减少这种噪音的方法之一是通过应用轻微的模糊。有太多的模糊技术,我们将在本书的高级部分进行探讨。但现在,我们只是要使用高斯模糊。

做到这一点的函数是高斯滤波器,正如我提到的。这个函数是cv.GaussianBlur()。它有以下参数:<img, kernel_size, border_type>。

内核大小是我们要应用的滤波器的内核的大小。它实际上是一个二乘二的元组,是OpenCV用来计算模糊图像的窗口大小。我们将在高级部分讨论这个问题,所以不用太担心这个。只要知道这个内核大小(ksize)必须是一个奇数。

为了增加你的模糊度,我们可以把内核大小从3乘3增加到7乘7。

import cv2 as cv

img = cv.imread('Pictures/tiger.jpg', cv.IMREAD_UNCHANGED)
blur = cv.GaussianBlur(img, (25,25),0)
cv.imshow('GaussianBlur', blur)
cv.waitKey(0)
cv.destroyAllWindows()

边缘

在边缘级联中,我们将寻找图像中存在的边缘。有很多边缘级联函数,但我们使用的是坎尼边缘检测器,它在计算机视觉领域很有名。

从本质上讲,这是一个多步骤的过程,涉及大量的模糊处理,然后涉及大量的分级计算和类似的东西。

import cv2 as cv

img = cv.imread('Pictures/desk.webp', cv.IMREAD_UNCHANGED)
edge = cv.Canny(img, 125, 175)
cv.imshow('Canny Edges', edge)
dilate = cv.dilate(edge, (5,5), iterations=2)
cv.imshow('Dilated', dilate)
eroded = cv.erode(dilate, (3,3), iterations=3)
cv.imshow('Eroded', eroded)
cv.waitKey(0)
cv.destroyAllWindows()

因此,我们创建了叫做edge的变量,并将其分配给cv.Canny(),我们传入图像和125和175的阈值。
我们可以通过在本质上模糊图像来减少一些边缘。我们这样做的方法是,不传递图像。我们传入模糊。试试这个,然后运行代码。正如你所看到的,图像中发现的边缘要少得多。这是一种方法,你可以通过应用大量的模糊来减少被发现的边缘的数量,或者通过应用轻微的模糊来摆脱一些边缘。

扩张

扩张是一种形态学程序,用于使图像看起来更好。这意味着你要按照特定的结构元素来增强图像的某些特征。现在我们要使用的结构元素是我们之前创建的这些边缘。我们将使用的函数叫做cv.dilate()。

我们传入了src,"blur",并取了一个内核大小,我们将其指定为5乘5,而且还取了2的迭代。

扩张的重点是增强图像的特征,你可以看到那是多么酷。

腐蚀

侵蚀可以去除物体边界上的像素。这是对这个扩张的图像进行侵蚀的一种方式,以找回这个结构元素。现在,它不会是完美的,但在某些情况下它会发挥作用。这个操作可以通过cv.erode()函数来完成。

该函数将接收扩张后的图像,内核大小为3乘3,并给定迭代次数为3。

正如你所看到的,显示的图像显示的是扩张后的图像的侵蚀版本。现在,它和扩张后的图像的结构元素不一样。但你正好可以把这个特征做出来。但你可以看到,两者之间的边缘和边缘的厚度都有细微的变化。

调整图像的大小

我们在上一章中来调整视频帧和图像的大小。现在,我们要谈谈为调整图像大小而设计的特殊函数,那就是cv.resize()函数。

import cv2 as cv

img = cv.imread('Pictures/desk.webp', cv.IMREAD_UNCHANGED)
resized = cv.resize(img, (500,500))
cv.imshow('Resized', resized)
cv.waitKey(0)
cv.destroyAllWindows()

cv.resize()函数接收一张要调整大小的图片,它将接收一个目标尺寸,我们将其设置为500乘500。

现在默认情况下,在后台会发生一个插值,这就是cv.INTER_AREA。如果你把图像缩小到比原来的尺寸小,这种插值方法就很有用。但在某些情况下,如果你试图放大图像并将图像缩放到一个更大的尺寸,你可能会使用INTER_LINEAR或INTER_CUBIC。现在cubic是它们中最慢的。但你得到的图像的质量比INTER_AREA或INTER_LINEAR要高得多。

裁剪图像

import cv2 as cv

img = cv.imread('Pictures/desk.webp', cv.IMREAD_UNCHANGED)
crop = img[50:200, 200:400]
cv.imshow('Cropped', crop)
cv.waitKey(0)
cv.destroyAllWindows()

图像变换

平移

平移的过程是沿着x轴和y轴移动图像。如果你正在做一个深度学习项目,你可以把这些平移作为添加到数据的步骤。平移是沿着x轴和y轴移动一个图像。因此,使用平移,你可以向上、向下、向左、向右移动图像,或以上述的任何组合。

与我们在前一章中讨论的其他函数不同,这个操作在OpenCV中没有内置的函数。

import cv2 as cv
import numpy as np

def translate(img, x, y):
    transMat = np.float32([[1,0,x],[0,1,y]])
    dimensions = (img.shape[1], img.shape[0])
    return cv.warpAffine(img, transMat, dimensions)


img = cv.imread('Pictures/tiger2.jpg', cv.IMREAD_UNCHANGED)
cv.imshow('Cats', img)

translated = translate(img, -100, 100)
cv.imshow("Translated", translated)
print(img.shape)
print(translated.shape)

cv.waitKey(0)

cv.warpAffine()函数。这个函数将把图像矩阵转为transMat,取其尺寸。

如果你的x值是负的,你基本上是把图像向左平移,负的y值意味着向上平移,正的x值意味着向右平移。而正如你所猜测的,正的y值向下移动。

旋转

import cv2 as cv
import numpy as np

def rotate(img, angle, rotPoint=None):
    (height,width) = img.shape[:2]
    if rotPoint is None:
        rotPoint = (width//2, height//2)

    rotMat = cv.getRotationMatrix2D(rotPoint, angle, 1.0)
    dimensions = (width, height)
    return cv.warpAffine(img, rotMat, dimensions)


img = cv.imread('Pictures/tiger2.jpg', cv.IMREAD_UNCHANGED)
cv.imshow('Cats', img)

rotated = rotate(img, 45)
cv.imshow("Rotated", rotated)

cv.waitKey(0)

通常情况下,旋转发生在中心的锚点,但你可以用OpenCV指定任何任意的点。它可以是任何一个角落,可以是向右10个像素,向下40个像素,你可以围绕这个点旋转图像。

而我们创建的旋转矩阵就像我们创建的平移矩阵一样。我们在中心传递了旋转点和围绕旋转的角度,也就是角度和缩放值。现在我们对缩放图像不感兴趣,所以我们将其设置为1.0。然后我们设置一个尺寸变量,等于宽度和高度。我们可以返回旋转后的图像,它个cv.warpAffine。

如果你想顺时针旋转这个图像,只要为这个角度指定负值,它就会把图像顺时针旋转。

调整大小

在上一章已经谈到了如何调整大小。

翻转图像

import cv2 as cv
import numpy as np

img = cv.imread('Pictures/tiger2.jpg', cv.IMREAD_UNCHANGED)
cv.imshow('Cats', img)

flip = cv.flip(img, -1)
cv.imshow("Flip", flip)

cv.waitKey(0)

翻转函数cv.flip,0为垂直翻转,1为水平翻转,-1为垂直和水平翻转。

轮廓检测

轮廓线基本上是物体的边界,是连接物体边界上的连续点的线或曲线。

当你进入形状分析和物体检测和识别时,轮廓线是很有价值的工具。从数学的角度来看,它们与边缘不一样。在大多数情况下,你可以不把轮廓线看作是边缘。但从数学的角度来看,轮廓线和边缘是两码事。

import cv2 as cv
import numpy as np

img = cv.imread('Pictures/tiger2.jpg', cv.IMREAD_UNCHANGED)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
cv.imshow('Gray', gray)
canny = cv.Canny(img, 125, 175)
cv.imshow("Canny Edges", canny)
contours, hierarchies = cv.findContours(canny, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)
print(f'{len(contours)} contours found!')
ret, thresh = cv.threshold(gray, 125, 255, cv.THRESH_BINARY)
contours, hierarchies = cv.findContours(thresh, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)
cv.imshow('Thresh', thresh)
 
blank = np.zeros(img.shape, dtype='uint8')
cv.drawContours(blank, contours, -1, (0,0,255), 2)
cv.imshow("Drawn Contours", blank)

cv.waitKey(0)

有些人喜欢使用红色的CHAIN APPROX SIMPLE,它基本上是将所有返回的数量压缩在最有意义的简单数量中。例如,如果你在图像中有一条线,如果你使用chain approx.None,你会得到该线的所有轮廓和所有点的坐标。Chain approx simple本质上是把那条线的所有这些点,只压缩到两个端点上。

因为这是最有意义的,一条线只由两个端点定义,而我们不希望中间的所有点。简而言之,这就是整个函数所要做的。

因此,由于contours是一个列表,我们基本上可以通过找出这个列表的长度来找到找到的contours的数量。所以我们可以用print()函数来打印这个列表的长度。

我们可以在找到边缘之前模糊图像。你还记得如何模糊图像吗?是的,高斯模糊函数。我们可以将图像作为图像源传入,并使用5乘5的核大小。

这将大大减少用5乘5的核大小模糊图像所找到的轮廓线的数量。

现在有另一种寻找轮廓的方法。我们可以使用OpenCV中的另一个函数--阈值,而不是使用canny边缘检测器。

只需添加以下代码来代替canny的代码。

ret, thresh = cv.threshold(grey, 125, 255, cv.THRESH_BINARY)

我们在这段代码中使用的cv.threshold函数将收进灰色图像,阈值为125,最大值为255。不要对阈值函数担心太多。现在,只要知道阈值本质上是看一个图像并试图将该图像二进制化。因此,如果一个特定的像素低于125,如果该像素的密度低于125,它将被设置为零或空白。如果它高于125,它就被设置为白色或2乘5。这就是它所做的一切。在findContours方法中,我们必须用thresh代替canny,最后一部分是指定一个阈值和类型。这就是cv.THRESH_BINARY,二进制提高图像。

现在不要太担心这个阈值的问题。我们将在本书的高级部分更深入地讨论这个问题。你需要知道的是,阈值处理试图将图像二进制化,将图像转换成二进制形式,要么是零,要么是黑或白。现在,在开放的CV中,最酷的是你可以通过在图像上画图,实际地将图像上发现的轮廓可视化。

在空白图像上绘制轮廓线
所以,如果你记得,Numpy是我们用来创建空白屏幕窗口的库。让我们创建一个空白屏幕并直接在上面绘制轮廓线,以显示我们的图像中的轮廓线。

我们通过使用cv.drawContours()方法来做到这一点。它接收了一个要画出空白的图像,并接收了轮廓线,它必须是一个列表,在这个例子中,它只是一个轮廓线列表。请看一下代码。

正如你在上面的代码中看到的,cv.drawsContours()方法考虑到了索引,基本上就是你在图像中想要多少个轮廓。因为我们想要所有的轮廓,所以我们指定它为-1,给它一个颜色,0,0,255。而且我们可以给它一个2的厚度。

一般来说,我建议你先使用Canny方法,然后尝试用它来寻找轮廓,而不是尝试对图像进行阈值处理,然后在此基础上寻找轮廓。正如我们将在高级部分讨论的那样,这种简单的阈值处理有其缺点。但在某些情况下,在大多数情况下,它是最受欢迎的一种阈值处理,因为它是最简单的,而且它的工作做得很好。

对于这一章,我们谈到了如何在OpenCV中识别轮廓。但有两种方法,首先试图用canny边缘检测器找到图像的边缘级联,并试图用它找到轮廓,还试图用cv.threshold将图像二值化,并在上面找到轮廓。

Gray Canny Edges Thresh Drawn Contours
上一篇 下一篇

猜你喜欢

热点阅读