opencv计算机视觉_计算机视觉 opencv - CSDN
精华内容
参与话题
  • 史上最全的OpenCV入门教程

    万次阅读 多人点赞 2018-06-21 16:06:36
    OpenCV 用于各种图像和视频分析,如面部识别和检测,车牌阅读,照片编辑,高级机器人视觉,光学字符识别等等。你将需要两个主要的库,第三个可选:python-OpenCV,Numpy 和 Matplotlib。Windows 用户:pyt...

    一、Python OpenCV 入门

    欢迎阅读系列教程,内容涵盖 OpenCV,它是一个图像和视频处理库,包含 C ++,C,Python 和 Java 的绑定。 OpenCV 用于各种图像和视频分析,如面部识别和检测,车牌阅读,照片编辑,高级机器人视觉,光学字符识别等等。

    你将需要两个主要的库,第三个可选:python-OpenCV,Numpy 和 Matplotlib。

    Windows 用户:

    python-OpenCV:有其他的方法,但这是最简单的。 下载相应的 wheel(.whl)文件,然后使用pip进行安装。 观看视频来寻求帮助。

    pip install numpypip install matplotlib

    不熟悉使用pip? 请参阅pip安装教程来获得帮助。

    Linux/Mac 用户

    pip3 install numpy

    或者

    apt-get install python3-numpy

    你可能需要apt-get来安装python3-pip。

    pip3 install matplotlib

    或者

    apt-get install python3-matplotlibapt-get install python-OpenCV

    Matplotlib 是用于展示来自视频或图像的帧的可选选项。 我们将在这里展示几个使用它的例子。 Numpy 被用于“数值和 Python”的所有东西。 我们主要利用 Numpy 的数组功能。 最后,我们使用python-OpenCV,它是 Python 特定的 OpenCV 绑定。

    OpenCV 有一些操作,如果没有完整安装 OpenCV (大小约 3GB),你将无法完成,但是实际上你可以用 python-OpenCV 最简安装。 我们将在本系列的后续部分中使用 OpenCV 的完整安装,如果你愿意的话,你可以随意获得它,但这三个模块将使我们忙碌一段时间!

    通过运行 Python 并执行下列命令来确保你安装成功:

    import cv2import matplotlibimport numpy

    如果你没有错误,那么你已经准备好了。好了嘛?让我们下潜吧!

    首先,在图像和视频分析方面,我们应该了解一些基本的假设和范式。对现在每个摄像机的记录方式来说,记录实际上是一帧一帧地显示,每秒 30-60 次。但是,它们的核心是静态帧,就像图像一样。因此,图像识别和视频分析大部分使用相同的方法。有些东西,如方向跟踪,将需要连续的图像(帧),但像面部检测或物体识别等东西,在图像和视频中代码几乎完全相同。

    接下来,大量的图像和视频分析归结为尽可能简化来源。这几乎总是起始于转换为灰度,但也可以是彩色滤镜,渐变或这些的组合。从这里,我们可以对来源执行各种分析和转化。一般来说,这里发生的事情是转换完成,然后是分析,然后是任何覆盖,我们希望应用在原始来源上,这就是你可以经常看到,对象或面部识别的“成品”在全色图像或视频上显示。然而,数据实际上很少以这种原始形式处理。有一些我们可以在基本层面上做些什么的例子。所有这些都使用基本的网络摄像头来完成,没有什么特别的:

    背景提取

    颜色过滤

    边缘检测

    用于对象识别的特征匹配

    一般对象识别

    在边缘检测的情况下,黑色对应于(0,0,0)的像素值,而白色线条是(255,255,255)。视频中的每个图片和帧都会像这样分解为像素,并且像边缘检测一样,我们可以推断,边缘是基于白色与黑色像素对比的地方。然后,如果我们想看到标记边缘的原始图像,我们记录下白色像素的所有坐标位置,然后在原始图像或视频上标记这些位置。

    到本教程结束时,你将能够完成上述所有操作,并且能够训练你的机器识别你想要的任何对象。就像我刚开始说的,第一步通常是转换为灰度。在此之前,我们需要加载图像。因此,我们来做吧!在整个教程中,我极力鼓励你使用你自己的数据来玩。如果你有摄像头,一定要使用它,否则找到你认为很有趣的图像。如果你有麻烦,这是一个手表的图像:

    import cv2import numpy as npfrom matplotlib import pyplot as pltimg = cv2.imread('watch.jpg',cv2.IMREAD_GRAYSCALE)cv2.imshow('image',img)cv2.waitKey(0)cv2.destroyAllWindows()

    首先,我们正在导入一些东西,我已经安装了这三个模块。接下来,我们将img定义为cv2.read(image file, parms)。默认值是IMREAD_COLOR,这是没有任何 alpha 通道的颜色。如果你不熟悉,alpha 是不透明度(与透明度相反)。如果你需要保留 Alpha 通道,也可以使用IMREAD_UNCHANGED。很多时候,你会读取颜色版本,然后将其转换为灰度。如果你没有网络摄像机,这将是你在本教程中使用的主要方法,即加载图像。

    你可以不使用IMREAD_COLOR ...等,而是使用简单的数字。你应该熟悉这两种选择,以便了解某个人在做什么。对于第二个参数,可以使用-1,0或1。颜色为1,灰度为0,不变为-1。因此,对于灰度,可以执行cv2.imread('watch.jpg', 0)。

    一旦加载完成,我们使用cv2.imshow(title,image)来显示图像。从这里,我们使用cv2.waitKey(0)来等待,直到有任何按键被按下。一旦完成,我们使用cv2.destroyAllWindows()来关闭所有的东西。

    正如前面提到的,你也可以用 Matplotlib 显示图像,下面是一些如何实现的代码:

    import cv2import numpy as npfrom matplotlib import pyplot as pltimg = cv2.imread('watch.jpg',cv2.IMREAD_GRAYSCALE)plt.imshow(img, cmap = 'gray', interpolation = 'bicubic')plt.xticks([]), plt.yticks([]) # to hide tick values on X and Y axisplt.plot([200,300,400],[100,200,300],'c', linewidth=5)plt.show()

    请注意,你可以绘制线条,就像任何其他 Matplotlib 图表一样,使用像素位置作为坐标的。 不过,如果你想绘制你的图片,Matplotlib 不是必需的。 OpenCV 为此提供了很好的方法。 当你完成修改后,你可以保存,如下所示:

    cv2.imwrite('watchgray.png',img)

    将图片导入 OpenCV 似乎很容易,加载视频源如何? 在下一个教程中,我们将展示如何加载摄像头或视频源。

    二、加载视频源

    在这个 Python OpenCV 教程中,我们将介绍一些使用视频和摄像头的基本操作。 除了起始行,处理来自视频的帧与处理图像是一样的。 我们来举例说明一下:

    import numpy as npimport cv2cap = cv2.VideoCapture(0) while(True): ret, frame = cap.read() gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) cv2.imshow('frame',gray) if cv2.waitKey(1) & 0xFF == ord('q'): breakcap.release()cv2.destroyAllWindows()

    首先,我们导入numpy和cv2,没有什么特别的。 接下来,我们可以cap = cv2.VideoCapture(0)。 这将从你计算机上的第一个网络摄像头返回视频。 如果你正在观看视频教程,你将看到我正在使用1,因为我的第一个摄像头正在录制我,第二个摄像头用于实际的教程源。

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

    这段代码启动了一个无限循环(稍后将被break语句打破),其中ret和frame被定义为cap.read()。 基本上,ret是一个代表是否有返回的布尔值,frame是每个返回的帧。 如果没有帧,你不会得到错误,你会得到None。

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    在这里,我们定义一个新的变量gray,作为转换为灰度的帧。 注意这个BGR2GRAY。 需要注意的是,OpenCV 将颜色读取为 BGR(蓝绿色红色),但大多数计算机应用程序读取为 RGB(红绿蓝)。 记住这一点。

    cv2.imshow('frame',gray)

    请注意,尽管是视频流,我们仍然使用imshow。 在这里,我们展示了转换为灰色的源。 如果你想同时显示,你可以对原始帧和灰度执行imshow,将出现两个窗口。

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

    这个语句每帧只运行一次。 基本上,如果我们得到一个按键,那个键是q,我们将退出while循环,然后运行:

    cap.release()cv2.destroyAllWindows()

    这将释放网络摄像头,然后关闭所有的imshow()窗口。

    在某些情况下,你可能实际上需要录制,并将录制内容保存到新文件中。 以下是在 Windows 上执行此操作的示例:

    import numpy as npimport cv2cap = cv2.VideoCapture(1)fourcc = cv2.VideoWriter_fourcc(*'XVID')out = cv2.VideoWriter('output.avi',fourcc, 20.0, (640,480))while(True): ret, frame = cap.read() gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) out.write(frame) cv2.imshow('frame',gray) if cv2.waitKey(1) & 0xFF == ord('q'): breakcap.release()out.release()cv2.destroyAllWindows()

    这里主要要注意的是正在使用的编解码器,以及在while循环之前定义的输出信息。 然后,在while循环中,我们使用out.write()来输出帧。 最后,在while循环之外,在我们释放摄像头之后,我们也释放out。

    太好了,现在我们知道如何操作图像和视频。 如果你没有网络摄像头,你可以使用图像甚至视频来跟随教程的其余部分。 如果你希望使用视频而不是网络摄像头作为源,则可以为视频指定文件路径,而不是摄像头号码。

    现在我们可以使用来源了,让我们来展示如何绘制东西。 此前你已经看到,你可以使用 Matplotlib 在图片顶部绘制,但是 Matplotlib 并不真正用于此目的,特别是不能用于视频源。 幸运的是,OpenCV 提供了一些很棒的工具,来帮助我们实时绘制和标记我们的源,这就是我们将在下一个教程中讨论的内容。

    三、在图像上绘制和写字

    在这个 Python OpenCV 教程中,我们将介绍如何在图像和视频上绘制各种形状。 想要以某种方式标记检测到的对象是相当普遍的,所以我们人类可以很容易地看到我们的程序是否按照我们的希望工作。 一个例子就是之前显示的图像之一:

    对于这个临时的例子,我将使用下面的图片:

    鼓励你使用自己的图片。 像往常一样,我们的起始代码可以是这样的:

    import numpy as npimport cv2img = cv2.imread('watch.jpg',cv2.IMREAD_COLOR)

    下面,我们可以开始绘制,这样:

    cv2.line(img,(0,0),(150,150),(255,255,255),15)cv2.imshow('image',img)cv2.waitKey(0)cv2.destroyAllWindows()

    cv2.line()接受以下参数:图片,开始坐标,结束坐标,颜色(bgr),线条粗细。

    结果在这里:

    好吧,很酷,让我们绘制更多形状。 接下来是一个矩形:

    cv2.rectangle(img,(15,25),(200,150),(0,0,255),15)

    这里的参数是图像,左上角坐标,右下角坐标,颜色和线条粗细。

    圆怎么样?

    cv2.circle(img,(100,63), 55, (0,255,0), -1)

    这里的参数是图像/帧,圆心,半径,颜色和。 注意我们粗细为-1。 这意味着将填充对象,所以我们会得到一个圆。

    线条,矩形和圆都很酷,但是如果我们想要五边形,八边形或十八边形? 没问题!

    pts = np.array([[10,5],[20,30],[70,20],[50,10]], np.int32)# OpenCV documentation had this code, which reshapes the array to a 1 x 2. I did not # find this necessary, but you may:#pts = pts.reshape((-1,1,2))cv2.polylines(img, [pts], True, (0,255,255), 3)

    首先,我们将坐标数组称为pts(点的简称)。 然后,我们使用cv2.polylines来画线。 参数如下:绘制的对象,坐标,我们应该连接终止的和起始点,颜色和粗细。

    你可能想要做的最后一件事是在图像上写字。 这可以这样做:

    font = cv2.FONT_HERSHEY_SIMPLEXcv2.putText(img,'OpenCV Tuts!',(0,130), font, 1, (200,255,155), 2, cv2.LINE_AA)

    目前为止的完整代码:

    import numpy as npimport cv2img = cv2.imread('watch.jpg',cv2.IMREAD_COLOR)cv2.line(img,(0,0),(200,300),(255,255,255),50)cv2.rectangle(img,(500,250),(1000,500),(0,0,255),15)cv2.circle(img,(447,63), 63, (0,255,0), -1)pts = np.array([[100,50],[200,300],[700,200],[500,100]], np.int32)pts = pts.reshape((-1,1,2))cv2.polylines(img, [pts], True, (0,255,255), 3)font = cv2.FONT_HERSHEY_SIMPLEXcv2.putText(img,'OpenCV Tuts!',(10,500), font, 6, (200,255,155), 13, cv2.LINE_AA)cv2.imshow('image',img)cv2.waitKey(0)cv2.destroyAllWindows()

    结果:

    在下一个教程中,我们将介绍我们可以执行的基本图像操作。

    四、图像操作

    在 OpenCV 教程中,我们将介绍一些我们可以做的简单图像操作。 每个视频分解成帧。 然后每一帧,就像一个图像,分解成存储在行和列中的,帧/图片中的像素。 每个像素都有一个坐标位置,每个像素都由颜色值组成。 让我们列举访问不同的位的一些例子。

    我们将像往常一样读取图像(如果可以,请使用自己的图像,但这里是我在这里使用的图像):

    import cv2import numpy as npimg = cv2.imread('watch.jpg',cv2.IMREAD_COLOR)

    现在我们可以实际引用特定像素,像这样:

    px = img[55,55]

    下面我们可以实际修改像素:

    img[55,55] = [255,255,255]

    之后重新引用:

    px = img[55,55]print(px)

    现在应该不同了,下面我们可以引用 ROI,图像区域:

    px = img[100:150,100:150]print(px)

    我们也可以修改 ROI,像这样:

    img[100:150,100:150] = [255,255,255]

    我们可以引用我们的图像的特定特征:

    print(img.shape)print(img.size)print(img.dtype)

    我们可以像这样执行操作:

    watch_face = img[37:111,107:194]img[0:74,0:87] = watch_facecv2.imshow('image',img)cv2.waitKey(0)cv2.destroyAllWindows()

    这会处理我的图像,但是可能不能用于你的图像,取决于尺寸。这是我的输出:

    这些是一些简单的操作。 在下一个教程中,我们将介绍一些我们可以执行的更高级的图像操作。

    五、图像算术和逻辑运算

    欢迎来到另一个 Python OpenCV 教程,在本教程中,我们将介绍一些简单算术运算,我们可以在图像上执行的,并解释它们的作用。 为此,我们将需要两个相同大小的图像来开始,然后是一个较小的图像和一个较大的图像。 首先,我将使用:

    首先,让我们看看简单的加法会做什么:

    import cv2import numpy as np# 500 x 250img1 = cv2.imread('3D-Matplotlib.png')img2 = cv2.imread('mainsvmimage.png')add = img1+img2cv2.imshow('add',add)cv2.waitKey(0)cv2.destroyAllWindows()

    结果:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    你不可能想要这种混乱的加法。 OpenCV 有一个“加法”方法,让我们替换以前的“加法”,看看是什么:

    add = cv2.add(img1,img2)

    结果:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    这里可能不理想。 我们可以看到很多图像是非常“白色的”。 这是因为颜色是 0-255,其中 255 是“全亮”。 因此,例如:(155,211,79) + (50, 170, 200) = 205, 381, 279...转换为(205, 255,255)。

    接下来,我们可以添加图像,并可以假设每个图像都有不同的“权重”。 这是如何工作的:

    import cv2import numpy as npimg1 = cv2.imread('3D-Matplotlib.png')img2 = cv2.imread('mainsvmimage.png')weighted = cv2.addWeighted(img1, 0.6, img2, 0.4, 0)cv2.imshow('weighted',weighted)cv2.waitKey(0)cv2.destroyAllWindows()

    对于addWeighted方法,参数是第一个图像,权重,第二个图像,权重,然后是伽马值,这是一个光的测量值。 我们现在就把它保留为零。

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    这些是一些额外的选择,但如果你真的想将一个图像添加到另一个,最新的重叠在哪里? 在这种情况下,你会从最大的开始,然后添加较小的图像。 为此,我们将使用相同的3D-Matplotlib.png图像,但使用一个新的 Python 标志:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    现在,我们可以选取这个标志,并把它放在原始图像上。 这很容易(基本上使用我们在前一个教程中使用的相同代码,我们用一个新的东西替换了图像区域(ROI)),但是如果我们只想要标志部分而不是白色背景呢? 我们可以使用与之前用于 ROI 替换相同的原理,但是我们需要一种方法来“去除”标志的背景,使得白色不会不必要地阻挡更多背景图像。 首先我将显示完整的代码,然后解释:

    import cv2import numpy as np# Load two imagesimg1 = cv2.imread('3D-Matplotlib.png')img2 = cv2.imread('mainlogo.png')# I want to put logo on top-left corner, So I create a ROIrows,cols,channels = img2.shaperoi = img1[0:rows, 0:cols ]# Now create a mask of logo and create its inverse maskimg2gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)# add a thresholdret, mask = cv2.threshold(img2gray, 220, 255, cv2.THRESH_BINARY_INV)mask_inv = cv2.bitwise_not(mask)# Now black-out the area of logo in ROIimg1_bg = cv2.bitwise_and(roi,roi,mask = mask_inv)# Take only region of logo from logo image.img2_fg = cv2.bitwise_and(img2,img2,mask = mask)dst = cv2.add(img1_bg,img2_fg)img1[0:rows, 0:cols ] = dstcv2.imshow('res',img1)cv2.waitKey(0)cv2.destroyAllWindows()

    这里发生了很多事情,出现了一些新的东西。 我们首先看到的是一个新的阈值:ret, mask = cv2.threshold(img2gray, 220, 255, cv2.THRESH_BINARY_INV)。

    我们将在下一个教程中介绍更多的阈值,所以请继续关注具体内容,但基本上它的工作方式是根据阈值将所有像素转换为黑色或白色。 在我们的例子中,阈值是 220,但是我们可以使用其他值,或者甚至动态地选择一个,这是ret变量可以使用的值。 接下来,我们看到:mask_inv = cv2.bitwise_not(mask)。 这是一个按位操作。 基本上,这些操作符与 Python 中的典型操作符非常相似,除了一点,但我们不会在这里触及它。 在这种情况下,不可见的部分是黑色的地方。 然后,我们可以说,我们想在第一个图像中将这个区域遮住,然后将空白区域替换为图像 2 的内容。

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    下个教程中,我们深入讨论阈值。

    六、阈值

    欢迎阅读另一个 OpenCV 教程。在本教程中,我们将介绍图像和视频分析的阈值。阈值的思想是进一步简化视觉数据的分析。首先,你可以转换为灰度,但是你必须考虑灰度仍然有至少 255 个值。阈值可以做的事情,在最基本的层面上,是基于阈值将所有东西都转换成白色或黑色。比方说,我们希望阈值为 125(最大为 255),那么 125 以下的所有内容都将被转换为 0 或黑色,而高于 125 的所有内容都将被转换为 255 或白色。如果你像平常一样转换成灰度,你会变成白色和黑色。如果你不转换灰度,你会得到二值化的图片,但会有颜色。

    虽然这听起来不错,但通常不是。我们将在这里介绍多个示例和不同类型的阈值来说明这一点。我们将使用下面的图片作为我们的示例图片,但可以随意使用你自己的图片:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    这个书的图片就是个很好的例子,说明为什么一个人可能需要阈值。 首先,背景根本没有白色,一切都是暗淡的,而且一切都是变化的。 有些部分很容易阅读,另一部分则非常暗,需要相当多的注意力才能识别出来。 首先,我们尝试一个简单的阈值:

    retval, threshold = cv2.threshold(img, 10, 255, cv2.THRESH_BINARY)

    二元阈值是个简单的“是或不是”的阈值,其中像素为 255 或 0。在很多情况下,这是白色或黑色,但我们已经为我们的图像保留了颜色,所以它仍然是彩色的。 这里的第一个参数是图像。 下一个参数是阈值,我们选择 10。下一个是最大值,我们选择为 255。最后是阈值类型,我们选择了THRESH_BINARY。 通常情况下,10 的阈值会有点差。 我们选择 10,因为这是低光照的图片,所以我们选择低的数字。 通常 125-150 左右的东西可能效果最好。

    import cv2import numpy as npimg = cv2.imread('bookpage.jpg')retval, threshold = cv2.threshold(img, 12, 255, cv2.THRESH_BINARY)cv2.imshow('original',img)cv2.imshow('threshold',threshold)cv2.waitKey(0)cv2.destroyAllWindows()

    结果:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    现在的图片稍微更便于阅读了,但还是有点乱。 从视觉上来说,这样比较好,但是仍然难以使用程序来分析它。 让我们看看我们是否可以进一步简化。

    首先,让我们灰度化图像,然后使用一个阈值:

    import cv2import numpy as npgrayscaled = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)retval, threshold = cv2.threshold(grayscaled, 10, 255, cv2.THRESH_BINARY)cv2.imshow('original',img)cv2.imshow('threshold',threshold)cv2.waitKey(0)cv2.destroyAllWindows()
    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    更简单,但是我们仍然在这里忽略了很多背景。 接下来,我们可以尝试自适应阈值,这将尝试改变阈值,并希望弄清楚弯曲的页面。

    import cv2import numpy as npth = cv2.adaptiveThreshold(grayscaled, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 115, 1)cv2.imshow('original',img)cv2.imshow('Adaptive threshold',th)cv2.waitKey(0)cv2.destroyAllWindows()
    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    还有另一个版本的阈值,可以使用,叫做大津阈值。 它在这里并不能很好发挥作用,但是:

    retval2,threshold2 = cv2.threshold(grayscaled,125,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)cv2.imshow('original',img)cv2.imshow('Otsu threshold',threshold2)cv2.waitKey(0)cv2.destroyAllWindows()

    七、颜色过滤

    在这个 Python OpenCV 教程中,我们将介绍如何创建一个过滤器,回顾按位操作,其中我们将过滤特定的颜色,试图显示它。或者,你也可以专门筛选出特定的颜色,然后将其替换为场景,就像我们用其他方法替换ROI(图像区域)一样,就像绿屏的工作方式。

    为了像这样过滤,你有几个选项。通常,你可能会将你的颜色转换为 HSV,即“色调饱和度纯度”。例如,这可以帮助你根据色调和饱和度范围,使用变化的值确定一个更具体的颜色。如果你希望的话,你可以实际生成基于 BGR 值的过滤器,但是这会有点困难。如果你很难可视化 HSV,不要感到失落,查看维基百科页面上的 HSV,那里有一个非常有用的图形让你可视化它。我最好亲自描述颜色的色调饱和度和纯度。现在让我们开始:

    import cv2import numpy as npcap = cv2.VideoCapture(0)while(1): _, frame = cap.read() hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) lower_red = np.array([30,150,50]) upper_red = np.array([255,255,180]) mask = cv2.inRange(hsv, lower_red, upper_red) res = cv2.bitwise_and(frame,frame, mask= mask) cv2.imshow('frame',frame) cv2.imshow('mask',mask) cv2.imshow('res',res) k = cv2.waitKey(5) & 0xFF if k == 27: breakcv2.destroyAllWindows()cap.release()
    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    这只是一个例子,以红色为目标。 它的工作方式是,我们所看到的是我们范围内的任何东西,基本上是 30-255,150-255 和 50-180。 它用于红色,但可以随便尝试找到自己的颜色。 HSV 在这里效果最好的原因是,我们想要范围内的颜色,这里我们通常需要相似的颜色。 很多时候,典型的红色仍然会有一些绿色和蓝色分量,所以我们必须允许一些绿色和蓝色,但是我们会想要几乎全红。 这意味着我们会在这里获得所有颜色的低光混合。

    为了确定 HSV 的范围,我认为最好的方法就是试错。 OpenCV 内置了将 BGR 转换为 HSV 的方法。 如果你想挑选单一的颜色,那么 BGR 到 HSV 将会很好用。 为了教学,下面是这个代码的一个例子:

    dark_red = np.uint8([[[12,22,121]]]) dark_red = cv2.cvtColor(dark_red,cv2.COLOR_BGR2HSV)

    这里的结果是一个 HSV 值,与dark_red值相同。这很棒...但是,同样...你遇到了颜色范围和 HSV 范围的基本问题。他们根本不同。你可能合理使用 BGR 范围,它们仍然可以工作,但是对于检测一种“颜色”,则无法正常工作。

    回到主代码,然而,我们首先要把帧转换成 HSV。那里没什么特别的。接下来,我们为红色指定一些 HSV 值。我们使用inRange函数,为我们的特定范围创建掩码。这是真或假,黑色或白色。接下来,我们通过执行按位操作来“恢复”我们的红色。基本上,我们显示了frame and mask。掩码的白色部分是红色范围,被转换为纯白色,而其他一切都变成黑色。最后我们展示所有东西。我选择了显示原始真,掩码和最终结果,以便更好地了解发生的事情。

    在下一个教程中,我们将对这个主题做一些介绍。你可能看到了,我们在这里还是有一些“噪音”。东西有颗粒感,红色中的黑点很多,还有许多其他的小色点。我们可以做一些事情,试图通过模糊和平滑来缓解这个问题,接下来我们将讨论这个问题。

    八、模糊和平滑

    在这个 Python OpenCV 教程中,我们将介绍如何尝试从我们的过滤器中消除噪声,例如简单的阈值,或者甚至我们以前的特定的颜色过滤器:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    正如你所看到的,我们有很多黑点,其中我们喜欢红色,还有很多其他的色点散落在其中。 我们可以使用各种模糊和平滑技术来尝试弥补这一点。 我们可以从一些熟悉的代码开始:

    import cv2import numpy as npcap = cv2.VideoCapture(0)while(1): _, frame = cap.read() hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) lower_red = np.array([30,150,50]) upper_red = np.array([255,255,180]) mask = cv2.inRange(hsv, lower_red, upper_red) res = cv2.bitwise_and(frame,frame, mask= mask)

    现在,让我们应用一个简单的平滑,我们计算每个像素块的均值。 在我们的例子中,我们使用15x15正方形,这意味着我们有 225 个总像素。

    kernel = np.ones((15,15),np.float32)/225 smoothed = cv2.filter2D(res,-1,kernel) cv2.imshow('Original',frame) cv2.imshow('Averaging',smoothed) k = cv2.waitKey(5) & 0xFF if k == 27: breakcv2.destroyAllWindows()cap.release()
    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    这个很简单,但是结果牺牲了很多粒度。 接下来,让我们尝试一些高斯模糊:

    blur = cv2.GaussianBlur(res,(15,15),0) cv2.imshow('Gaussian Blurring',blur)
    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    另一个选项是中值模糊:

    median = cv2.medianBlur(res,15) cv2.imshow('Median Blur',median)
    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    最后一个选项是双向模糊:

    bilateral = cv2.bilateralFilter(res,15,75,75) cv2.imshow('bilateral Blur',bilateral)

    所有模糊的对比:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    至少在这种情况下,我可能会使用中值模糊,但是不同的照明,不同的阈值/过滤器,以及其他不同的目标和目标可能会决定你使用其中一个。

    在下一个教程中,我们将讨论形态变换。

    九、形态变换

    在这个 Python OpenCV 教程中,我们将介绍形态变换。 这些是一些简单操作,我们可以基于图像形状执行。

    我们要谈的第一对是腐蚀和膨胀。 腐蚀是我们将“腐蚀”边缘。 它的工作方式是使用滑块(核)。 我们让滑块滑动,如果所有的像素是白色的,那么我们得到白色,否则是黑色。 这可能有助于消除一些白色噪音。 另一个版本是膨胀,它基本上是相反的:让滑块滑动,如果整个区域不是黑色的,就会转换成白色。 这是一个例子:

    import cv2import numpy as npcap = cv2.VideoCapture(0)while(1): _, frame = cap.read() hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) lower_red = np.array([30,150,50]) upper_red = np.array([255,255,180]) mask = cv2.inRange(hsv, lower_red, upper_red) res = cv2.bitwise_and(frame,frame, mask= mask) kernel = np.ones((5,5),np.uint8) erosion = cv2.erode(mask,kernel,iterations = 1) dilation = cv2.dilate(mask,kernel,iterations = 1) cv2.imshow('Original',frame) cv2.imshow('Mask',mask) cv2.imshow('Erosion',erosion) cv2.imshow('Dilation',dilation) k = cv2.waitKey(5) & 0xFF if k == 27: breakcv2.destroyAllWindows()cap.release()

    结果:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    下一对是“开放”和“关闭”。 开放的目标是消除“假阳性”。 有时在背景中,你会得到一些像素“噪音”。 “关闭”的想法是消除假阴性。 基本上就是你检测了你的形状,例如我们的帽子,但物体仍然有一些黑色像素。 关闭将尝试清除它们。

    cap = cv2.VideoCapture(1)while(1): _, frame = cap.read() hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) lower_red = np.array([30,150,50]) upper_red = np.array([255,255,180]) mask = cv2.inRange(hsv, lower_red, upper_red) res = cv2.bitwise_and(frame,frame, mask= mask) kernel = np.ones((5,5),np.uint8) opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) closing = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) cv2.imshow('Original',frame) cv2.imshow('Mask',mask) cv2.imshow('Opening',opening) cv2.imshow('Closing',closing) k = cv2.waitKey(5) & 0xFF if k == 27: breakcv2.destroyAllWindows()cap.release()
    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    另外两个选项是tophat和blackhat,对我们的案例并不有用:

    # It is the difference between input image and Opening of the image cv2.imshow('Tophat',tophat) # It is the difference between the closing of the input image and input image. cv2.imshow('Blackhat',blackhat)

    在下一个教程中,我们将讨论图像渐变和边缘检测。

    十、边缘检测和渐变

    欢迎阅读另一个 Python OpenCV 教程。 在本教程中,我们将介绍图像渐变和边缘检测。 图像渐变可以用来测量方向的强度,边缘检测就像它所说的:它找到了边缘! 我敢打赌你肯定没看到。

    首先,我们来展示一些渐变的例子:

    import cv2import numpy as npcap = cv2.VideoCapture(1)while(1): # Take each frame _, frame = cap.read() hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) lower_red = np.array([30,150,50]) upper_red = np.array([255,255,180]) mask = cv2.inRange(hsv, lower_red, upper_red) res = cv2.bitwise_and(frame,frame, mask= mask) laplacian = cv2.Laplacian(frame,cv2.CV_64F) sobelx = cv2.Sobel(frame,cv2.CV_64F,1,0,ksize=5) sobely = cv2.Sobel(frame,cv2.CV_64F,0,1,ksize=5) cv2.imshow('Original',frame) cv2.imshow('Mask',mask) cv2.imshow('laplacian',laplacian) cv2.imshow('sobelx',sobelx) cv2.imshow('sobely',sobely) k = cv2.waitKey(5) & 0xFF if k == 27: breakcv2.destroyAllWindows()cap.release()
    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    如果你想知道什么是cv2.CV_64F,那就是数据类型。 ksize是核大小。 我们使用 5,所以每次查询5×5的渔区。

    虽然我们可以使用这些渐变转换为纯边缘,但我们也可以使用 Canny 边缘检测!

    import cv2import numpy as npcap = cv2.VideoCapture(0)while(1): _, frame = cap.read() hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) lower_red = np.array([30,150,50]) upper_red = np.array([255,255,180]) mask = cv2.inRange(hsv, lower_red, upper_red) res = cv2.bitwise_and(frame,frame, mask= mask) cv2.imshow('Original',frame) edges = cv2.Canny(frame,100,200) cv2.imshow('Edges',edges) k = cv2.waitKey(5) & 0xFF if k == 27: breakcv2.destroyAllWindows()cap.release()
    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    这真是太棒了! 但是,这并不完美。 注意阴影导致了边缘被检测到。 其中最明显的是蓝狗窝发出的阴影。

    在下一个 OpenCV 教程中,我们将讨论如何在其他图像中搜索和查找相同的图像模板。

    十一、模板匹配

    欢迎阅读另一个 Python OpenCV 教程,在本教程中,我们将介绍对象识别的一个基本版本。 这里的想法是,给出一定的阈值,找到匹配我们提供的模板图像的相同区域。 对于具体的对象匹配,具有精确的照明/刻度/角度,这可以工作得很好。 通常会遇到这些情况的例子就是计算机上的任何 GUI。 按钮等东西总是相同的,所以你可以使用模板匹配。 结合模板匹配和一些鼠标控制,你已经实现了一个基于 Web 的机器人!

    首先,你需要一个主要图像和一个模板。 你应该从你正在图像中查找的“东西”选取你的模板。 我将提供一个图像作为例子,但随意使用你最喜爱的网站的图像或类似的东西。

    主要图像:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    我们要搜索的模板:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    这只是其中一个端口,但我们很好奇,看看我们是否可以匹配任何其他端口。 我们确实要选择一个阈值,其中某种东西可能是 80% 匹配,那么我们说这就匹配。 所以,我们将开始加载和转换图像:

    import cv2import numpy as npimg_rgb = cv2.imread('opencv-template-matching-python-tutorial.jpg')img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)template = cv2.imread('opencv-template-for-matching.jpg',0)w, h = template.shape[::-1]

    到目前为止,我们加载了两个图像,转换为灰度。 我们保留原始的 RGB 图像,并创建一个灰度版本。 我之前提到过这个,但是我们这样做的原因是,我们在灰度版本上执行所有的处理,然后在彩色图像上使用相同的标签来标记。

    对于主要图像,我们只有彩色版本和灰度版本。 我们加载模板并记下尺寸。

    res = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED)threshold = 0.8loc = np.where( res >= threshold)

    在这里,我们用img_gray(我们的主图像),模板,和我们要使用的匹配方法调用matchTemplate,并将返回值称为res。 我们指定一个阈值,这里是 80%。 然后我们使用逻辑语句,找到res大于或等于 80% 的位置。

    最后,我们使用灰度图像中找到的坐标,标记原始图像上的所有匹配:

    for pt in zip(*loc[::-1]): cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,255,255), 2)cv2.imshow('Detected',img_rgb)
    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    所以我们得到了几个匹配。也许需要降低阈值?我们试试 0.7。

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    这里有一些假阳性。 你可以继续调整门槛,直到你达到 100%,但是如果没有假阳性,你可能永远不会达到它。 另一个选择就是使用另一个模板图像。 有时候,使用相同对象的多个图像是有用的。 这样,你可以使阈值足够高的,来确保你的结果准确。

    在下一个教程中,我们将介绍前景提取。

    十二、GrabCut 前景提取

    欢迎阅读 Python OpenCV 前景提取教程。 这里的想法是找到前景,并删除背景。 这很像绿屏,只是这里我们实际上不需要绿屏。

    首先,我们将使用一个图像:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    随意使用你自己的。

    让我们加载图像并定义一些东西:

    import numpy as npimport cv2from matplotlib import pyplot as pltimg = cv2.imread('opencv-python-foreground-extraction-tutorial.jpg')mask = np.zeros(img.shape[:2],np.uint8)bgdModel = np.zeros((1,65),np.float64)fgdModel = np.zeros((1,65),np.float64)rect = (161,79,150,150)

    到目前为止,我们已经导入了cv2,numpy和matplotlib。 然后我们加载图像,创建一个掩码,指定算法内部使用的背景和前景模型。 真正重要的部分是我们定义的矩形。 这是rect = (start_x, start_y, width, height)。

    这是包围我们的主要对象的矩形。 如果你正在使用我的图片,那就是要使用的矩阵。 如果你使用自己的,找到适合你的图像的坐标。

    下面:

    cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')img = img*mask2[:,:,np.newaxis]plt.imshow(img)plt.colorbar()plt.show()

    所以在这里我们使用了cv2.grabCut,它用了很多参数。 首先是输入图像,然后是掩码,然后是主要对象的矩形,背景模型,前景模型,要运行的迭代量以及使用的模式。

    这里改变了掩码,使得所有像素 0 和 2 转换为背景,而像素 1 和 3 现在是前景。 从这里,我们乘以输入图像,得到我们的最终结果:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    下个教程中,我们打算讨论如何执行角点检测。

    十三、角点检测

    欢迎阅读 Python OpenCV 角点检测教程。 检测角点的目的是追踪运动,做 3D 建模,识别物体,形状和角色等。

    对于本教程,我们将使用以下图像:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    我们的目标是找到这个图像中的所有角点。 我会注意到,在这里我们有一些别名问题(斜线的锯齿),所以,如果我们允许的话,会发现很多角点,而且是正确的。 和往常一样,OpenCV 已经为我们完成了难题,我们需要做的就是输入一些参数。 让我们开始加载图像并设置一些参数:

    import numpy as npimport cv2img = cv2.imread('opencv-corner-detection-sample.jpg')gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)gray = np.float32(gray)corners = cv2.goodFeaturesToTrack(gray, 100, 0.01, 10)corners = np.int0(corners)

    到目前为止,我们加载图像,转换为灰度,然后是float32。 接下来,我们用goodFeaturesToTrack函数检测角点。 这里的参数是图像,检测到的最大角点数量,品质和角点之间的最小距离。 如前所述,我们在这里的锯齿问题将允许找到许多角点,所以我们对其进行了限制。 下面:

    for corner in corners: x,y = corner.ravel() cv2.circle(img,(x,y),3,255,-1) cv2.imshow('Corner',img)

    现在我们遍历每个角点,在我们认为是角点的每个点上画一个圆。

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    在下一个教程中,我们将讨论功能匹配/单映射。

    十四、特征匹配(单映射)爆破

    欢迎阅读 Python OpenCV 特征匹配教程。 特征匹配将是稍微更令人印象深刻的模板匹配版本,其中需要一个完美的,或非常接近完美的匹配。

    我们从我们希望找到的图像开始,然后我们可以在另一幅图像中搜索这个图像。 这里的完美是图像不需要相同的光照,角度,旋转等。 特征只需要匹配。

    首先,我们需要一些示例图像。 我们的“模板”,或者我们将要尝试匹配的图像:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    之后是我们用于搜索这个模板的图像:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    在这里,我们的模板图像在模板中,比在我们要搜索的图像中要小一些。 它的旋转也不同,阴影也有些不同。

    现在我们将使用一种“爆破”匹配的形式。 我们将在这两个图像中找到所有特征。 然后我们匹配这些特征。 然后,我们可以绘制我们想要的,尽可能多的匹配。 但是要小心。 如果你绘制 500 个匹配,你会有很多误报。 所以只绘制绘制前几个。

    import numpy as npimport cv2import matplotlib.pyplot as pltimg1 = cv2.imread('opencv-feature-matching-template.jpg',0)img2 = cv2.imread('opencv-feature-matching-image.jpg',0)

    到目前为止,我们已经导入了要使用的模块,并定义了我们的两个图像,即模板(img1)和用于搜索模板的图像(img2)。

    orb = cv2.ORB_create()

    这是我们打算用于特征的检测器。

    kp1, des1 = orb.detectAndCompute(img1,None)kp2, des2 = orb.detectAndCompute(img2,None)

    在这里,我们使用orb探测器找到关键点和他们的描述符。

    bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)

    这就是我们的BFMatcher对象。

    matches = bf.match(des1,des2)matches = sorted(matches, key = lambda x:x.distance)

    这里我们创建描述符的匹配,然后根据它们的距离对它们排序。

    img3 = cv2.drawMatches(img1,kp1,img2,kp2,matches[:10],None, flags=2)plt.imshow(img3)plt.show()

    这里我们绘制了前 10 个匹配。输出:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    十五、MOG 背景减弱

    在这个 Python OpenCV 教程中,我们将要讨论如何通过检测运动来减弱图像的背景。 这将要求我们回顾视频的使用,或者有两个图像,一个没有你想要追踪的人物/物体,另一个拥有人物/物体。 如果你希望,你可以使用你的摄像头,或者使用如下的视频:

    人们行走的样例视频

    这里的代码实际上很简单,就是我们现在知道的:

    import numpy as npimport cv2cap = cv2.VideoCapture('people-walking.mp4')fgbg = cv2.createBackgroundSubtractorMOG2()while(1): ret, frame = cap.read() fgmask = fgbg.apply(frame) cv2.imshow('fgmask',frame) cv2.imshow('frame',fgmask) k = cv2.waitKey(30) & 0xff if k == 27: break cap.release()cv2.destroyAllWindows()

    结果:

    pythonprogramming.net/static/imag…

    这里的想法是从静态背景中提取移动的前景。 你也可以使用这个来比较两个相似的图像,并立即提取它们之间的差异。

    在我们的例子中,我们可以看到我们确实已经检测到了一些人,但是我们确实有一些“噪音”,噪音实际上是树叶在周围的风中移动了一下。 只要我们知道一种减少噪音的方法。 等一下! 我们的确知道! 一个疯狂的挑战已经出现了你面前!

    接下来的教程开始让我们远离滤镜或变换的应用,并让我们使用 Haar Cascades 来检测一般对象,例如面部检测等等。

    十六、Haar Cascade 面部检测

    在这个 Python OpenCV 教程中,我们将讨论 Haar Cascades 对象检测。我们将从脸部和眼睛检测来开始。为了使用层叠文件进行对象识别/检测,首先需要层叠文件。对于非常流行的任务,这些已经存在。检测脸部,汽车,笑脸,眼睛和车牌等东西都是非常普遍的。

    首先,我会告诉你如何使用这些层叠文件,然后我将告诉你如何开始创建你自己的层叠,这样你就可以检测到任何你想要的对象,这很酷!

    你可以使用 Google 来查找你可能想要检测的东西的各种 Haar Cascades。对于找到上述类型,你应该没有太多的麻烦。我们将使用面部层叠和眼睛层叠。你可以在 Haar Cascades 的根目录找到更多。请注意用于使用/分发这些 Haar Cascades 的许可证。

    让我们开始我们的代码。我假设你已经从上面的链接中下载了haarcascade_eye.xml和haarcascade_frontalface_default.xml,并将这些文件放在你项目的目录中。

    import numpy as npimport cv2# multiple cascades: https://github.com/Itseez/opencv/tree/master/data/haarcascades#https://github.com/Itseez/opencv/blob/master/data/haarcascades/haarcascade_frontalface_default.xmlface_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')#https://github.com/Itseez/opencv/blob/master/data/haarcascades/haarcascade_eye.xmleye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')cap = cv2.VideoCapture(0)

    在这里,我们从导入cv2和numpy开始,然后加载我们的脸部和眼部的层叠。 目前为止很简单。

    while 1: ret, img = cap.read() gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.3, 5)

    现在我们开始我们的典型循环,这里唯一的新事物就是脸部的创建。 更多信息请访问detectMultiScale函数的文档。 基本上,它找到了面部! 我们也想找到眼睛,但是在一个假阳性的世界里,在面部里面寻找眼睛,从逻辑上来说是不是很明智? 我们希望我们不寻找不在脸上的眼睛! 严格来说,“眼睛检测”可能不会找到闲置的眼球。 大多数眼睛检测使用周围的皮肤,眼睑,眼睫毛,眉毛也可以用于检测。 因此,我们的下一步就是先去拆分面部,然后才能到达眼睛:

    for (x,y,w,h) in faces: cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2) roi_gray = gray[y:y+h, x:x+w] roi_color = img[y:y+h, x:x+w]

    在这里,我们找到了面部,它们的大小,绘制矩形,并注意 ROI。 接下来,我们找了一些眼睛:

    eyes = eye_cascade.detectMultiScale(roi_gray) for (ex,ey,ew,eh) in eyes: cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)

    如果我们找到这些,我们会继续绘制更多的矩形。 接下来我们完成:

    cv2.imshow('img',img) k = cv2.waitKey(30) & 0xff if k == 27: breakcap.release()cv2.destroyAllWindows()

    完整代码:

    import numpy as npimport cv2# multiple cascades: https://github.com/Itseez/opencv/tree/master/data/haarcascades#https://github.com/Itseez/opencv/blob/master/data/haarcascades/haarcascade_frontalface_default.xmlface_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')#https://github.com/Itseez/opencv/blob/master/data/haarcascades/haarcascade_eye.xmleye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')cap = cv2.VideoCapture(0)while 1: ret, img = cap.read() gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.3, 5) for (x,y,w,h) in faces: cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2) roi_gray = gray[y:y+h, x:x+w] roi_color = img[y:y+h, x:x+w] eyes = eye_cascade.detectMultiScale(roi_gray) for (ex,ey,ew,eh) in eyes: cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2) cv2.imshow('img',img) k = cv2.waitKey(30) & 0xff if k == 27: breakcap.release()cv2.destroyAllWindows()
    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    不错。你可能会注意到我不得不取下我的眼镜。这些造成了一些麻烦。我的嘴也经常被检测为眼睛,有时甚至是一张脸,但你明白了。面部毛发和其他东西经常可以欺骗基本面部检测,除此之外,皮肤的颜色也会造成很大的麻烦,因为我们经常试图尽可能简化图像,从而失去了很多颜色值。甚至还有一个小型行业,可以避免人脸检测和识别。CVDazzle 网站就是一个例子。其中有些非常古怪,但他们很有效。你也可以总是走完整的面部重建手术的路线,以避免自动跟踪和检测,所以总是这样,但是这更永久。做个发型比较短暂也容易做到。

    好吧,检测面部,眼睛和汽车是可以的,但我们是程序员。我们希望能够做任何事情。事实证明,事情会变得相当混乱,建立自己的 Haar Cascades 有一定的难度,但是其他人也这么做......你也可以!这就是在下一个教程中所讨论的。

    十七、创建自己的 Haar Cascade

    欢迎使用 Python OpenCV 对象检测教程。在本教程中,你将看到如何创建你自己的 Haar Cascades,以便你可以跟踪任何你想要的对象。由于这个任务的本质和复杂性,本教程将比平时稍长一些,但奖励是巨大的。

    虽然你可以在 Windows 中完成,我不会建议这样。因此,对于本教程,我将使用 Linux VPS,并且我建议你也这样做。你可以尝试使用 Amazon Web Services 提供的免费套餐,但对你来说可能太痛苦了,你可能需要更多的内存。你还可以从 Digital Ocean 获得低至五美元/月的 VPS。我推荐至少 2GB 的内存用于我们将要做的事情。现在大多数主机按小时收费,包括 DO。因此,你可以购买一个 20 美元/月的服务器,使用它一天,获取你想要的文件,然后终止服务器,并支付很少的钱。你需要更多的帮助来设置服务器?如果是的话,看看这个具体的教程。

    一旦你的服务器准备就绪,你会打算获取实际的 OpenCV 库。

    将目录更改到服务器的根目录,或者你想放置工作区的地方:

    cd ~sudo apt-get updatesudo apt-get upgrade

    首先,让我们为自己制作一个漂亮的工作目录:

    mkdir opencv_workspacecd opencv_workspace

    既然我们完成了,让我们获取 OpenCV。

    sudo apt-get install gitgit clone https://github.com/Itseez/opencv.git

    我们这里克隆了 OpenCV 的最新版本。现在获取一些必需品。

    编译器:sudo apt-get install build-essential

    库:sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev

    Python 绑定:sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev

    最后,让我们获取 OpenCV 开发库:

    sudo apt-get install libopencv-dev

    现在,我们该如何完成这个过程呢?所以当你想建立一个 Haar Cascade 时,你需要“正片”图像和“底片”图像。 “正片”图像是包含要查找的对象的图像。这可以是具有对象的主要图像,也可以是包含对象的图像,并指定对象所在的 ROI(兴趣区域)。有了这些正片图像,我们建立一个矢量文件,基本上是所有这些东西放在一起。正片图像的一个好处是,你可以实际只有一个你想要检测的对象的图像,然后有几千个底片图像。是的,几千。底片图像可以是任何东西,除了他们不能包含你的对象。

    在这里,使用你的底片图像,你可以使用opencv_createsamples命令来创建一堆正片的示例。你的正片图像将叠加在这些底片上,而且会形成各种各样的角度。它实际上可以工作得很好,特别是如果你只是在寻找一个特定的对象。但是,如果你正在寻找所有螺丝刀,则需要拥有数千个螺丝刀的独特图像,而不是使用opencv_createsamples为你生成样品。我们将保持简单,只使用一个正片图像,然后用我们的底片创建一堆样本。

    我们的正片图像:

    这是另外一个场景,如果你使用自己的图像,你可能会更喜欢这个。如果事情出错了,试试看我的,但是我建议你自己画一下。保持较小。 50x50像素应该可以。

    好吧,获得正片图像是没有问题的!只有一个问题。我们需要成千上万的底片图像。可能在未来,我们也可能需要成千上万的正片图像。我们可以在世界的哪个地方实现它?基于 WordNet 的概念,有一个非常有用的站点叫做 ImageNet。从这里,你可以找到几乎任何东西的图像。我们这里,我们想要手表,所以搜索手表,你会发现大量种类的手表。让我们检索电子表。真棒!看看下载标签!存在用于所有电子表手表的 URL!很酷。好吧,但我说过我们只会使用一个正片,所以我们只是检测一个手表。如果你想检测“全部”手表,需要准备获取多余 50,000 个手表图像,至少 25000 个“底片”的图像。之后,准备足够的服务器,除非你想要你的 Haar Cascade 训练花一个星期。那么我们如何得到底片? ImageNet 的全部重点是图像训练,所以他们的图像非常具体。因此,如果我们搜索人,汽车,船只,飞机......无论什么,都不会有手表。你可能会看到一些人或类似的东西,但你明白了。既然你可能看到人周围或上面的手表,我其实认为你也会得到人的图像。我的想法是寻找做运动的人,他们可能没有戴电子表。所以,我们来找一些批量图片的 URL 链接。我发现体育/田径链接有 1,888 张图片,但你会发现很多这些都是完全损坏的。让我们再来找一个:

    好吧,我们拥有所有这些图片,现在呢?那么,首先,我们希望所有这些大小都相同,而且要小很多!天哪,只要我们知道一个方法来操作图像...嗯...哦,这是一个 OpenCV 教程!我们可以处理它。所以,首先,我们要做的就是编写一个简单的脚本,访问这些 URL 列表,获取链接,访问链接,拉取图像,调整大小,保存它们,然后重复,直到完成。当我们的目录充满图像时,我们还需要一种描述图像的描述文件。对于正片,手动创建这个文件特别痛苦,因为你需要指定你的对象,每个图像的具体的兴趣区域。幸运的是,create_samples方法将图像随机放置,并为我们做了所有工作。我们只需要一个用于底片的简单描述符,但是这不是问题,在拉伸和操作图像时我们可以实现。

    www.youtube.com/embed/z_6fP…

    在任何你喜欢的地方随意运行这个代码。 我要在我的主机上运行它,因为它应该快一点。 你可以在你的服务器上运行。 如果你想使用cv2模块,请执行sudo apt-get install python-OpenCV。 目前,我不知道在 Linux 上为 Python 3 获得这些绑定的好方法。 我将要写的脚本是 Python 3,所以记住这一点。 主要区别是Urllib处理。

    # download-image-by-link.pyimport urllib.requestimport cv2import numpy as npimport osdef store_raw_images(): neg_images_link = '//image-net.org/api/text/imagenet.synset.geturls?wnid=n00523513' neg_image_urls = urllib.request.urlopen(neg_images_link).read().decode() pic_num = 1 if not os.path.exists('neg'): os.makedirs('neg') for i in neg_image_urls.split(' '): try: print(i) urllib.request.urlretrieve(i, "neg/"+str(pic_num)+".jpg") img = cv2.imread("neg/"+str(pic_num)+".jpg",cv2.IMREAD_GRAYSCALE) # should be larger than samples / pos pic (so we can place our image on it) resized_image = cv2.resize(img, (100, 100)) cv2.imwrite("neg/"+str(pic_num)+".jpg",resized_image) pic_num += 1 except Exception as e: print(str(e))

    很简单,这个脚本将访问链接,抓取网址,并继续访问它们。从这里,我们抓取图像,转换成灰度,调整大小,然后保存。我们使用一个简单的计数器来命名图像。继续运行它。你可能看到,有很多确实的图片等。没关系。这些错误图片中的一些更有问题。基本上都是白色,带有一些文本,说他们不再可用,而不是服务和 HTTP 错误。现在,我们有几个选择。我们可以忽略它们,或者修复它。嘿,这是一个没有手表的图像,所以什么是对的呢?当然,你可以采取这种观点,但如果你为正片使用这种拉取方式的话,这肯定是一个问题。你可以手动删除它们...或者我们可以使用我们新的图像分析知识,来检测这些愚蠢的图像,并将其删除!

    我继续生成了一个新的目录,称之为“uglies(丑陋)”。在那个目录中,我点击并拖动了所有丑陋的图像版本(只是其中之一)。在底片中我只发现了一个主犯,所以我只有一个。让我们编写一个脚本来查找这个图像的所有实例并删除它。

    www.youtube.com/embed/t0HOV…

    def find_uglies(): match = False for file_type in ['neg']: for img in os.listdir(file_type): for ugly in os.listdir('uglies'): try: current_image_path = str(file_type)+'/'+str(img) ugly = cv2.imread('uglies/'+str(ugly)) question = cv2.imread(current_image_path) if ugly.shape == question.shape and not(np.bitwise_xor(ugly,question).any()): print('That is one ugly pic! Deleting!') print(current_image_path) os.remove(current_image_path) except Exception as e: print(str(e))

    现在我们只有底片,但是我留下了空间让你轻易在那里添加'pos'(正片)。 你可以运行它来测试,但我不介意先抓住更多的底片。 让我们再次运行图片提取器,仅仅使用这个 url://image-net.org/api/text/imagenet.synset.geturls?wnid=n07942152。 最后一张图像是#952,所以让我们以 953 开始pic_num,并更改网址。

    def store_raw_images(): neg_images_link = '//image-net.org/api/text/imagenet.synset.geturls?wnid=n07942152' neg_image_urls = urllib.request.urlopen(neg_images_link).read().decode() pic_num = 953 if not os.path.exists('neg'): os.makedirs('neg') for i in neg_image_urls.split(' '): try: print(i) urllib.request.urlretrieve(i, "neg/"+str(pic_num)+".jpg") img = cv2.imread("neg/"+str(pic_num)+".jpg",cv2.IMREAD_GRAYSCALE) # should be larger than samples / pos pic (so we can place our image on it) resized_image = cv2.resize(img, (100, 100)) cv2.imwrite("neg/"+str(pic_num)+".jpg",resized_image) pic_num += 1 except Exception as e: print(str(e))

    现在我们有超过2000张照片。 最后一步是,我们需要为这些底片图像创建描述符文件。 我们将再次使用一些代码!

    def create_pos_n_neg(): for file_type in ['neg']: for img in os.listdir(file_type): if file_type == 'pos': line = file_type+'/'+img+' 1 0 0 50 50 ' with open('info.dat','a') as f: f.write(line) elif file_type == 'neg': line = file_type+'/'+img+' ' with open('bg.txt','a') as f: f.write(line)

    运行它,你有了个bg.txt文件。 现在,我知道有些人的互联网连接可能不是最好的,所以我做个好人,在这里上传底片图片和描述文件。 你应该通过这些步骤。 如果你对本教程感到困扰,则需要知道如何执行这部分。 好吧,所以我们决定我们将一个图像用于正片前景图像。 因此,我们需要执行create_samples。 这意味着,我们需要将我们的neg目录和bg.txt文件移动到我们的服务器。 如果你在服务器上运行所有这些代码,不要担心。

    www.youtube.com/embed/eay7C…

    如果你是一个术士,并已经想出了如何在 Windows 上运行create_samples等,恭喜! 回到服务器的领地,我的文件现在是这样的:

    opencv_workspace--neg----negimages.jpg--opencv--info--bg.txt--watch5050.jpg

    你可能没有info目录,所以继续并mkdir info。 这是我们放置所有正片图像的地方。

    我们现在准备根据watch5050.jpg图像创建一些正片样本。 为此,请在工作区中通过终端运行以下命令:

    opencv_createsamples -img watch5050.jpg -bg bg.txt -info info/info.lst -pngoutput info -maxxangle 0.5 -maxyangle 0.5 -maxzangle 0.5 -num 1950

    这样做是基于我们指定的img创建样本,bg是背景信息,我们将输出info.list(很像bg.txt文件)的信息,然后-pngoutput就是我们想要放置新生成的图像的任何地方。 最后,我们有一些可选的参数,使我们的原始图像更加动态一些,然后用= num来表示我们想要创建的样本数量。 太棒了,让我们来运行它。 现在你的info目录应该有约 2,000 个图像,还有一个名为info.lst的文件。 这个文件基本上是你的“正片”文件。 打开它,并且看看它怎么样:

    0001_0014_0045_0028_0028.jpg 1 14 45 28 28

    首先你有文件名,之后是图像中有多少对象,其次是它们的所有位置。 我们只有一个,所以它是图像中对象矩形的x,y,宽度和高度。 这是一个图像:

    很难看到它,但如果你很难看到,手表就是这个图像。 图像中最左侧人物的左下方。 因此,这是一个“正片”图像,从另外一个“底片”图像创建,底片图像也将用于训练。 现在我们有了正片图像,现在我们需要创建矢量文件,这基本上是一个地方,我们将所有正片图像拼接起来。我们会再次为此使用opencv_createsamples!

    opencv_createsamples -info info/info.lst -num 1950 -w 20 -h 20 -vec positives.vec

    这是我们的矢量文件。 在这里,我们只是让它知道信息文件的位置,我们想要在文件中包含多少图像,在这个矢量文件中图像应该是什么尺寸,然后才能输出结果。 如果你愿意的话,你可以做得更大一些,20×20可能足够好了,你做的越大,训练时间就会越长。 继续,我们现在只需要训练我们的层叠。

    首先,我们要把输出放在某个地方,所以让我们创建一个新的数据目录:

    mkdir data,你的工作空间应该如下所示:

    opencv_workspace--neg----negimages.jpg--opencv--info--data--positives.vec --bg.txt--watch5050.jpg

    现在让我们运行训练命令:

    opencv_traincascade -data data -vec positives.vec -bg bg.txt -numPos 1800 -numNeg 900 -numStages 10 -w 20 -h 20

    在这里,我们表明了,我们想要数据去的地方,矢量文件的位置,背景文件的位置,要使用多少个正片图像和底片图像,多少个阶段以及宽度和高度。请注意,我们使用的numPos比我们少得多。这是为了给阶段腾出空间。

    有更多的选择,但这些就够了。这里主要是正片和底片的数量。一般认为,对于大多数实践,你需要 2:1 比例的正片和底片图像。有些情况可能会有所不同,但这是人们似乎遵循的一般规则。接下来,我们拥有阶段。我们选择了 10 个。你至少要 10-20 个,越多需要的时间越长,而且是指数级的。第一阶段通常很快,第五阶段要慢得多,第五十个阶段永远不会做完!所以,我们现在执行 10 个阶段。这里不错的事情是你可以训练 10 个阶段,稍后再回来,把数字改成 20,然后在你离开的地方继续。同样的,你也可以放一些像 100 个阶段的东西,上床睡觉,早上醒来,停下来,看看你有多远,然后用这些阶段“训练”,你会立即得到一个层叠文件。你可能从最后一句话中得出,这个命令的结果确实很棒,是个不错的层叠文件。我们希望能检测到我的手表,或者你决定检测的任何东西。我所知道的是,在输出这一段的时候,我还没有完成第一阶段的工作。如果你真的想要在一夜之间运行命令,但不想让终端打开,你可以使用nohup:

    nohup opencv_traincascade -data data -vec positives.vec -bg bg.txt -numPos 1800 -numNeg 900 -numStages 10 -w 20 -h 20 &

    这使命令即使在关闭终端之后也能继续运行。 你可以使用更多,但你可能会或可能不会用完你的 2GB RAM。

    www.youtube.com/embed/-Mhy-…

    在我的 2GB DO 服务器上,10 个阶段花了不到 2 个小时的时间。 所以,要么有一个cascade.xml文件,要么停止脚本运行。 如果你停止运行,你应该在你的data目录下有一堆stageX.xml文件。 打开它,看看你有多少个阶段,然后你可以使用这些阶段,再次运行opencv_traincascade,你会立即得到一个cascade.xml文件。 这里,我只想说出它是什么,以及有多少个阶段。 对我来说,我做了 10 个阶段,所以我将它重命名为watchcascade10stage.xml。 这就是我们所需的,所以现在将新的层次文件传回主计算机,放在工作目录中,让我们试试看!

    import numpy as npimport cv2face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')#this is the cascade we just made. Call what you wantwatch_cascade = cv2.CascadeClassifier('watchcascade10stage.xml')cap = cv2.VideoCapture(0)while 1: ret, img = cap.read() gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.3, 5) # add this # image, reject levels level weights. watches = watch_cascade.detectMultiScale(gray, 50, 50) # add this for (x,y,w,h) in watches: cv2.rectangle(img,(x,y),(x+w,y+h),(255,255,0),2) for (x,y,w,h) in faces: cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2) roi_gray = gray[y:y+h, x:x+w] roi_color = img[y:y+h, x:x+w] eyes = eye_cascade.detectMultiScale(roi_gray) for (ex,ey,ew,eh) in eyes: cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2) cv2.imshow('img',img) k = cv2.waitKey(30) & 0xff if k == 27: breakcap.release()cv2.destroyAllWindows()
    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    你可能注意到,手表的方框很小。 它似乎并没有达到整个手表。 回想一下我们的训练规模是20x20。 因此,我们最多有个20x20的方框。 你可以做100x100,但是,要小心,这将需要很长时间来训练。 因此,我们不绘制方框,而是,为什么不在手表上写字或什么东西? 这样做相对简单。 我们不在手表上执行cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),2),我们可以执行:

    font = cv2.FONT_HERSHEY_SIMPLEX cv2.putText(img,'Watch',(x-w,y-h), font, 0.5, (11,255,255), 2, cv2.LINE_AA)
    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    很酷! 所以你可能没有使用我的手表,你是怎么做的? 如果遇到麻烦,请尝试使用与我完全相同的所有内容。 检测图像,而不是检测摄像头,这里是一个:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    在图像上运行检测会给你:

    史上最全的OpenCV入门教程!这篇够你学习半个月了!万字长文入门

    Haar Cascades 往往是 100-2000 KB 的大小。大于等于 2,000 KB 的 Haar Cascades 应该非常准确。考虑你的情况,你可能会遇到约 5000 个一般物体。考虑 Haar Cascades 平均可能是约 500 KB。我们需要:0.5 MB * 5,000 = 2,500 MB或 2.5 GB。你需要 2.5 GB 的内存来识别 5000 个对象,并且可能是你在一天中遇到的最多对象。这让我着迷。考虑到我们可以访问所有的 image-net,并可以立即拉取很大范围的对象的一般图像。考虑 image-net 上的大多数图像,基本上都是 100% 的“跟踪”对象,因此,你可以通过手动标注位置,并仅使用 0,0 和图像的全部大小来得到正确的结果。

    展开全文
  • Opencv计算机视觉实战(Python版) 计算机博士,专注于机器学习与计...

    订阅后:请点击此处观看视频课程

     

    视频教程-Opencv计算机视觉实战(Python版)-计算机视觉

    学习有效期:永久观看

    学习时长:832分钟

    学习计划:14天

    难度:

     

    口碑讲师带队学习,让你的问题不过夜」

    讲师姓名:唐宇迪

    高校教师 / 培训机构讲师

    讲师介绍:计算机博士,专注于机器学习与计算机视觉领域,深度学习领域一线实战讲师。在图像识别领域有着丰富经验,实现过包括人脸识别,物体识别,关键点检测等多种应用的新算法。 参与多个国家级计算机视觉项目,多年数据领域培训经验,丰富的教学讲解经验,出品多套机器学习与深度学习系列课程,课程生动形象,风格通俗易懂。

    ☛点击立即跟老师学习☚

     

    「你将学到什么?」

    购买课程后,添加小助手微信(微信号:csdnxy68)回复【唐宇迪】

    进入学习群,获取唐宇迪老师答疑

    Opencv计算机视觉实战课程旨在帮助大家快速掌握机器视觉领域必备知识点原理及其在Opencv中的使用方法。课程风格通俗易懂,用最接地气的方式来讲解晦涩难懂的知识点。整体设计以项目实战来驱动学习,课程中所有代码均使用Python完成,在实战中首先讲解整体解决方案,接下来通过Debug模式来一步步分析每一行代码的作用及其完成的效果,提供全部课件及项目中所使用代码和数据。

     

    「课程学习目录」

    第1章:课程简介与环境配置
    1.课程简介
    2.Python与Opencv配置安装
    3.Notebook与IDE环境
    第2章:图像基本操作
    1.计算机眼中的图像(数据代码下载请点击------------->)
    2.视频的读取与处理
    3.ROI区域
    4.边界填充
    5.数值计算
    第3章:阈值与平滑处理
    1.图像阈值
    2.图像平滑处理
    3.高斯与中值滤波
    第4章:图像形态学操作
    1.腐蚀操作
    2.膨胀操作
    3.开运算与闭运算
    4.梯度计算
    5.礼帽与黑帽
    第5章:图像梯度计算
    1.Sobel算子
    2.梯度计算方法
    3.scharr与lapkacian算子
    第6章:边缘检测
    1.Canny边缘检测流程
    2.非极大值抑制
    3.边缘检测效果
    第7章:图像金字塔与轮廓检测
    1.图像金字塔定义
    2.金字塔制作方法
    3.轮廓检测方法
    4.轮廓检测结果
    5.轮廓特征与近似
    6.模板匹配方法
    7.匹配效果展示
    第8章:直方图与傅里叶变换
    1.直方图定义
    2.均衡化原理
    3.均衡化效果
    4.傅里叶概述
    5.频域变换结果
    6.低通与高通滤波
    第9章:项目实战-信用卡数字识别
    1.总体流程与方法讲解
    2.环境配置与预处理
    3.模板处理方法
    4.输入数据处理方法
    5.模板匹配得出识别结果
    第10章:项目实战-文档扫描OCR识别
    1.整体流程演示
    2.文档轮廓提取
    3.原始与变换坐标计算
    4.透视变换结果
    5.tesseract-ocr安装配置
    6.文档扫描识别效果
    第11章:图像特征-harris
    1.角点检测基本原理
    2.基本数学原理
    3.求解化简
    4.特征归属划分
    5.opencv角点检测效果
    第12章:图像特征-sift
    1.尺度空间定义
    2.高斯差分金字塔
    3.特征关键点定位
    4.生成特征描述
    5.特征向量生成
    6.opencv中sift函数使用
    第13章:案例实战-全景图像拼接
    1.特征匹配方法
    2.RANSAC算法
    3.图像拼接方法
    4.流程解读
    第14章:项目实战-停车场车位识别
    1.任务整体流程
    2.所需数据介绍
    3.图像数据预处理
    4.车位直线检测
    5.按列划分区域
    6.车位区域划分
    7.识别模型构建
    8.基于视频的车位检测
    第15章:项目实战-答题卡识别判卷
    1.整体流程与效果概
    2.预处理操作
    3.填涂轮廓检测
    4.选项判断识别
    第16章:背景建模
    1.背景消除-帧差法
    2.混合高斯模型
    3.学习步骤
    4.背景建模实战
    第17章:光流估计
    1.基本概念
    2.Lucas-Kanade算法
    3.推导求解
    4.光流估计实战
    第18章:Opencv的DNN模块
    1.DNN模块
    2.模型加载与结果输出
    第19章:项目实战-目标追踪
    1.目标追踪概述
    2.多目标追踪实战
    3.深度学习检测框架加载
    4.基于dlib与ssd的追踪
    5.多进程目标追踪
    6.多进程效率提升对比
    第20章:卷积原理与操作
    1.卷积神经网络的应用
    2.卷积层解释
    3.卷积计算过程
    4.pading与stride
    5.卷积参数共享
    6.池化层原理
    7.卷积效果演示
    8.卷积操作流程
    第21章:项目实战-疲劳检测
    1.关键点定位概述
    2.获取人脸关键点
    3.定位效果演示
    4.闭眼检测
    5.检测效果

     

    7项超值权益,保障学习质量」

    • 大咖讲解

    技术专家系统讲解传授编程思路与实战。

    • 答疑服务

    专属社群随时沟通与讲师答疑,扫清学习障碍,自学编程不再难。

    • 课程资料+课件

    超实用资料,覆盖核心知识,关键编程技能,方便练习巩固。(部分讲师考虑到版权问题,暂未上传附件,敬请谅解)

    • 常用开发实战

    企业常见开发实战案例,带你掌握Python在工作中的不同运用场景。

    • 大牛技术大会视频

    2019Python开发者大会视频免费观看,送你一个近距离感受互联网大佬的机会。

    • APP+PC随时随地学习

    满足不同场景,开发编程语言系统学习需求,不受空间、地域限制。

     

    「什么样的技术人适合学习?」

    • 想进入互联网技术行业,但是面对多门编程语言不知如何选择,0基础的你
    • 掌握开发、编程技术单一、冷门,迫切希望能够转型的你
    • 想进入大厂,但是编程经验不够丰富,没有竞争力,程序员找工作难。

     

    「悉心打造精品好课,14天学到大牛3年项目经验」

    【完善的技术体系】

    技术成长循序渐进,帮助用户轻松掌握

    掌握计算机视觉知识,扎实编码能力

    【清晰的课程脉络】

    浓缩大牛多年经验,全方位构建出系统化的技术知识脉络,同时注重实战操作。

    【仿佛在大厂实习般的课程设计】

    课程内容全面提升技术能力,系统学习大厂技术方法论,可复用在日后工作中。

     

    「你可以收获什么?」

    掌握计算机视觉必备知识点

    掌握Opencv图像处理方法

    熟练使用opencv构建视觉项目

     

    展开全文
  • opencv计算机视觉

    2019-03-09 16:24:50
    opencv计算机视觉 计算机视觉:是一门研究如何使机器“看”的科学,更进一步的说,就是是指用摄影机和电脑代替人眼对目标进行识别、跟踪和测量等机器视觉,并进一步做图形处理,使电脑处理成为更适合人眼观察或传送...

    opencv与计算机视觉

    计算机视觉:是一门研究如何使机器“看”的科学,更进一步的说,就是是指用摄影机和电脑代替人眼对目标进行识别、跟踪和测量等机器视觉,并进一步做图形处理,使电脑处理成为更适合人眼观察或传送给仪器检测的图像。(百度百科)
    opencv:简单的说就是为你提供一个处理图片和视频的框架,可以使用opencv或者自己的算法,不用担心分配和释放图片的内存,所以opencv最强大的地方在于它强大的内存管理和自身的架构。

    用计算机对图像进行处理的技术–图像处理

    常用方法
    (1)图像变换:由于图像阵列很大,直接在空间域中进行处理,涉及计算量很大。因此,往往采用各种图像变换的方法,如傅立叶变换 离散余弦变换(高斯模糊)。

    (2 )图像编码压缩:图像编码压缩技术可减少描述图像的数据量(即比特数),以便节省图像传输、处理时间和减少所占用的存储器容量。

    (3)图像增强和复原:图像增强和复原的目的是为了提高图像的质量,如去除噪声,提高图像的清晰度等。图像增强不考虑图像降质的原因,突出图像中所感兴趣的部分。如强化图像高频分量,可使图像中物体轮廓清晰,细节明显;如强化低频分量可减少图像中噪声影响。图像复原要求对图像降质的原因有一定的了解,一般讲应根据降质过程建立“降质模型”,再采用某种滤波方法,恢复或重建原来的图像。
    常用方法:灰度增强(直方图均衡化),线性滤波器。

    (4)图像分割:图像分割是数字图像处理中的关键技术之一。图像分割是将图像中有意义的特征部分提取出来,其有意义的特征有图像中的边缘、区域等。
    图像边缘处理方法:边缘检测

    (5)图像描述

    (6)图像分类(识别):图像分类(识别)属于模式识别的范畴,其主要内容是图像经过某些预处理(增强、复原、压缩)后,进行图像分割和特征提取,从而进行判决分类。图像分类常采用经典的模式识别方法,有统计模式分类和句法(结构)模式分类。

    矩阵变换 傅里叶变换与高斯模糊
    1.傅里叶变换
    q
    1.1 能将满足一定条件的某个函数表示成三角函数(正弦和/或余弦函数)或者它们的积分的线性组合。在不同的研究领域,傅立叶变换具有多种不同的变体形式,如连续傅立叶变换和离散傅立叶变换。傅立叶变换是一种分析信号的方法,它可分析信号的成分可用这些成分合成信号

    1.2 性质:线性性质 尺度变换性质 对偶性 平移性质 微分关系 时域卷积定理 频域卷积定理

    1.3应用:
    在这里插入图片描述

    2.高斯模糊
    2.1所谓高斯模糊,就是指一个图像与二维高斯分布的概率密度函数做卷积。它的效果如下:
    在这里插入图片描述
    高斯模糊常常用来模拟人眼中的物体变远、变快的效果。在照片处理中,我们常常将背景施以高斯模糊,使得背景仿佛变远了,从而突出前景的人物或物体。高斯模糊对于图像来说就是一个低通滤波器。

    2.2机理:高斯模糊是一种图像模糊滤波器,它用正态分布计算图像中每个像素的变换。N维空间正态分布方程为

    在这里插入图片描述

    在二维空间定义为
    在这里插入图片描述

    其中r是模糊半径,σ是正态分布的标准偏差。在二维空间中,这个公式生成的曲面的等高线是从中心开始呈正态分布的同心圆。分布不为零的像素组成的卷积矩阵与原始图像做变换。每个像素的值都是周围相邻像素值的加权平均。原始像素的值有最大的高斯分布值,所以有最大的权重,相邻像素随着距离原始像素越来越远,其权重也越来越小。这样进行模糊处理比其它的均衡模糊滤波器更高地保留了边缘效果。

    2.3应用:图像卷积滤波
    它是图像处理最基本的方法,可以产生不同的效果。比如

    在这里插入图片描述
    可以看到原图像经过二维的图像滤波矩阵(卷积核)处理后可以变换成为另一个图像,对于原图像的每一个像素点来说,计算它的领域之内的像素和滤波矩阵对应元素的乘积,相加得到的值作为当前中心像素位置的值,这样就完成了滤波的功能。

    对于滤波器的要求
    ① 滤波器的大小应该是奇数,这样它才有一个中心,例如3x3,5x5或者7x7。有中心了,也有了半径的称呼,例如5x5大小的核的半径就是2。
    ② 滤波器矩阵所有的元素之和应该要等于1,这是为了保证滤波前后图像的亮度保持不变。当然了,这不是硬性要求了。
    ③ 如果滤波器矩阵所有元素之和大于1,那么滤波后的图像就会比原图像更亮,反之,如果小于1,那么得到的图像就会变暗。如果和为0,图像不会变黑,但也会非常暗。
    ④ 对于滤波后的结构,可能会出现负数或者大于255的数值。对这种情况,我们将他们直接截断到0和255之间即可。对于负数,也可以取绝对值。

    2.4卷积核的一些用法

    2.4.1 空卷积核
    滤波后得到的图和原图是一样的。因为只有中心点的值是1。邻域点的权值都是0,对滤波后的取值没有任何影响。

    在这里插入图片描述
    2.4.2图像锐化滤波器

    图像的锐化和边缘检测很像,首先找到边缘,然后把边缘加到原来的图像上面,这样就强化了图像的边缘,使图像看起来更加锐利了。这两者操作统一起来就是锐化滤波器了,也就是在边缘检测滤波器的基础上,再在中心的位置加1,这样滤波后的图像就会和原始的图像具有同样的亮度了,但是会更加锐利。
    在这里插入图片描述
    我们把核加大,就可以得到更加精细的锐化效果
    在这里插入图片描述
    另外,下面的滤波器会更强调边缘

    在这里插入图片描述
    2.4.3浮雕
    浮雕滤波器可以给图像一种3D阴影的效果。只要将中心一边的像素减去另一边的像素就可以了。这时候,像素值有可能是负数,我们将负数当成阴影,将正数当成光,然后我们对结果图像加上128的偏移。这时候,图像大部分就变成灰色了。

    下面是45度的浮雕滤波器

    在这里插入图片描述
    只要加大滤波器,就可以得到更加夸张的效果了

    在这里插入图片描述
    2.4.4均值模糊

    我们可以将当前像素和它的四邻域的像素一起取平均,然后再除以5,或者直接在滤波器的5个地方取0.2的值即可

    在这里插入图片描述
    可以看到,这个模糊还是比较温柔的,我们可以把滤波器变大,这样就会变得粗暴了:注意要将和再除以13.

    在这里插入图片描述
    可以看到均值模糊也可以做到让图片模糊,但是它的模糊不是很平滑,不平滑主要在于距离中心点很远的点与距离中心点很近的所带的权重值相同,产生的模糊效果一样,而想要做到平滑,让权重值跟随中心点位置距离不同而不同,则可以利用正态分布(中间大,两端小)这个特点来实现。

    高斯模糊
    有了前面的知识,我们知道如果要想实现高斯模糊的特点,则需要通过构建对应的权重矩阵来进行滤波。
    3.1正态分布

    在这里插入图片描述
    正态分布中,越接近中心点,取值越大,越远离中心,取值越小。

    计算平均值的时候,我们只需要将"中心点"作为原点,其他点按照其在正态曲线上的位置,分配权重,就可以得到一个加权平均值。正态分布显然是一种可取的权重分配模式。

    3.2 高斯函数–用来描述正态分布
    上面的正态分布是一维的,而对于图像是二维的,所以我们需要二维的正态分布。

    在这里插入图片描述
    正态分布的密度函数叫做"高斯函数"(Gaussian function)。它的一维形式是:
    其中,μ是x的均值,σ是x的方差。因为计算平均值的时候,中心点就是原点,所以μ等于0。
    在这里插入图片描述
    根据一维高斯函数,可以推导二维高斯函数:

    在这里插入图片描述
    通过此函数,可以计算每个点的权重。

    3.3获取权重矩阵
    假设中心点坐标为(0,0)

    在这里插入图片描述
    为了计算权重矩阵,需要设定σ的值。假定σ=1.5,则模糊半径为1的权重矩阵如下:

    在这里插入图片描述
    这9个点的权重总和等于0.4787147,如果只计算这9个点的加权平均,还必须让它们的权重之和等于1,因此上面9个值还要分别除以0.4787147,得到最终的权重矩阵。
    在这里插入图片描述
    除以总值这个过程也叫做”归一问题“

    目的是让滤镜的权重总值等于1。否则的话,使用总值大于1的滤镜会让图像偏亮,小于1的滤镜会让图像偏暗。
    接下来计算高斯模糊的值就好啦。

    代码
    导入头文件

    #include <opencv2/opencv.hpp>
    #include "opencv2/highgui/highgui.hpp"
    #include <iostream>
    #define WINDOW_NAME "【程序窗口】"
    #define PI 3.1415926

    调用摄像头

    VideoCapture video(0);
    while (true)
    {
       Mat frame;//视频的每一帧
       video >> frame;  //将视频写入每一帧
       cvtColor(frame,frame,COLOR_RGB2GRAY);
       cvNamedWindow("frame", WINDOW_AUTOSIZE);
       imshow("frame",frame);
       waitKey(30);
    }
    

    高斯模糊实现

    Mat srcImage = imread("D:\\Desktop\\lena.jpg", 1);  //读取图片
    cvtColor(srcImage, srcImage,COLOR_BGR2GRAY);  //转为灰度图像
    
    namedWindow("srcImage", WINDOW_AUTOSIZE);  //新建窗口
    imshow("srcImage", srcImage);  //显示图像
    //waitKey(0);  //图像显示为一帧
    
    //由于中型差分的原因,输出图像比原始图象少了两列
    Mat dImage = Mat(srcImage.rows,srcImage.cols - 2,CV_8UC1);
    
    //循环遍历整个图像
    for (int i = 0; i < srcImage.rows; i++)
    {
        for (int j = 1; j < srcImage.cols - 1; j++)
        {
            //对整型数据类型进行运算,进行中型差分
            dImage.at<uchar>(i, j - 1) = srcImage.at<uchar>(i, j + 1) - srcImage.at<uchar>(i, j - 1);
        }
    }
    namedWindow("dImage", WINDOW_AUTOSIZE);  //处理后图像窗口
    imshow("dImage", dImage);  //显示处理后的图像
    
    waitKey(0);  //图像显示为一帧
    
    Mat srcImage = imread("D:\\Desktop\\lena.jpg", 1); //读取图片
    cvtColor(srcImage, srcImage, COLOR_BGR2GRAY);      //转为灰度图像
    namedWindow("srcImage", WINDOW_AUTOSIZE);          //新建窗口
    imshow("srcImage", srcImage);                      //显示图像
    
    /*高斯模糊*/
    
    //5×5卷积模板
    Mat model = Mat(5, 5, CV_64FC1);
    double sigma = 80;  //超参数,根据经验所得
    for (int i = - 2; i <= 2 ; i++)  //进行遍历
    {
        for (int j = -2; j <= 2; j++)
        {
            //正态分布
            model.at<double>(i + 2, j + 2) =
                exp(-(i * i + j * j) / (2 * sigma * sigma)) /
                (2 * PI * sigma * sigma);
        }
    }
    
    //归一化
    double gaussSum = 0;
    gaussSum = sum(model).val[0];  //卷积核 求和
    for (int i = 0; i < model.rows; i++)
    {
        for (int j = 0; j < model.cols; j++)
        {
            model.at<double>(i, j) = model.at<double>(i, j)
                / gaussSum;
        }
    }
    
    Mat dst = Mat(srcImage.rows - 4,srcImage.cols - 4,CV_8UC1);
    
    //对整个图片进行遍历卷积
    for (int i = 2; i < srcImage.rows - 2; i++)
    {
        for (int j = 2; j < srcImage.cols - 2; j++)
        {
            double sum = 0;  //求和目标值
            for (int m = 0; m < model.rows; m++)
            {
                for (int n = 0; n < model.cols; n++)
                {
                    sum += (double)srcImage.at<uchar>(i + m - 2, j + n - 2) * 
                        model.at<double>(m,n);  //对整个卷积核进行卷积
                }
            }
            dst.at<uchar>(i - 2, j - 2) = (uchar)sum;  //结果赋值到dst图像当中
        }
    }
    namedWindow("gaussBlur", WINDOW_AUTOSIZE);
    imshow("gaussBlur", dst);
    waitKey(0); //图像显示为一帧

    调用高斯模糊库函数

    Mat dst = srcImage.clone();
    //一行代码高斯模糊
    GaussianBlur(srcImage, dst, Size(17, 17), 180);
    namedWindow("gaussBlur", WINDOW_AUTOSIZE);
    imshow("gaussBlur", dst);
    waitKey(0); //图像显示为一帧

    运行结果
    在这里插入图片描述
    参考
    [高斯模糊(https://www.cnblogs.com/invisible2/p/9177018.html)
    [(https://imlogm.github.io/图像处理/image-fft/)
    [(http://www.cvvision.cn/8907.html)
    [https://www.jianshu.com/p/191d1e21f7ed?tdsourcetag=s_pcqq_aiomsg)
    百度百科

    培训感受
    感觉学长们很厉害,可以把很多东西深入浅出的讲解,让我们这些小白对图像处理方面有一些全新的认识和了解,给我们提供了又一个新的方向,但是我知道,师傅领进门修行在个人,希望自己做好手边的每一件事情,谢谢各位前辈的指点(●ˇ∀ˇ●)
    要努力( •̀ ω •́ )

    展开全文
  • 计算机视觉——OpenCV

    2019-02-25 13:06:06
    源码:https://github.com/PacktPublishing/OpenCV3-Computer-Vision-Application-Programming-Cookbook-Third-Edition 访问像素值 用指针扫描图像 用迭代器扫描图像 ...在OpenCV的C++API中,所有的类和...

    源码:https://github.com/PacktPublishing/OpenCV3-Computer-Vision-Application-Programming-Cookbook-Third-Edition

    • 访问像素值
    • 用指针扫描图像
    • 用迭代器扫描图像
    • 编写高效的图像扫描循环
    • 扫描图像并访问相邻像素
    • 实现简单的图像运算
    • 图像重映射

    在OpenCV的C++API中,所有的类和函数都在命名空间cv内定义。访问他们的方法共有两种:

    (1)在定义main函数前使用如下声明:

    using namespace cv ;

    (2)是根据命名空间规范给所有OpenCV的类和函数加上前缀cv::,添加前缀后,代码中OpenCV的类和函数将更容易识别。

    highgui模块中国有一批能帮助我们轻松显示图像并对图像进行操作的函数。

    //读取一个图像文件,并将其转换为灰度图像
    image = cv::imread("puppy.bmp",CV::IMREAD_GRAYSCALE);

    使用imread函数装在图像时,通过设置选项把它转化为灰度图像,有些计算机视觉算法必须使用灰度图像。在读入图像的同时进行色彩转换,可以提高运行速度并减少内存的使用。这样生成的图像由无符号字节(unsigned byte,c++中为unsigned char)构成,在OpenCV中用常量CV_8U表示。

    //读取图像,并将其转换为三通道彩色图像
    image = cv::imread("puppy.bmp",CV::IMREAD_COLOR);

    在这样创建的图像中,每个像素有3个字节,OpenCV中用CV_8UC3表示。如果输入的图像文件是灰度图像,这三个通道的值就是相同的。如果在读入图像时采用文件本身的格式,只需把第二个参数设置为负数。

    std::cout<<"this image has "<<image.channels()<<"channel(s)";

    可用channels方法检查图像的通道数。

    注意:当用imread打开路径置顶不完整的图像时,imread会自动采用默认目录:

    如果从控制台运行程序,默认路录显然就是当前控制台的目录;

    如果直接在IDE中运行程序,默认目录就是项目文件所在的目录(主函数文件所在目录);

     

    imshow显示由整数(CV_16U表示16为无符号整数,CV_32S表示32为有符号整数)构成的图像时,图像每个像素会被除以256,以便能够与在256级灰度中显示。同样在显示由浮点数构成的图像时,指的范围会被假设为0.0(显示黑色)~1.0(显示白色)。超出这个范围的值会被显示为白色(大于1.0的值)或者黑色(小于0.0的值)。

    cv::flip(image,image,1);//就地处理,对image进行水平翻转
    //正数表示水平,0表示垂直,负数表示水平和垂直

    回调函数:响应特定事件的时候被程序调用。为了能被程序识别,回调函数需要具备特定的签名,并且必须注册。

    cv::Mat iamge(240,320,CV_8UC3,cv::Scanlar(0,0,255));//创建一个红色图像,通道次序为BGR

    CV ::Mat类是用来存放图像(以及其他矩阵数据)的数据结构。

    一旦没有了指定cv::Mat对象的引用,分配的内存就会被自动释放。避免了C++动态内存分配中经常发生的内存泄漏问题。实现方法是通过cv::Mat实现计数引用和浅复制。深复制可以使用copyTo方法。或者产生一个图像副本的方法clone。

    把一幅图像复制到另一幅图像中,且两者的数据类型不一定相同,那就要使用convertTo方法;

    图像掩码:

    OpenCV中国的有些操作(copyTo)可以用来定义掩码。函数或方法通常对图像中所有的像素进行操作,通过定义掩码可以限制这些函数或方法的作用范围。掩码是一个8位图像,如果掩码中某个位置的值不为0,在这个位置上的操作就会起作用;如果为0,则不起作用。

    操作像素:

    椒盐噪声(salt-and-pepper noise):它随机选择一些像素,把他们的颜色替换成白色或者黑色。如果通信出错,部分像素的值在传输时丢失,就会产生这种噪声。

    为了降低分析的复杂性,有时需要减少图像多种颜色的数量。一种实现方法就是把RGB空间细分到大小相等的方块中。例如,如果把每种颜色数量减少到1/8,那么颜色总数就变为了32*32*32.将旧图像中的每个颜色值划分到一个方块,该方块的中间值就是新的颜色值;新图像使用新的颜色值,颜色数就减少了。

    减色算法实现:假设N是减色因子,将图像中每个像素的值除以N(这里假设使用整数除法,不保留余数)。然后将结果乘以N,得到N的倍数,并且刚好不超过院士像素值。加上N/2,就得到相邻的N倍数之间的中间值。对所有8位通道值重复这个过程,就会得到(256/N)*(256/N)*(256/N)种可能的颜色值。

    其他减色算法:

    data[i] = (data[i] / div)*div +div/2;

    减色计算也可以使用取模运算符,可以直接得到div的倍数:

    data[i] = data[i] - data[i] % div +div /2;

    还可以使用位运算。如果把减色因子限定为2的指数,即div = pow(2,n),那么把像素值的前n为掩码后就能得到最接近的div的倍数。可以用简单的位操作获得掩码:

    //用来截取像素值的掩码
    uchar mask = 0xFF << n;  //如div = 16,则mask= 0xF0
    *data & = mask; //掩码
    *data++  += div >>1;  //加上div/2
    //这里的+也可改为“按位或”运算符

    锐化滤波器:

    0

    -1

    0

    -1

    5

    -1

    0

    -1

    0

    在对像素领域进行计算式,通常用一个核心矩阵来表示。这个核心矩阵展现了如何将于计算相关的像素组合起来得到预期的效果。

    除非另有说明,当前像素用核心矩阵中心单元格表示。核心矩阵中的每个单元格表示相关像素的乘法系数,像素应用核心矩阵得到的结果,即这些乘积的累加。核心矩阵的大小就是邻域的大小。

    根据锐化滤波器的要求,水平和垂直方向的四个相邻像素与-1相乘,当前像素与5相乘。这也是信号处理中卷积概念的基础。

    • 用策略设计模式比较颜色
    • 用GrabCut算法分割图像
    • 转换颜色表示法
    • 用色调、饱和度和亮度表示颜色

    策略设计模式:

    策略设计模式把算法封装进类,尽可能的将算法的复杂性隐藏在一个直观的编程接口后面,更有利于算法的部署。可以通过创建类的实例来部署算法,实例通常是在程序初始化的时候创建的。在运行构造函数时,类的实例会用默认值初始化算法的各种参数,使其立即进入可用状态。还可以用适当的方法来读写算法的参数值。

    GrabCut算法:

    背景/前景分割步骤:

    1. 首先,把所有未标记的像素临时标记为前景(cv::GC_PR_FGD)。基于当前的分类情况,算法把像素划分为多个颜色相似的组(即k个背景组和k个前景组)。
    2. 通过引入前景和背景像素之间的边缘,确定背景/前景的分割(通过一个优化过程来实现),在此过程中,将试图连接具有相似标记的像素,并且避免边缘出现在强度相对均匀的区域。(Graph Cuts算法可以高效的解决这个优化问题:把问题表示成一幅连通的图形,然后在图形上进行分割,以形成最优的形态。分割完成后,像素会有新的标记。然后重复这个分组过程,找到新的最优分割方案,如此反复。)

    GrabCut算法是一个逐步改进分割结果的迭代过程。

    CIEL*a*b颜色模型:

    利用RGB色彩空间计算颜色之间的差距并不是衡量两个颜色相似度的最好方式。实际上,RGB并不是感知均匀的色彩空间。

    CIEL*a*b*就是一种具有感知均匀特性的颜色表示法。把图像转换到这种表示法后,就可以真正的使用图像像素与目标颜色之间的欧几里得距离,来度量颜色之间的视觉相似度。

    使用OpenCV的函数cv::cvtColor可以轻松的转换图像的色彩空间。

    //转换成Lab色彩空间
    cv::cvtColor(image,converted,CV_BGR2Lab);

    CIEL*u*v*是另一种感知均匀的色彩空间。若想从BGR转换成CIEL*u*v*,可使用代码CV_BGR2Luv.

    L*a*b*和L*u*v*对亮度通道使用同样的转换公式,但对色度通道则使用不同的表示法。,另外,为了实现视觉感知上的均匀,这两种色彩空间都扭曲了RGB的颜色范围,所以这些转换过程都是非线性的(不完全可逆)。

    HSV和HLS这两种色彩空间,把颜色分解成加值的色调和饱和度组件或亮度组件。用这种方式描述的颜色会更加自然。

    把图像的通道分割到三个独立的图像中,看到HSV的组件:

    std::vector<cv::Mat> channels;
    cv:split(hsv , channels);
    //channels[0]是色调
    //channels[1]是饱和度
    //channels[2]是亮度

    引入色调/饱和度/亮度的色彩空间概念:因为人们喜欢凭直觉用色彩、彩度、亮度等直观属性来描述分辨各种颜色,色调(hue)表示主色,饱和度(saturation)表示颜色的鲜艳程度,柔和的颜色饱和度较低,而彩虹的颜色饱和度就很高;亮度(brightness)是一个主观的属性,表示某种颜色的光亮程度。

    注意:如果颜色的饱和度很低,计算出来的色调就很不靠谱。7

    肤色检测领域的大量研究已经表明:来自不同人种的人群的皮肤颜色,可以在色调-饱和度色彩空间中很好的归类。

    • 计算图像直方图
    • 利用查找表修改图像外观
    • 直方图均衡化
    • 反向投影直方图检测特定图像内容
    • 用均值平移算法查找目标
    • 比较直方图搜索相似图像
    • 用积分图像统计像素

    直方图是一个简单的表格,表示一幅图像(有时是一组图像)中具有某个值的像素的数量。因此,灰度图像的直方图有256个项目。也叫箱子(bin)。0号箱子提供值为0的像素的数量,1号箱子提供值为1 的像素的数量,以此类推。如果把直方图的所有箱子进行累加,得到的结果就是像素的总数。可以把直方图归一化,即所有箱子的累加和等于1。这时,每个箱子的数值表示对应的像素数量占总数的百分比。

    利用查找表修改图像外观:

    1、对像素强度进行简单的反转,即强度为0变成255、1变成254、255变成0,生成原始图像的反向图像。

    2、定义一个修改原始图像直方图的查找表可以提高图像的对比度。假设图中根本没有大于200的像素值。可以通过延伸直方图来生成以个对比度更高的图像。使用以个百分比阈值,在强度值中找到最小值和最大值,然后重新映射。

    3、在彩色图像上应用查找表实现减色函数。

    直方图均衡化:

    通过伸展直方图使它布满可用强度值的全部范围,增强图像对比度。但是很多时候,图像的视觉缺陷并不因为它的使用的强度值范围太窄,而是因为部分强度值的使用频率远高于其他强度值。因此,均衡对所有像素强度值的使用频率可以作为提高图像质量的一种手段。(查找表是针对整幅图像的多对一的转换过程,所以直方图是不能做到完全平稳的。)

    反向投影直方图检测特定图像内容:

    假设需要在某幅图像中检测出特定的内容,首先要做的就是选择一个包含所需样本的感兴趣的区域。接着提取该ROI的直方图,通过归一化直方图,可以得到一个函数,得到特定强度值的像素属于这个区域的概率。反向投影直方图的过程:从归一化后的直方图中读取概率值并把输入图像中的每个像素替换成与之对应的概率值。(实验中除了云彩,其他区域也被错误的检测到了,这个概率函数是从简单的灰度直方图提取的,很多其他像素的强度值域云彩像素的强度值是相同的,在对直方图进行反向投影时会用相同的概率值替换相同强度值的像素。还可以进一步利用色彩信息提高检测效果。)

    均值偏移算法:

    均值偏移算法是一个迭代过程,用于定位概率函数的局部最大值,方法是寻找预定义窗口内部数据点的重心或加权平均值。然后,把窗口移动到重心的位置,并重复该过程,知道窗口中心收敛到一个稳定的点。OpenCV实现该算法定义了两个停止条件:迭代次数达到最大值;窗口中心的偏移值小于某个限值,可认为该位置收敛到一个稳定点。

    比较直方图搜索相似图像:

    计算机视觉的重要课题:基于内容的图像检索;而直方图是标识内容的一种有效方式;

    原理:逐个箱子的比较每个直方图中的数值,并保存最小的值。然后把这些最小值累加,作为相似度测量值。(两个没有相同颜色的直方图得到的交叉值为0 ,而两个完全相同的直方图得到的值就等于像素总数。)

    其他算法:

    • 卡方测量法(cv::HISTCMP_CHISQR标志)累加个箱子的归一化平方差;
    • 关联性算法(cv::HISTCMP_CORREL标志)基于信号处理中的归一化交叉关联操作符测量两个信号的相似度。
    • Bhattacharyya测量法(cv::HISTCMP_BHATTACHARYYA标志)和Kullback-Leibler发散度(cv::HISTCMP_KL_DIV标志)都在用统计学中,评估两个概率分布得相似度)
    • 推土机距离(Earth Mover Distance)是另一种流行的直方图比较方法,在OpenCV中通过cv::EMD函数实现。这个方法的主要优势在于,它在评估两个直方图的相似度时,考虑了在邻近箱子中发现的数值。

    积分图像:

    快速计算矩形区域内的像素累加和;

    原理:取矩形图像左上方的全部像素计算累加和,并利用这个累加和替换图像中的每一个像素,用这种方式得到的图像称为积分图像。计算积分图像时,只需要对图像扫描一次。实际上,当前像素的积分值等于上方像素的积分值加上当前行的累计值。因此积分图像就是一个包含像素累加和的新图像。为了防止溢出,积分图像的值通常采用int类型(CV_32S)或float类型(CV_32F)。

    积分图像的像素A包含左上角区域,计算完积分图像后,只需要访问四个像素就可以得到任何矩形区域的像素累加和。计算有A、B、C、D四个像素表示区域的像素累加和,先读取D的积分值,然后再减去B的像素值和C的左手边区域的像素值。但这样就把A左上角的像素累加和减了两次,因此需要重新加上A的积分值。所以计算A、B、C、D区域内的像素累加的公式为A-B-C+D。

    自适应阈值化:

    即采用局部阈值,根据美国像素的邻域计算阈值。将每个像素的值域邻域的平均值进行比较。如果某像素的值域它的局部平均值差别很大,就会被当做异常值在阈值化过程中剔除。(自适应阈值化需要计算每个像素周围的局部平均值,这需要多次计算图像窗口的累计值,可以通过积分图像提高计算效率。)

    bug

    OpenCV的颜色空间转换函数:

    参数dstCn原来一直沿用CV_BGR2GRAY, CV_RGB2GRAY, CV_GRAY2BGR, CV_GRAY2RGB等格式,但最新几个版本的OpenCV已改为COLOR_BGR2GRAY类似形式,今天才看源代码imgproc.hpp才发现,但官方文档还没修改,如下图,如果程序中使用较新的版本OpenCV,采用CV_BGR2GRAY可能会一直出错。

    新的OpenCV中imgproc.hpp的定义如下:

    ...
    COLOR_BGR2HSV =40,
    COLOR_RGB2HSV =41,
    COLOR_BGR2Lab =44,
    COLOR_RGB2Lab =45,
    ...

    解决:使用OpenCV3.4版本,导入opencv2

    • 用形态学滤波器腐蚀和膨胀图像
    • 用形态学滤波器开启和闭合图像
    • 在灰度图像中应用形态学运算
    • 在分水岭算法实现图像分割
    • 用MSER算法提取特征区域

    数学形态学:用于分析和处理离散图像。它定义了一系列运算,用预先定义的形状元素探测图像,从而实现图像的转换。这个形状元素与像素邻域的相交方式决定了运算的结果。

    用形态学滤波器腐蚀(cv::erode)和膨胀(cv::dilate)图像

    结构元素:像素的组合。在对应的像素上定义了一个原点(也称锚点)

    形态学滤波器的应用过程就包含了用这个结构元素探测图像中每个像素的操作过程。把某个像素设为结构元素的原点后,结构元素的图像重叠部分的像素集就是特定形态学运算的应用对象。

    用形态学滤波器开启和闭合图像

    开启和闭合滤波器的定义只与基本的腐蚀和膨胀运算有关;

    定义:

    闭合:是对图像先膨胀后腐蚀;开启:是对图像先腐蚀后膨胀;

    //膨胀原图像
    cv::dilate(image,result,cv::Mat());
    //就地腐蚀膨胀后的图像
    cv::erode(result,result,cv::Mat());

    调换这两个函数的调用次序,就能得到开启滤波器;滤波器常用于目标检测;

    闭合滤波器:可以把错误分裂成小碎片的物体连接起来;

    开启滤波器:可以移除因图像噪声产生的斑点;

    因此:最好按一定的顺序调用这些滤波器。如果优先考虑过滤噪声,可以先开启后闭合,但这样做的坏处是会消除部分物体碎片;

    注意:对同一幅图像进行多次同样的开启运算是没有作用的(闭合运算也一样),因为第一次使用开启滤波器时已经填充了空隙,再使用同一个滤波器将不会使图像产生变化。即这些运算是幂等(idempotent)的。

    形态学梯度运算可以提取图像的边缘(cv::morpholgyEx函数)得到图像中物体的轮廓。

    //用3*3结构元素得到梯度图像
    cv::Mat result;
    cv::morphlogyEx(image,result,cv::MORPH_GRADIENT,cv::Mat());

    形态学顶帽(hat-top)变换可以从图像中提取出局部的小型前景物体。(提取图像中的文字)

    //使用7*7结构元素做顶帽变换
    cv::Mat element7(7,7,CV_8U,cv::Scanlar(1));
    cv::morphologyEx(image,result,cv::MORPH_BLACKHAT,element7);

     形态学运算在灰度图像上的效果理解:

    把图像看做是一个拓扑地貌,不同灰度级别代表不同的高度(海拔),明亮的区域代表高山,黑暗的区域代表深谷;边缘相当于黑暗和明亮像素之间的快速过渡,因此可以比作陡峭的悬崖。腐蚀这种地形的最终结果是:每个像素被替换成特定领域内的最小值,从而降低它的高度。结果是悬崖“缩小”,山谷“扩大”。膨胀的效果刚好相反,即悬崖“扩大”,山谷“缩小”。但不管哪种情况,平地(即强度值固定的区域)都会相对保持不变。

    根据这个结论,可以得到一种检测图像边缘(悬崖)的简单方法Beucher梯度:通过计算膨胀后的图像与腐蚀后的图像之间的差距得到边缘。因为这两种转换后的图像的差别主要在边缘地带。显然,结构元素越大,检测到的边缘就越宽。

    另外两种简单的方法得到类似效果:用膨胀后的图像减去原始图像,或者用原始图像减去腐蚀后的图像,那样得到的边缘会更窄。

    分水岭算法实现图像分割

    分水岭变换是一种流行的图像处理算法,用于快速将图像分割成多个同质区域。

    思想:如果把图像看作一个扩屏地貌,那么同类区域就相当于陡峭边缘内相对平坦的盆地。平、分水岭算法通过逐步增高水位,把地貌分割成多个部分。(cv::watershed函数)

    MSER算法提取特征区域

    最大稳定外部区域(MSER)

    该算法用相同的水淹类比,以便从图像中提取有意义的区域。创建这些区域时也使用逐步提高水位的方法,但是该算法关注的是水淹过程中的某段时间内,保持相对稳定的盆地。这个稳定的盆地就是MSER。检测方法:计算区域的当前面积以及该区域原先的面积(比当前水位低一个特定值的时候),并比较这两个面积,如果相对变化达到局部最小值,就认为这个区域时MSER。

    • 用低通滤波器进行图像滤波
    • 用滤波器进行缩减像素采样
    • 用中值滤波器进行图像滤波
    • 用定向滤波器检测边缘
    • 计算图像的拉普拉斯算子

    滤波是信号和图像处理中的基本操作,它的目的是选择性的提取图像中某些方面的内容;滤波可以去除图像中的噪声,提取有用的视觉特征,对图像重新采样。

    频域(frequency domain):图像中灰度级的变化频率;傅里叶变换或余弦变换显示图像的频率成分;

    图像是二维的,因此,频率分为:垂直频率和水平频率;

    空域(spatial domain):灰度分布来描述图像特征;

    在频域分析框架下,滤波器时一种放大(也可以不变)图像中的某些频段,同时滤掉(或减弱)其他频段的算子。eg:低通滤波器的作用是消除图像中的高频部分;高通滤波器刚好相反,用来消除图像中的低频部分;

    缩减像素采样(downsampling):降低图像精度的过程;

    提升像素采样(upsampling):提升图像精度的过程;

    Nyquist-Shannon定理:如果把图像缩小一半,那么其可见的频率带宽也将减少一半。为了避免混叠现象的发生,在缩减图像之前必须进行低筒滤波(低通滤波可以消除在缩减后的图像中无法表示的高频部分)

    像素插值:进行插值的最基本的方法是使用最近邻策略。把待生成图像的像素网格放在原图像的上方,每个新像素被赋予原图像中最邻近像素的值。当像素升采样(即新网格比原始网格更密集时),会根据同一个院士像素,确定新网格中多个像素的值。

    中值滤波器:因为中值滤波器是非线性的,所以不能用核心矩阵表示,也不能进行卷积运算。但它也是通过操作一个像素的邻域,来确定输出的像素值的。(中值滤波器把当前像素和它的邻域组成一个集合,然后计算出这个集合的中间值,以此作为当前像素的值)。

    中值滤波器对消除椒盐噪声非常有用;因为,如果在某个像素邻域中有一个异常的黑色或白色像素,该像素将无法作为中间值(它是最大值或最小值),因此肯定会被邻域的值替换掉。

    中值滤波器还有利于保留边缘的尖锐度,但它会洗去均质区域中的纹理(例如背景中的树木)

    因为中值滤波器具有良好的视觉效果,因此照片编辑软件常用它创建特效。可以用彩色照片生成类似卡通的图像。

    用定向滤波器检测边缘:

    高通滤波器进行边缘检测:放大图像中的高频成分;

    Sobel滤波器:它只对垂直或水平方向的图像频率起作用(具体方向取决于滤波器选用的内核)所以是一种定向滤波器。(图像浮雕化的特效就是定向滤波器生成的)Sobel算子称作边缘检测器,是一种典型的用于边缘检测的线性滤波器。如果把图像看作二维函数,那么Sobel算子就是图像在垂直和水平方向变化的速度(梯度),它是一个二维向量,向量的元素时横竖两个方向的函数的一阶导数;

    Sobel算子在水平和垂直方向计算像素值的差分,得到图像梯度的近似值。它在像素周围的一定范围内进行运算,以减少噪声带来的影响。cv::Sobel函数使用Sobel内核来计算图像的卷积。

    梯度是一个二维向量,所以它有范数和方向,梯度向量的范数表示变化的振幅,计算时通常被当做欧几里得范数(L2范数)。

    梯度向量总是指向变化最剧烈的方向。对于一幅图像来说,这意味着梯度的方向与边缘垂直;

    梯度算子:Prewitt算子;Roberts算子;Scharr算子;所有的这些定向滤波器都会计算图像函数的一阶导数。计算图像导数的滤波器被称为高通滤波器。

    拉普拉斯算子(cv::Laplacian):一种基于图像导数运算的高通线性滤波器,它通过计算二阶导数来度量图像函数的曲率;

    用拉普拉斯算子增强图像的对比度:通过图像中减去它的拉普拉斯图像,可以增强图像的对比度;

    高斯差分:用两个不同带宽的高斯滤波器对一幅图像做滤波,然后将这两个结果相减,就能得到由较高的频率构成的图像。这些频率被一个滤波器保留,被另一个滤波器抛弃。

    • 用Canny算子检测图像轮廓
    • 用霍夫变换检测直线
    • 点集的直线拟合
    • 提取连续区域
    • 计算区域的形状描述子

    要进行基于内容的图像分析,就必须从构成图像的像素集中提取出有意义的特征:轮廓、直线、斑点等基本的图像图元;

    用Canny算子检测图像轮廓

    核心原理:用两个不同的阈值来判断哪个点属于轮廓,一个是低阈值,一个是高阈值;

    选择低阈值:保证它能包含所有属于重要图像轮廓的边缘像素。(由于使用了比较宽松的阈值,所以很多并不需要的边缘也被检测出来了)

    选择高阈值:作用就是界定重要轮廓的边缘,排除掉异常的边缘。(得到肯定属于本场景中的重要轮廓)

    Canny算法将这两种边缘分布图结合,生成最优的轮廓分布图(在低阈值边缘分布图上只保留具有连续路径的边缘点,同时把那些边缘点连接到属于高阈值边缘分布图的边缘上。)这种基于两个阈值获得二值分布图的策略被称为滞后阈值化;

    用霍夫变换(Hough transform)检测直线

    在霍夫变换中,用这个方程式表示直线:

    ρ = X cosθ + Y sin θ    //ρ可以为负数

    霍夫变换的目的是在二值图像中找出全部直线,并且这些直线必须穿过足够多的像素点。

    处理方法:检查输入的二值分布图中每个独立的像素点,识别出穿过该像素带你的所有可能直线。如果同一条直线穿过很多像素点,就说明这条直线明显到足以被认定。为了统计某条直线被标识的次数,霍夫变换使用了一个二维累加器。

    霍夫变换也能用来检测其他任何可以用一个参数方程来表示的物体,但一般来说参数越多,累加器的维数越多,霍夫变换就越复杂,可靠性也越低;

    点集的直线拟合:

    使每个点到直线的距离之和最小化(欧几里得距离的计算速度最快),最小化计算的基础是M估算法技术,它采用迭代方式解决加权最小二乘法问题,其中权重与点到直线的距离成反比。

    提取连续区域

    原理:提取轮廓的算法,它系统的扫描图像,直到找到连续区域。从区域的起点开始,沿着它的轮廓对边界像素做标记。处理完这个轮廓后,就从上个位置继续扫描,直到发现新的区域。

    计算区域的形状描述子

    连续区域通常代表着场景中的某个物体。为了识别该物体,或将它与其他图像元素作比较,需要对此区域进行测量,以提取出部分特征。

    边界框:

    在表示和定位图像中区域的方法中,边界框可能是最简洁的。

    定义:能完整包含该形状的最小垂直矩形。比较边界框的高度和宽度,可以获得物体在垂直或水平方向的特征,最小覆盖圆通常在只需要区域尺寸和位置的近似值时使用;

    如果要更紧凑的表示区域的形状,可采用多边形逼近。在创建时要制定精确度参数,表示形状与对应的简化多边形之间能接受的最大距离。

    • 检测图像中的角点
    • 快速检测特征
    • 尺度不变特征的检测
    • 多尺度FAST特征的检测

    兴趣点(关键点或特征点):在图像中选取某些特征点并对图像进行局部分析(提取局部特征),而非观察整幅图像(提取全局特征)只要图像中有足够多可检测的兴趣点,并且这些兴趣点各不相同且特征稳定、能被精确定位。

    检测图像中的角点

    在图像中搜索有价值的特征点时,使用角点是一种不错的方法。角点是很容易在图像中定位的局部特征。并且大量存在于人造物体中,角点的价值在于它是两条边缘线的结合点,是一种二维特征,可以被精确的检测,Harris特征检测是检测角点的经典方法。

    Harris特征检测:在假定的兴趣点周围放置了一个小窗口,并观察窗口内某个方向上强度值的平均变化;

    首先获得平均强度值变化最大的方向,然后检查垂直方向上的平均强度变化值,看它是否也很大;如果是,就说明这是一个角点;判断一个点为角点的条件是它的协方差矩阵的最小特征值要大于指定的阈值。

    快速检测特征:

    FAST对角点的定义基于候选特征点周围的图像强度值。以某个点为中心做一个圆,根据圆上的像素值判断该点是否为关键点。如果存在这样一段圆弧,它的连续长度超过周长的3/4,并且它上面所有像素的强度值都与圆心的强度值明显不同,那么久认为这是一个关键点。

    尺度不变特征的检测

    SURF特征(加速稳健特征)不仅是尺度不变特征,而且是具有较高计算效率的特征。是SIFT算法的加速版。SURT和SIFT是受专利保护的,在用于商业应用程序是必须遵守许可协议。这也是它们被放在cv::xfeatures2d包中的原因之一。

    多尺度FAST特征的检测

    BRISK(二元稳健恒定可扩展关键点)检测法

    ORB(定向FAST和旋转BRIEF)检测法 (原理基于每个被检测的兴趣点总是关联了一个方向,ORB算法建议使用关键点周围的圆形邻域的重心方向。因为根据定义,FAST关键点肯定有一个偏离中心点的重心,中心点与重心连线的角度重视非常明确) 这两种可以实现快速可靠的图像匹配,如果搭上相关的二值描述子,它们的性能可以进一步提高。

    • 局部模板匹配
    • 描述并匹配局部强度值模式
    • 用二值描述子匹配关键点

    局部模板匹配:

    通过特征点匹配,可以将一幅图像的点集和另一幅图像(或一批图像)的点集关联起来。如果两个点集对应着现实世界中的同一个场景元素,它们就应该是匹配的。

    仅凭单个像素就判断两个关键点的相似度显然是不够的,因此要在匹配过程中考虑每个关键点周围的图像块。如果两幅图像块对应着同一场景元素,那么它们的像素值应该会比较相似。

    最常见的图像块是边长为奇数的正方形,关键点的位置就是正方形的中心。可通过比较块内像素的强度值来衡量两个正方形图像块的相似度。(常见的方案是采用简单的差的平方和(Sum of Squared Differents,SSD)先使用FAST检测器检测每幅图像的关键点。

    只要两幅图像的视角和光照都比较相似,仅用差值平方和来比较两个图像窗口也能得到较好的结果。

    实际上,只要光照有变化,图像块中所有像素的强度值就会增强或降低,差值平方也会发生很大的变化。为了减少光照对匹配结果的影响,还可以采用衡量图像窗口相似度的其他公式:如归一化的差值平方和

    模板匹配:

    图像分析中的常见任务是检测图像中是否存在特定的图案或物体。实现方法是把包含该物体的小图像作为模板,然后在指定图像上搜索与模板相似的部分。搜索的范围通常仅限于可能发现该物体的区域。在这个区域上滑动模板,并在每个像素位置计算相似度。

    cv::matchTemplate  //函数的输入对象是一个小图像模板和一个被搜索的图像。

    模板尺寸M×N,图像尺寸为W×H,则结果矩阵的尺寸就是(W-M+1)×(H-N+1)

    描述并匹配局部强度值模式:

    在图像分析中,可以用邻域包含的视觉信息来标识每个特征点,以便区分各个特征点。特征描述子通常是一个N维向量,在光照变化和拍摄角度发生微小扭曲时,它描述特征点的方式不会发生变化。通常可以用简单的差值矩阵来比较描述子(欧几里得距离)。

     

    使用SURF和SIFT的特征和描述子可以进行尺度无关的匹配。

    1、交叉检查匹配项

    有一种简单的方法可以验证得到的匹配项,即重新进行同一个匹配过程,但在第二次匹配时,将第二幅图像的每个关键点逐个与第一幅图像的关键点进行比较。只有在两个方向都匹配了同一对关键点(即两个关键点互为最佳匹配)时,才认为是一个有效的匹配项。

    cv::BFMatcher matcher2(cv::NORM_L2,  //度量差距
                                 true);  //交叉检查标志

    2、比率检验法

    当场景中有很多相似的物体,一个关键点可以与多个其他关键点匹配,错误匹配项非常多,需要将其排除。因此需要找到两个最佳的匹配项。循环遍历每个关键点匹配项,然后执行比率检验法。

    3、匹配差值的阈值化

    把描述子之间的差值太大的匹配项排除。(多个策略结合使用提升匹配效果)

     

     

    • 计算图像对的基础矩阵
    • 用RANSAC算法匹配图像
    • 计算两幅图像之间的单应矩阵
    • 检测图像中的平面目标

     

    计算图像对的基础矩阵:

    我们知道,沿着三维点X和相机中心点之间的连线,可在图像上找到对应的点x。反过来,在三维空间中,与成像平面上的位置x对应的肠镜点可以位于线条上的任何位置。这说明如果要根据图像中的一个点找到另一幅图像中对应的点,就需要在第二个成像平面上沿着这条线的投影搜素。这条虚线称为点x的对极线。它规定了两个对应点必须满足的基本条件,即对于一个点,在另一个视图中与它匹配的点必须位于它的对极线上,并且对极线的准确方向取决于两个相机的相对位置。事实上,所有对极线组成的结构决定了双视图系统的几何形状。

    用RANSAC(随机抽样一致性)算法匹配图像

    计算两幅图像之间的单应矩阵

    仍然考虑三维点和它在相机中的影像之间的投影关系,会发现有两种特殊情况,这种特殊的矩阵称为单应矩阵:

    第一:同一场景中两个视图之间的差别只有(这是外部矩阵的第四列全部变为0,即没有平移量),在这种特殊情况下,投影关系就变为了3×3的矩阵。

    第二:如果拍摄目标是一个平面,也会出现类似的情况,这时,可以假设仍能保持通用性,即平面上的点都位于Z=0的位置。场景点中值为0 的坐标会消除掉投影矩阵的第三列,从而又变成一个3×3的矩阵。

    在这种特殊情况下,世界坐标系的点和它的影像之间是线性关系。由于该矩阵是可逆的,所以只要两个视图只是经过了旋转或者拍摄的是平面物体,那么就可以将一个视图中的像素点与另一个视图中的对应的像素点直接关联起来。单应矩阵的格式为:

    其中H是一个3×3矩阵。这个关系式包含了一个尺度因子,用s表示。计算得到这个矩阵后,一个视图中的所有点都可以根据这个关系式转换到另一个视图。(注意,在使用单位矩阵关系式后,基础矩阵就没有意义了。)

    检测图像中的平面目标

    采取的方法是检测这个平面物体的特征点,然后试着在图像中匹配这些特征点。然后用鲁棒匹配方案来验证这些匹配项,但要基于单应矩阵。如果有效匹配项的数量很多,就说明该平面目标在当前图像中。

    由于不知道图像中目标物体的大小,所以我们把目标图像转换成一系列不同的尺寸,构建成一个金字塔。除了这种方法,也可以采用尺度不变特征。

    执行三个步骤:

    1、在输入图像中检测兴趣点。

    2、将图像与目标金字塔总的每幅图像进行鲁棒匹配,并把优质匹配项最多的那一层保留下来;如果这一层的匹配项小足够多,就可以认为已经找到目标。

    3、使用得到的单应矩阵和cv::getPerspectiveTransform函数,把目标中的四个角点重新投影到输入图像中。

    • 相机标定
    • 相机姿态还原
    • 用标定相机实现三维重建
    • 计算立体图像的深度

    相机标定

    相机标定的基本原理是,确定场景中一系列点的三维坐标并拍摄这个场景,然后观测这些点再图像上投影的位置。有了足够多的三维点和图像上对应的二维点,就可以根据投影方程推断出准确的相机参数。显然,为了得到精确的结果,就要观测尽可能多的点。【1】是对一个包含大量三维点的场景取像。但是在实际操作中,这种做法几乎是不可行的。【2】更实用的做法是从不同的视角为一些三维点拍摄多个照片。但是它除了需要计算相机本身的参数,还需要计算每个相机视图的位置。

     

     

     

     

     

     

     

     

    展开全文
  • OpenCV计算机视觉(Python实现)-3:人脸检测和识别

    千次阅读 热门讨论 2019-05-09 14:43:18
    5.3 使用OpenCV进行人脸检测 5.3.1 静态图像中的人脸检测 5.3.2 视频中的人脸检测 5.3.3 人脸识别 5.4 总结 类Haar特征:一种用于实现实时人脸跟踪的特征。每个类Haar特征都描述了相邻图像区域的对比模式。 ...
  • 推荐几本经典的计算机视觉OpenCV书籍

    万次阅读 多人点赞 2018-11-16 09:36:49
    全文摘要 机器学习涉及到的理论方法非常繁多,本文选自选出了非常具有代表性的15到机器学习面试真题,如果...作为一个基于开源发行的跨平台计算机视觉库,OpenCV实现了图像处理和计算机视觉方面的很多通用算法。《O...
  • Python+OpenCV计算机视觉

    万人学习 2018-12-28 17:42:46
    Python+OpenCV计算机视觉系统全面的介绍。
  • 【Halcon】Halcon与OpenCV介绍、比较

    万次阅读 2018-07-26 14:40:33
     MVTec HALCON 是世界上最全能的机器视觉软件.世界各地的用户从HALCON为快速开发图像分析和机器视觉程序的灵活架构获益匪浅.HALCON 提供了超过1100多种具备突出性能控制器的库,如模糊分析,形态,模式匹配,3D校正等....
  • openCV学习系列1:我为什么要学习openCV,什么是openCV

    万次阅读 多人点赞 2017-06-30 08:46:49
    个人学习openCV两个原因:1、看好计算机视觉技术未来广阔市场前景;2、为产品做升级引入新技术。 什么是openCV? 以下内容从各种百科中整理而得: OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以...
  • OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频培训课程概况:教程中会讲解到基于opencv视频和摄像机录制、播放和播放进度控制,多视频图像合并、多视频图像融合、剪切、视频亮度、对比度、尺寸(近邻插值(手动实现)...
  • python计算机视觉+opencv3编程入门+opencv计算机视觉编程攻略下载地址分享: https://download.csdn.net/download/xiaopc3357/10558180 三本高清书已经打包好!!! 1. python计算机视觉 2. opencv3编程入门 ...
  • opencv应用实例-实战视频教学

    千人学习 2018-12-17 17:54:06
    opencv应用实例视频教程,该课程主要分享在机器视觉及算法领域的具体应用案例,并且对这些案例的算法流程及相关接口进行详细说明与讲解,涉及到的opencv具体案例有鸟瞰图、肤色检测、颜色聚类、模板匹配、人眼检测、...
  • Python+OpenCV3.3图像处理视频教程

    千人学习 2018-10-22 21:38:11
    Python+OpenCV3.3图像处理视频培训课程:该教程基于Python3.6+OpenCV新版本3.3.0详细讲述Python OpenCV图像处理部分内容,包括opencv人脸识别、人脸检测、数字验证码识别等内容。是Python开发者学习图像知识与应用...
  • OpenCV3.2 Java图像处理视频学习教程

    千人学习 2019-06-25 11:28:34
    OpenCV3.2 Java图像处理视频培训课程:基于OpenCV新版本3.2.0详细讲述Java OpenCV图像处理部分内容,包括Mat对象使用、图像读写、 基于常用核心API讲述基本原理、使用方法、参数、代码演示、图像处理思路与流程讲授...
  • OpenCV(开源计算机视觉库)

    千次阅读 2018-09-13 17:33:08
    OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Android和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言...
  • 基于OpenCV计算机视觉技术实现(pdf电子书)

    千次下载 热门讨论 2020-07-28 23:33:29
    自己在网上找了遍也没有找到《基于OpenCV计算机视觉技术实现》这本书,找到的都是该书每章对应的程序实现代码,但是如果原理不知道的话,光看代码很难理解的。希望这本书给学习利用opencv研究机器视觉的朋友带来...
  • windows 下OpenCV的安装部署详细教程

    万次阅读 多人点赞 2020-04-07 20:55:01
    OpenCV的全称是Open Source Computer Vision Library,是一个跨平台的计算机视觉库。OpenCV是由英特尔公司发起并参与开发,以BSD许可证授权发行,可以在商业和研究领域中免费使用。OpenCV可用于开发实时的图像处理、...
  • 机器视觉、模式识别库

    万次阅读 2013-08-19 16:44:47
    开源生物特征识别库 OpenBR OpenBR 是一个用来从照片中识别人脸的工具。还支持推算性别与年龄... 使用方法:$ br -algorithm FaceRecognition -compare me.jpg you.jpg更多OpenBR信息 ...计算机视觉库 OpenC
  • OpenCV

    千次阅读 2014-09-16 15:03:06
    OpenCV(Open Source Computer Vision Library) 由公司在背后提供支持。它包含了超过500个函数来实现用于图形处理和计算机视觉方面的通用算法。
  • Opencv——基于Java环境搭建

    万次阅读 2019-07-03 18:36:34
    OpenCV(开源计算机视觉库)是一个开源的计算机视觉和机器学习软件库。 目前使用java库搭建环境进行一些图像处理,学习中,记录一些常用的处理方法 一、创建maven项目,引入依赖 <dependency> <...
1 2 3 4 5 ... 20
收藏数 28,323
精华内容 11,329
关键字:

opencv计算机视觉