当前位置: 首页 > news >正文

计算机是如何看到图像的

本节课为「计算机视觉 CV 核心知识」第 4 节;

「AI秘籍」系列课程:

  • 人工智能应用数学基础
  • 人工智能Python基础
  • 人工智能基础核心知识
  • 人工智能BI核心知识
  • 人工智能CV核心知识

在这里插入图片描述

本文涉及代码:https://github.com/hivandu/AI_Cheats

Hi, 大家好。我是茶桁。

今天我们来讨论一个问题:计算机是如何看到图像的

那计算机到底是如何看到图像的呢?我们一点点来看:

首先是照相机内,又或者是我们手机上的摄像头。咱们的手机本身也是有 CPU 的对吧?除此之外,其实手机内还有感光器件和感光传感器的,这个感光器又叫 CMOS, 亦或是 CCD。这两者其实也是有区别的,CMOS 全称为 Complementary Metal Oxide Semiconductor, 是互补氧化物半导体,而 CCD 是 Charge-Coupled Device,含义是电荷耦合器件。

那么感光器是如何感光的呢?实际上它的表面类似于一个矩形,当然圆形的也有,一般是矩形的,圆形的很少了。

一个矩形的感光器件,当光线打入,照射到这个感光器件上,后台就会产生一个电流或者电压,光线越强电压越强,光线越暗电压越小。但其实 CCD 和 CMOS 在这方面也是有一些区别的,像元信号 CCD 是电荷,CMOS 是电压。我们就都先按电压来理解,反正就是照射出来的光线的强度决定了感光器件每个位置的电压高低。换句话说,电压的高低直接就显示出来当前射进来光线的强弱了。

那我们知道一张图像就是由光线的强弱组成的,每个图像本身一些位置光线强,一些位置光线弱。图像由明暗组成就形成了纹理,这个纹理就是黑白图像。所以我们感光元器件通过这种方式,就可以把纹理测出来了,通过用电压的方式表示出来。电压要存储到后面的存储器上存储起来。曝光时间结束的时候,来自传感单元的电荷同时传输到所有像素的中间存储器,并通过垂直和水平位移从那里读出。

在这里插入图片描述

那么你看,因为它每个位置都存储有一个值,要存储的值也就非常多。到底有多少个值,要涉及到多少个位置。那么多少个位置呢,因为光线有无数,感光器的最小感光单元也是有无数条光线的,这是一个区域内的光线。我们这个感光元器件只能看它的分辨率是多少,就是它的最小的感光元器件多少,它测的是最小的感光元器件的区域所有的光线之和的强度。

也就是说这个感光元件上有多少个最小单位的感光单元,排列成 N 行 M 列的这种形式,最后存储的数字就是 N 行 M 列。把光线的明暗存储成数字来表达,最后存储的数字就是一个图片了,就用矩阵来表示图片。

另外,射入这个感光元件的每个感光单元的光线一定是垂直的。在现实中,各种方向都有可能让这个感光元件发生反应。因为垂直的我们好衡量,所以就有了透镜。透镜的作用是什么,最后你都是这个方向来,朝这个方向走,加这样的一个镜片。就这是摄像头内部的一个结构,我们大概理解一下是如何成像的。

之前讲过,感光器这个矩阵后面每个位置都有个值,这个值并不是直接成像的。这个值有高有低,它是电压值的范围。比如说零点几伏,或者是零点几微伏。计算机视觉这个程序中处理的图像,我们看到这个图像的取值都是 0-255 这样的数字,实际上这就是电压的值。后面还是要经过一些处理,做一下归一化,把它归一化到 0-1 范围内然后输出出来,保存成图片。

这个归一化后输出的范围是个矩阵,再保存为图片,比如说保存成 jpg、png 等各种格式。而大部分图片格式几乎都是用了一种压缩办法,jpg 就是其中最流行的,压缩之后将图片保存下来。

这个原因是因为图片如果直接保存的话矩阵量非常大,比如说 600*800 的图片,48万的数字。48万的数字保存的 txt 文件那是非常大的,所以才需要压缩一下,一是为了压缩后便于保存,占用更小的磁盘或者硬盘空间,也是为了便于传输。特别是互联网最初拨号上网的年代。在以前,磁盘和带宽都是非常费钱的。

当然,现阶段咱们手机流量已经很便宜了,手机的内存空间也再渐渐增加。还记得刚拿到第一款 Android 手机——HTC G1, 那会儿我尝试着跑了一下 Google Earth,前后没 1 分钟,就跑了 10 多块钱,而且那龟速的移动网络,地图都还没完全打开。说回正题,由于存储以及传输的需求,我们就用压缩办法把这个矩阵存储下来。

总结一下,图片从摄像头采集成电压的值,然后进行传输和存储,再进行归一化,然后再用压缩办法把它压缩成图片。关于图片格式,目前不进行图片压缩进行存储的格式有 BMP 和 TIF,其中 TIF 和 Photoshop 的源文件有点类似,包含了分层信息以及透明通道信息。这就是图片为什么存储矩阵,以及大体的流程。

那么存完之后如何显示呢?我们用显像管或者显示屏显示。现在图片存储为了一个矩阵,显示的时候怎么办呢?

我们来想象有一盏灯,现在有三个电压经过它,电压为 1 的时候,这个灯就是最亮的,电压为 0 的时候,这盏灯就是暗的,如果是 0.5 呢?那这盏灯就没那么亮,也就是给它的电压小一些,灯就稍微暗一些,不那么亮,也不那么暗。

在这个显示屏上有无数的灯,每个灯其实都对应的有它的一个像素值,就是它亮度值对应它灯的亮度值。所有灯排列起来就是一幅图像。不过这个显示出来之后是一个黑白的图像,如果我们最后存到的值只有 0,1 这两个值,这时候的图片我们叫它二值图,它显示出来就是非黑即白的这种图片。

data = [[0, 1, 1, 0, 1, 0], [1, 0, 1, 1, 0, 0], [0, 0, 0, 1, 0, 1], [0, 0, 0, 1, 0, 1], [1, 0, 1, 1, 0, 0], [0, 1, 1, 0, 1, 0]]plt.imshow(data, cmap='Greys_r')
plt.show()

20240701235840

如果我们的值是在 0 ~ 255 范围内,或者在 0~1 之间有浮点值,我们称呼其为灰度图。这个和二值图的区别在于,灰度信息就不只是 2 个值了,而是灰度级更多了。

data2 = [[0, 1, 0.5, 0.5, 1, 0], [1, 0, 1, 1, 0.5, 0], [0, 0, 0.5, 1, 0.5, 1], [0, 0.5, 0, 1, 0.5, 1], [1, 0, 0.5, 1, 0.5, 0.5], [0, 1, 0.5, 0.5, 1, 0]]
plt.imshow(data2,  cmap='Greys_r')
plt.show()

20240702000409

注意到「灰度级」这个概念。二值图中,灰度级就只有两个,一个 0,一个 1,中间没有任何值。而如果是 0~255,或者是 0~1 之间加上浮点值,那么这个灰度级就多了,也就更细腻一点了。我可以表示黑,也可以表示白,也可以表示中间的灰度。 0~1 中间的灰度级就是无数个。

这里补充一下,0~1 表示图像和 0~255 表示图像,这两种方法在我们是初学者的时候,显示图像通常会遇到一些问题。如果一个图像矩阵的取值范围是 0~255,那么它就必须都是一个整数值,如果出现浮点数值,比如说 1.025, 255.0 这种,它是一个 float 型的。那这个时候直接去显示这样一个矩阵,你就会发现很多程序内图片会是全白的,当然并不是所有的程序中都会如此。这是为什么呢?是因为我们显示软件中如果是 float 型的,就默认你的取值范围是 0~1, 如果你是 0 的话就是黑色,如果是 1 的话就是白色。

部分程序中,就自动做一次归一化,就是将 1.0 ~ 255.0 的数值归一到 0~1 的范围之内,然后再做显示。

data4 = [[0.0, 255.0, 255.0, 255.0, 255.0, 255.0], [255.0, 255.0, 255.0, 255.0, 255.0, 255.0], [255.0, 255.0, 255.0, 255.0, 255.0, 255.0], [255.0, 255.0, 255.0, 255.0, 255.0, 255.0], [255.0, 255.0, 255.0, 255.0, 255.0, 255.0], [255.0, 255.0, 255.0, 255.0, 255.0, 125.0]]
plt.imshow(data4,  cmap='Greys_r')
plt.show()

20240702111930

如果矩阵中的数据是 0.0~255.0 的浮点数从而导致了图片显示不正常,就需要先将所有数值都改为整数型,这样正常的灰度级就有了。所以如果你拿到一张图片,处理完之后显示出来发现不正常,要么是全黑,要么是全白,这时候你就需要去看一下你的数据类型对不对了。

接下来咱们一起看一个视频,这个视频充分说明了像素矩阵来表示图像这个例子:

视频中可以看到,每个同学其实就代表矩阵中的一个元素,这个元素是包含了两种颜色,也是二值图。白和红这两色,其实就对应咱们的 1 和 0。这个以方阵的形式表示矩阵,以每个同学作为一个像素来表示图像的方法就体现出来了。现在我问你,如果让你去当一个教官,去设计这样的一个方阵表演,你会怎么去协调每个同学呢?

实际上每个同学,不管是喊口令,数拍子,其实就只需要知道自己当前是举 0 还是举 1 就行了,对吧?也就是说,这个看着好像蛮复杂的,实际上这个表演起来如果是学计算机的人去设计,就不那么困难了。比如咱们左下角的同学,他基本上就是举白的,也就是大部分时间内就举个0就行了。

中间有些同学可能一会要举 0,一会要举 1,但基本上每个同学的工作量其实并不大。就告诉你,你数到几的时候举什么牌子就行了。

大概看一下,这个其实看起来比较抓眼球,我们知道它是用矩阵表示图像就行了。

OK,我们刚才看这个图片呢,介绍了二值图像,黑白图像,因为只有两个值。也介绍了灰度图,一共包含了 256 个灰度级。

现在,我来问你们一个问题:彩色图像如何表示

彩色图像其实跟我们二值图像的显示方法是一样的,显像管和显示屏来显示,显然我们一个采样图像最终是要通过显像管和显示屏来显示出来的。那么通过显示屏来显示的时候,显示屏中每个位置还是有灯对吧?那现在咱们的电脑屏幕不像刚才是一样是灰度图了,而是彩色的,那么彩色显示它是什么原理呢?

彩色显示是因为每个像素内我有三个值,蓝灯、绿灯、红灯。这三个值就是我们所熟知的RGB,即是三原色,那这三个值就可以合成任意颜色。也就是说,你想让这个像素显示成任意颜色都可以,这个像素发出来的光是任意颜色的光。只需要调整红,绿,蓝之间的比值就可以了。

在这里插入图片描述

所以我们在每一个像素的位置都放三个灯,然后调整亮度值就可以了,这样我们就可以用来显示这个彩色图像了。那显示这个彩色图像的时候我们是需要数据来支撑的,就是说这个位置这个蓝色的灯到底要亮多少,这个红色灯要亮多少,绿色灯到底它亮度给多少。这些呢,都是有值的。

所以说每个像素的位置我们有3个值,R 值是多少,G 值是多少,B 值是多少。我们如果这样一个位置三个值写一行的话,比如这是 m 乘 n 的图像,那它就是 3 ∗ m ∗ n 3 * m * n 3mn 这样的一个矩阵了。但这种表示方式其实不是那么直观,更直观的方式是什么?我们把所有位置的 R 作为一个 m * n 的矩阵,然后 G 也如此,B 也如此,于是就有了如下三个矩阵:

在这里插入图片描述

因为彩色图像显示每个位置要用3个灯来显示,所以彩色图像的表示就用 3 个矩阵来表示,这样就表示了彩色的图片了。

那为什么一定是红、绿、蓝作为三原色而不是其他的呢?原因是因为咱们现实世界中生产红、绿、蓝色的灯相对容易些,造价也更低一些,技术相对更成熟一些,生产其他颜色的灯技术不成熟。就这么个原因。这就是为什么我们现实中常常说RGB的原因。

OK,这是彩色图像。到现在为止,我们图像的显示方式讲了几种:一个是二值图,这个图的取值方式是 0 和 1,数据类型是整型,然后是灰度图,每个像素的取值范围是 0~1 的浮点数,或者是 0~255 的整型。其实这两种统称为灰度图,可以表示同样的内容。但是你要注意它的数据类型,显示不正常的时候就看一下数据类型对不对。

接着是第三种,我们讲了彩色图形的显示,表示方式为 RGB。原来表示灰度图、二值图用一个矩阵就行了,现在表示彩色图我就需要三个矩阵了。因为什么?我要三原色合成任意颜色。

除此之外,我们还讲了为什么图像要用矩阵的表示方式。理解了图像的矩阵表示方式之后,接着我们来整理一下计算机视觉整体的一个流程和表现。

在这里插入图片描述

首先,镜头完成一个图像的采集,采集的是电压的值。这个电压的值我们需要对其进行归一化,得到一个数字的矩阵。然后我们将这个矩阵进行存储、修改并显示。当然这样一个数字化的矩阵我们还可以进行 copy,转移。其实老式相机也几乎是这样一个过程,可以这样去理解,只是我们现在是将这个矩阵存储到存储设备上,而老式相机是将其存储到了底片上。

因为是这样一个过程,采集完成之后是用矩阵来显示的,所以我们最后对图像进行处理其实就是对这个矩阵进行处理了。所以,我们可以得出一个结论:计算机视觉它主要是处理矩阵的。因为处理图像就是处理矩阵,计算机视觉可以视为就是在处理图像,所以计算机视觉本质上就是在处理矩阵。

现在我们来看,咱们之前说的所有的内容:人脸检测、人脸识别、美颜功能,手势检测、换脸换妆、自动驾驶、活体检测等等这些应用,它其实都是在对矩阵进行处理。那我们以后要讲的计算视觉的各种模型,它的分类模型、检测模型、分割模型、跟踪模型等等,还有一些其他的模型,这些也都是对矩阵的处理。

好,到此,今天我们所要讨论的,计算机视觉到底是如何看到并显示图像的我们也就有了一个清晰的概念。下一节课,咱们来讲讲计算机处理图像的方式和方法。

好,下节课再见,大家拜拜。

相关文章:

  • 泰雷茲具有首个通过FIPS 140-3 三级认证的HSMs
  • 引领AI新时代:深度学习与大模型的关键技术
  • 探索大型语言模型自动评估 LLM 输出长句准确性的方法
  • Python面试宝典第4题:环形链表
  • 3099. 哈沙德数 Easy
  • 广州外贸建站模板
  • 中控室监控台在水处理行业的作用
  • C++实现简化版Qt的QObject(5):通过IEventLoopHost扩展实现win32消息循环
  • 视频字幕提取在线工具有哪些?总结5个字幕提取工具
  • three.js地理坐标系有哪些,和屏幕坐标系的转换。
  • layui+jsp项目中实现table单元格嵌入下拉选择框功能,下拉选择框可手动输入内容或选择默认值,修改后数据正常回显。
  • Emp.dll文件丢失?理解Emp.dll重要性与处理常见问题
  • 【NodeJs】入门
  • VuePress 的更多配置
  • 用C语言声明汇编编写的函数,是否需要带参数列表?
  • [iOS]Core Data浅析一 -- 启用Core Data
  • bootstrap创建登录注册页面
  • canvas 五子棋游戏
  • exif信息对照
  • Hibernate【inverse和cascade属性】知识要点
  • JAVA多线程机制解析-volatilesynchronized
  • nodejs:开发并发布一个nodejs包
  • Object.assign方法不能实现深复制
  • Sass Day-01
  • tab.js分享及浏览器兼容性问题汇总
  • Vue UI框架库开发介绍
  • 对象管理器(defineProperty)学习笔记
  • ------- 计算机网络基础
  • 驱动程序原理
  • 容器服务kubernetes弹性伸缩高级用法
  • 如何设计一个比特币钱包服务
  • 山寨一个 Promise
  • 使用docker-compose进行多节点部署
  • 数组大概知多少
  • 学习HTTP相关知识笔记
  • 一天一个设计模式之JS实现——适配器模式
  • 用jQuery怎么做到前后端分离
  • No resource identifier found for attribute,RxJava之zip操作符
  • 阿里云ACE认证之理解CDN技术
  • 教程:使用iPhone相机和openCV来完成3D重建(第一部分) ...
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • ​软考-高级-信息系统项目管理师教程 第四版【第23章-组织通用管理-思维导图】​
  • ​一些不规范的GTID使用场景
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • # 详解 JS 中的事件循环、宏/微任务、Primise对象、定时器函数,以及其在工作中的应用和注意事项
  • #中国IT界的第一本漂流日记 传递IT正能量# 【分享得“IT漂友”勋章】
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (搬运以学习)flask 上下文的实现
  • (超简单)使用vuepress搭建自己的博客并部署到github pages上
  • (第二周)效能测试
  • (翻译)Entity Framework技巧系列之七 - Tip 26 – 28
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (篇九)MySQL常用内置函数
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • .Net Core和.Net Standard直观理解