百面算法工程师 | 传统图像处理——OpenCV

本文给大家带来的百面算法工程师是传统图像处理的面试总结,文章内总结了常见的提问问题,旨在为广大学子模拟出更贴合实际的面试问答场景。在这篇文章中,我们将总结一些几何变换和图像平滑处理,并提供参考的回答及其理论基础,以帮助求职者更好地准备面试。通过对这些问题的理解和回答,求职者可以展现出自己的传统图像处理领域的专业知识、解决问题的能力以及对实际应用场景的理解。同时,这也是为了帮助求职者更好地应对深度学习目标检测岗位的面试挑战,提升面试的成功率和竞争力

目录

13.1 HSV色彩空间

13.2 swish激活函数

13.3 OpenCV——几何变换

13.3.1 缩放

13.3.2 翻转

13.3.3 仿射

13.3.4 透视

13.4 图像平滑处理

13.4.1 均值滤波

13.4.2 方框滤波

13.4.3 高斯滤波

13.4.4 中值滤波

13.4.5 双边滤波

13.4.6 横向对比

13.5 图像梯度

13.5.1 Sobel 理论基础、函数使用

13.5.2 Scharr 算子及函数使用

13.5.3 Laplacian 算子及函数使用

13.5.4 Canny边缘检测

13.5.5 横向对比


 欢迎大家订阅我的专栏一起学习共同进步

祝大家早日拿到offer! let's go

🚀🚀🚀http://t.csdnimg.cn/dfcH3🚀🚀🚀

13.1 HSV色彩空间

RGB 是从硬件的角度提出的颜色模型,在与人眼匹配的过程中可能存在一定的差异,HSV 色彩空间是一种面向视觉感知的颜色模型。HSV 色彩空间从心理学和视觉的角度出发,指出人 眼的色彩知觉主要包含三要素:色调(Hue,也称为色相)、饱和度(Saturation)、亮度(Value, 色调指光的颜色,饱和度是指色彩的深浅程度,亮度指人眼感受到的光的明暗程度。

  • 色调:色调与混合光谱中的主要光波长相关,例如“赤橙黄绿青蓝紫”分别表示不同的 色调。如果从波长的角度考虑,不同波长的光表现为不同的颜色,实际上它们体现的是 色调的差异。
  • 饱和度:指相对纯净度,或一种颜色混合白光的数量。纯谱色是全饱和的,像深红色(红 加白)和淡紫色(紫加白)这样的彩色是欠饱和的,饱和度与所加白光的数量成反比。
  • 亮度:反映的是人眼感受到的光的明暗程度,该指标与物体的反射度有关。对于色彩来 讲,如果在其中掺入的白色越多,则其亮度越高;如果在其中掺入的黑色越多,则其亮 度越低。

Caution
 

在 OpenCV 内,我们使用 cv2.cvtColor()函数实现色彩空间的变换。该函数能够实现多个色 彩空间之间的转换。其语法格式为:

dst = cv2.cvtColor( src, code [, dstCn] )
  • dst 表示输出图像,与原始输入图像具有同样的数据类型和深度。
  • src 表示原始输入图像。可以是 8 位无符号图像、16 位无符号图像,或者单精度浮点数等。
  • code 是色彩空间转换码
  • dstCn 是目标图像的通道数。如果参数为默认的 0,则通道数自动通过原始输入图像和 code 得到。

当图像由 RGB 色彩空间转换到 GRAY 色彩空间时,其处理方式如下: Gray = 0.299 · 𝑅 + 0.587 · 𝐺 + 0.114 · B

13.2 swish激活函数

 Swish(x) = x*sigmoid(ßx)

其中,ß 为可学习参数。Swish可以比ReLU激活函数更好,因为它在0附近提供了更平滑的转换,这可以带来更好的优化。

13.3 OpenCV——几何变换
13.3.1 缩放
dst = cv2.resize( src, dsize[, fx[, fy[, interpolation]]] )
# interpolation 代表插值方式, 默认是双线性插值(cv2.INTER_LINEAR)
# 在 cv2.resize()函数中,目标图像的大小可以通过“参数 dsize”或者“参数 fx 和 fy”二者之一来指定

当缩小图像时,使用区域插值方式(INTER_AREA)能够得到最好的效果;当放大图像时, 使用三次样条插值(INTER_CUBIC)方式和双线性插值(INTER_LINEAR)方式都能够取得 较好的效果。三次样条插值方式速度较慢,双线性插值方式速度相对较快且效果并不逊色。

13.3.2 翻转
dst = cv2.flip( src, flipCode )
# flipCode 代表旋转类型。

filpCode
为0,绕着x轴旋转
为正,绕着y轴旋转
为负,绕着x,y轴同时翻转

13.3.3 仿射

仿射变换是指图像可以通过一系列的几何变换来实现平移、旋转等多种操作。该变换能够 保持图像的平直性和平行性。平直性是指图像经过仿射变换后,直线仍然是直线;平行性是指 图像在完成仿射变换后,平行线仍然是平行线。

OpenCV 中的仿射函数为 cv2.warpAffine(),其通过一个变换矩阵(映射矩阵)M 实现变换,具体为:

dst(𝑥, 𝑦) = src(𝑀11𝑥 + 𝑀12𝑦 + 𝑀13, 𝑀21𝑥 + 𝑀22𝑦 + 𝑀23)
dst = cv2.warpAffine( src, M, dsize[, flags[, borderMode[, borderValue]]])
# flags 代表插值方法,默认为 INTER_LINEAR。
# borderMode 代表边类型, 默认为 BORDER_CONSTANT。

在 OpenCV 中使用函数 cv2.warpAffine()实现仿射变换,忽略其可选参数后的语法格式为:
dst = cv2.warpAffine( src , M , dsize)
其通过转换矩阵 M 将原始图像 src 转换为目标图像 dst:

dst(𝑥, 𝑦) = src(𝑀11𝑥 + 𝑀12𝑦 + 𝑀13, 𝑀21𝑥 + 𝑀22𝑦 + 𝑀23)

​通过此函数可以实现平移旋转和更加复杂的仿射变换

13.3.4 透视

仿射变换可以将矩形映射为任意平行四边形,透视变换则可以将矩形映射为任意四边形。

透视变换通过函数 cv2.warpPerspective()实现,该函数的语法是:

dst = cv2.warpPerspective( src, M, dsize[, flags[, borderMode[, borderValue]]])

与仿射变换一样,同样可以使用一个函数来生成函数 cv2.warpPerspective()所使用的转换矩阵。该函数是 cv2.getPerspectiveTransform(),其语法格式为:

retval = cv2.getPerspectiveTransform(src, dst)
# src 代表输入图像的四个顶点的坐标。
# dst 代表输出图像的四个顶点的坐标。

Caution

src 参数和 dst 参数是包含四个点的数组,与仿射变换函数 cv2.getAffineTransform()中的三个点是不同的。

13.4 图像平滑处理
13.4.1 均值滤波

在进行均值滤波时,首先要考虑需要对周围多少个像素点取平均值。通常情况下,我们会 以当前像素点为中心,对行数和列数相等的一块区域内的所有像素点的像素值求平均。可以以当前像素点为中心,对周围 3×3 区域内所有像素点的像素值求平均,也可 以对周围 5×5 区域内所有像素点的像素值求平均。

Caution

针对边缘像素点,可以只取图像内存在的周围邻域点的像素值均值。如下图所示,计算 左上角的均值滤波结果时,仅取图中灰色背景的 3×3 邻域内的像素值的平均值。

除此以外,还可以扩展当前图像的周围像素点。例如,将当前 9×7 大小的图像扩展为 13×11 大小的图像

dst = cv2.blur( src, ksize, anchor, borderType )
# anchor 是锚点,其默认值是(-1, -1),表示当前计算均值的点位于核的中心点位置。该值使用默认值即可,在特殊情况下可以指定不同的点作为锚点。

卷积核越大,参与到均值运算中的像素就会越多,即当前点计算的是更多点的像素值的平 均值。因此,卷积核越大,去噪效果越好,当然花费的计算时间也会越长,同时让图像失真越 严重。在实际处理中,要在失真和去噪效果之间取得平衡,选取合适大小的卷积核。

13.4.2 方框滤波

与均值滤波的不同在于,方框滤波不会计算像素均值。 在均值滤波中,滤波结果的像素值是任意一个点的邻域平均值,等于各邻域像素值之和除以邻 域面积。而在方框滤波中,可以自由选择是否对均值滤波的结果进行归一化,即可以自由选择 滤波结果是邻域像素值之和的平均值,还是邻域像素值之和。

在进行方框滤波时,如果计算的是邻域像素值的均值

在进行方框滤波时,如果计算的是邻域像素值之和

dst = cv2.boxFilter( src, ddepth, ksize, anchor, normalize, borderType )

normalize 表示在滤波时是否进行归一化(这里指将计算结果规范化为当前像素值范围 内的值)处理,该参数是一个逻辑值,可能为真(值为 1)或假(值为 0)。 【normalize 为默认值 True】

  • 当参数 normalize=1 时,表示要进行归一化处理,要用邻域像素值的和除以面积。
  • 当参数 normalize=0 时,表示不需要进行归一化处理,直接使用邻域像素值的和。
13.4.3 高斯滤波

在进行均值滤波和方框滤波时,其邻域内每个像素的权重是相等的。在高斯滤波中,会将 中心点的权重值加大,远离中心点的权重值减小,在此基础上计算邻域内各个像素值不同权重的和。

实际使用时往往需要进行归一化。严格来讲,使用没有进行归一化 处理的卷积核进行滤波,得到的结果往往是错误的。

dst = cv2.GaussianBlur( src, ksize, sigmaX, sigmaY, borderType )
# sigmaX 是卷积核在水平方向上(X 轴方向)的标准差,其控制的是权重比例。
# sigmaY 是卷积核在垂直方向上(Y 轴方向)的标准差。如果将该值设置为 0,则只采用sigmaX 的值;如果 sigmaX 和 sigmaY 都是 0,则通过 ksize.width 和 ksize.height 计算得到。
13.4.4 中值滤波

中值滤波与前面介绍的滤波方式不同,不再采用加权求均值的方式计算滤波结果。它用邻 域内所有像素值的中间值来替代当前像素点的像素值。

中值滤波会取当前像素点及其周围临近像素点(一共有奇数个像素点)的像素值,将这些 像素值排序,然后将位于中间位置的像素值作为当前像素点的像素值。

dst = cv2.medianBlur( src, ksize)

在中值滤波处理中,噪声成分很难被选上,所以可以在几乎不影响原有图像的情 况下去除全部噪声。但是由于需要进行排序等操作,中值滤波需要的运算量较大。

Note

在均值滤波、方框滤波、高斯滤波中,都会计算边缘上各个像素点的加权平均值,从而模 糊边缘信息。

13.4.5 双边滤波

双边滤波是综合考虑空间信息和色彩信息的滤波方式,在滤波过程中能够有效地保护图像 内的边缘信息。双边滤波在计算某一个像素点的新值时,不仅考虑距离信息(距离越远,权重越小),还 考虑色彩信息(色彩差别越大,权重越小)。双边滤波综合考虑距离和色彩的权重结果,既能 够有效地去除噪声,又能够较好地保护边缘信息。

在双边滤波中,当处在边缘时,与当前点色彩相近的像素点(颜色距离很近)会被给予较 大的权重值;而与当前色彩差别较大的像素点(颜色距离很远)会被给予较小的权重值(极端 情况下权重可能为 0,直接忽略该点),这样就保护了边缘信息。

dst = cv2.bilateralFilter( src, d, sigmaColor, sigmaSpace, borderType )
13.4.6 横向对比

平滑处理

特点

优点

缺点

均值滤波

使用邻域内像素的平均值来替换中心像素的值。

实现简单,计算速度快。
可以有效地去除噪声。

对较大的噪声有限制,可能导致图像细节丢失。
 无法有效处理边缘部分。

方框滤波

使用邻域内像素的均值来替换中心像素的值,但是相对于均值滤波,方框滤波使用加权均值。

相比均值滤波,对噪声有更好的抑制效果。

对较大的噪声有限制,可能导致图像细节丢失。
无法有效处理边缘部分。

高斯滤波

使用高斯核对邻域内像素进行加权平均,使得距离中心像素越近的像素具有更高的权重。

能够平滑图像并保留边缘信息。
 对于不同尺度的噪声有较好的抑制效果。

计算量较大,对性能要求较高。

中值滤波

将邻域内像素的值按照大小排序,然后选择中间值来替换中心像素的值。

能够有效地去除椒盐噪声等类型的噪声。
不会模糊图像细节。

对于高斯噪声等连续性噪声的效果可能不如其他方法。
计算量较大。

双边滤波

结合了空间域和灰度值域的信息,保留了图像的边缘信息的同时平滑图像。

能够平滑图像并保留边缘信息。
对噪声有一定的抑制效果。

计算量大,对性能要求较高。
参数调节较为复杂。

13.5 图像梯度
13.5.1 Sobel 理论基础、函数使用

Sobel 算子是一种离散的微分算子,该算子结合了高斯平滑和微分求导运算。该算子利用局部差分寻找边缘,计算所得的是一个梯度的近似值。

dst = cv2.Sobel( src, ddepth, dx, dy[,ksize[, scale[, delta[, borderType]]]] )
  • dst 代表目标图像。
  • src 代表原始图像。
  • ddepth 代表输出图像的深度。
  • dx 代表 x 方向上的求导阶数。
  • dy 代表 y 方向上的求导阶数。
  • ksize 代表 Sobel 核的大小。该值为-1 时,则会使用 Scharr 算子进行运算。
  • scale 代表计算导数值时所采用的缩放因子,默认情况下该值是 1,是没有缩放的。
  • delta 代表加在目标图像 dst 上的值,该值是可选的,默认为 0。
  • borderType 代表边界样式。

13.5.2 Scharr 算子及函数使用

在离散的空间上,有很多方法可以用来计算近似导数,在使用 3×3 的 Sobel 算子时,可能 计算结果并不太精准。OpenCV 提供了 Scharr 算子,该算子具有和 Sobel 算子同样的速度,且 精度更高。可以将 Scharr 算子看作对 Sobel 算子的改进,其核通常为:

dst = cv2.Scharr( src, ddepth, dx, dy[, scale[, delta[, borderType]]] )
  • dst 代表输出图像。
  • src 代表原始图像。
  • ddepth 代表输出图像深度。该值与函数 cv2.Sobel()中的参数 ddepth 的含义相同。
  • dx 代表 x 方向上的导数阶数。
  • dy 代表 y 方向上的导数阶数。
  • scale 代表计算导数值时的缩放因子,该项是可选项,默认值是 1,表示没有缩放。
  • delta 代表加到目标图像上的亮度值,该项是可选项,默认值为 0。
  • borderType 代表边界样式。
13.5.3 Laplacian 算子及函数使用

Laplacian(拉普拉斯)算子是一种二阶导数算子,其具有旋转不变性,可以满足不同方向 的图像边缘锐化(边缘检测)的要求。通常情况下,其算子的系数之和需要为零。

Laplacian 算子类似二阶 Sobel 导数,需要计算两个方向的梯度值。

dst = cv2.Laplacian( src, ddepth[, ksize[, scale[, delta[, borderType]]]] )
  • dst 代表目标图像。
  • src 代表原始图像。
  • ddepth 代表目标图像的深度。
  • ksize 代表用于计算二阶导数的核尺寸大小。该值必须是正的奇数。
  • scale 代表计算 Laplacian 值的缩放比例因子,该参数是可选的。默认情况下,该值为 1, 表示不进行缩放。
  • delta 代表加到目标图像上的可选值,默认为 0。
  • borderType 代表边界样式。
13.5.4 Canny边缘检测

Canny 边缘检测是一种使用多级边缘检测算法检测边缘的方法。1986 年,John F. Canny 发 表了著名的论文 A Computational Approach to Edge Detection,在该论文中详述了如何进行边缘 检测。 OpenCV 提供了函数 cv2.Canny()实现 Canny 边缘检测。

左图显示的是三个边缘信息,右图是对边缘信息进行分类的示意图,具体 划分如下:

  • A 点的梯度值值大于 maxVal,因此 A 是强边缘。
  • B 和 C 点的梯度值介于 maxVal 和 minVal 之间,因此 B、C 是虚边缘。
  • D 点的梯度值小于 minVal,因此 D 被抑制(抛弃)。

edges = cv.Canny( image, threshold1, threshold2[, apertureSize[, L2gradient]])
13.5.5 横向对比

算子

类型

用途

特点

Sobel算子

梯度算子

边缘检测

1. Sobel算子是一种离散型梯度算子,通常用于边缘检测和图像处理中的梯度计算。它基于卷积操作,可以计算出图像中每个像素点的梯度值,从而识别出图像中的边缘。
2. Sobel算子有两个核(即卷积模板),分别用于检测水平和垂直方向上的边缘。
3. 由于Sobel算子基于简单的卷积运算,计算速度较快,适用于实时边缘检测任务。

Scharr算子

梯度算子

边缘检测

1. Scharr算子与Sobel算子类似,也是一种离散型梯度算子,用于边缘检测。它相对于Sobel算子而言更加敏感,对噪声有更好的抑制能力。
2. Scharr算子通常用于需要更精细的梯度近似和边缘检测的任务,对于噪声较少的图像效果更好。
3. 它使用了更大的权重,以获得更平滑的梯度近似,从而提高了对边缘的检测效果。

Laplacian算子

二阶导数算子

边缘检测
特征提取

1. Laplacian算子是一种二阶导数算子,用于在图像中检测边缘和提取特征。它通过计算图像中像素值的二阶导数来寻找像素值的突变点,从而定位边缘。
2. Laplacian算子的主要优点是可以检测边缘的位置和方向变化,因此对于具有复杂纹理的图像效果较好。
3. 由于它是二阶导数算子,因此对于噪声较敏感,通常需要在使用之前进行图像平滑处理。

Canny算子

多阶段算子

边缘检测

1. Canny边缘检测算法是一种经典的多阶段边缘检测算法,包括多个步骤:噪声抑制、边缘强度计算、边缘细化和边缘连接。
2. 在第一阶段,Canny算法通过高斯滤波器对图像进行平滑处理,以抑制噪声;接着使用Sobel算子计算图像的梯度强度和方向;然后使用非极大值抑制技术来细化边缘,保留局部梯度最大值点;最后通过阈值处理和边缘连接来检测图像中的边缘。
3. Canny算法具有高精度和低误报率的特点,可以根据具体应用调节参数以获得最佳效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/12380.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++自定义日期类的精彩之旅(详解)

在学习了C的6个默认成员函数后,我们现在动手实现一个完整的日期类,来加强对这6个默认成员函数的认识。 这是日期类中所包含的成员函数和成员变量: 构造函数 // 函数:获取某年某月的天数 inline int GetMonthDay(int yea…

常见磁盘分区问题

给磁盘分区有几个主要的原因: 组织和管理数据:分区可以帮助用户更好地组织和管理数据。例如,你可以在一个分区上安装操作系统,而在另一个分区上存储个人文件。这样,即使操作系统崩溃或需要重新安装,你的个…

Docker 使用 Fedora 镜像

Fedora 在 Docker 中的使用也非常简单,直接使用命令 docker run -it fedora:latest bash 就可以 pull 到本地的容器中并且运行。 C:\Users\yhu>docker run -it fedora:latest bash Unable to find image fedora:latest locally latest: Pulling from library/fed…

【瑞萨RA6M3】2. UART 实验

https://blog.csdn.net/qq_35181236/article/details/132789258 使用 uart9 配置 打印 void hal_entry(void) {/* TODO: add your own code here */fsp_err_t err;uint8_t c;/* 配置串口 */err g_uart9.p_api->open(g_uart9.p_ctrl, g_uart9.p_cfg);while (1){g_uart9.…

mysql的隔离性——MVCC

MVCC通过undolog版本链和readview来实现 更新和删除时会写入undolog中。 读已提交:在事务任意读时创建readview,读最新提交的事务 可重复读:在事务第一次读时创建readview

使用Caché管理工具

Cach通过一个web工具来对其进行系统管理和完成管理任务,该方法的一个好处是不必将Cach安装到用于管理的系统上。目前,通过网络远程管理和控制对站点的访问,这些都比较容易。因为数据及其格式信息都直接来自被管理的系统,因此,这也可以最小化跨版本的兼容问题。 本文将描述…

Kubernetes二进制(单master)部署

文章目录 Kubernetes二进制(单master)部署一、常见的K8S部署方式1. Minikube2. Kubeadmin3. 二进制安装部署4. 小结 二、K8S单(Master)节点二进制部署1. 环境准备1.1 服务器配置1.2 关闭防火墙1.3 修改主机名1.4 关闭swap1.5 在/e…

(done) 关于 pytorch 代码里常出现的 batch_first 到底是啥?

参考文章:https://pytorch.org/docs/stable/generated/torch.nn.utils.rnn.pad_sequence.html 首先看参考文章里的解释,如下图 从文章描述来看,当 batch_first True 时,输出的张量的 size 是 B x T x *。当 batch_first False…

umi搭建react项目

UMI 是一个基于 React 的可扩展企业级前端应用框架,提供路由、状态管理、构建和部署等功能,可以帮助开发者快速构建复杂的单页面应用(SPA)和多页面应用(MPA)。它与 React 的关系是,UMI 构建在 R…

移动端自动化测试工具 Appium 之 main 启动

文章目录 一、背景二、生成xml文件2.1、创建xml方法2.2、执行主类MainTest2.3、自动生成的xml2.4、工程目录2.5、执行结果 三、命令行执行appium服务四、主方法启动类五、集成Jenkins六、总结 一、背景 Jenkins 做集成测试是不错的工具,那么UI自动化是否可以&#…

图解自动驾驶中的运动规划(Motion Planning),附几十种规划算法

目录 1 自动驾驶驶向何处?2 什么是运动规划?3 运动规划实战教程4 加入我们5 订阅需知 1 自动驾驶驶向何处? 自动驾驶,又称无人驾驶,是依靠计算机与人工智能技术在没有人为操纵的情况下,完成完整、安全、有效…

2.1.2 事件驱动reactor的原理与实现

LINUX 精通 2 day14 20240513 day15 20240514 算法刷题:2维前缀和,一二维差分 耗时 135min 习题课 4h 课程补20240425 耗时:4h 课程链接地址 回顾 怎么学0voice课网络io——一请求一线程,一个client一个连接再accpet分配io f…

linux系统修改网卡名称

说明: 因操作过程需要停用网卡,导致ssh远程连接不上,需要控制台登录操作。 测试环境: CentOS7.9、8.2虚拟机 Suse15 SP4虚拟机 操作步骤: 方法一: 1、 查看网卡当前名称及状态 ip a2、 将网卡状态从启用…

RK3566(泰山派):GP7101背光驱动

RK3566(泰山派):GP7101背光驱动 文章目录 RK3566(泰山派):GP7101背光驱动GP7101背光驱动电路配置i2c1设备树创建驱动编写Makefilegp7101_bl.c驱动触摸I2C驱动框架。驱动中的结构体probe函数devm_backlight_…

过滤器Filter和拦截器Interceptor实现登录校验

一.过滤器 Filter过滤器可以把对资源的请求拦截下来,从而实现一些登录验证的功能 1.Filter的快速入门 1.定义Filter:定义一个类,实现Filter接口,并重写其所有方法。2.配置 public class dofilter implements Filter {Override //初始化只…

【JAVA】数组的定义与使用

前一篇我们讲述了方法的使用和递归,这一讲 我们来叙述一下数组相关知识点。最近更新较快,大家紧跟步伐哦~~ 1. 数组的基本概念 1.1 为什么要使用数组 假设现在要存5个学生的javaSE考试成绩,并对其进行输出,按照之前掌握的知识点&…

obsidian 外观设置解毒

前言 一入obsidian深似海,外观设置也是五花八门,仿佛回到读书时期折腾桌面一样。 我对比了AnuPpuccin、minimal和其他的一些外观主题,设置都太复杂了,尤其是需要调整CSS文件,最后发现一款,非常好用&#…

数据传送指令

数据传送:负责把数据、地址或立即数传送到寄存器或存储单元中 数据传送指令可以分为四个类型:通用数据传送指令、地址传送指令、标志位传送指令、专用累加器传送指令(输入、输出指令) 一、通用数据传送指令 (一)传送字或字节指…

前端框架-echarts

Echarts 项目中要使用到echarts框架&#xff0c;从零开始在csdn上记笔记。 这是一个基础的代码&#xff0c;小白入门看一下 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" co…