
opencv
文章平均质量分 52
xddwz
这个作者很懒,什么都没留下…
展开
-
OpenCV之Shi-Tomasi角点检测
除了利用Harris进行角点检测外,还可以利用Shi-Tomasi方法进行角点检测。Shi-Tomasi算法是Harris算法的改进。OpenCV实现此算法的函数名为goodFeaturesToTrack,这是因为概算子是1994年在文章《Good Features to Track》中被提出的。确定图像强角点:goodFeaturesToTrack()函数goodFeaturesToTrack()函数结合了Shi-Tomasi算子,用于确定图像的强角点,函数原型:void goodFeatu原创 2021-02-01 20:16:55 · 473 阅读 · 0 评论 -
OpenCV之Harris角点检测
角点检测在图像处理领域,角点检测算法主要有以下几类:基于灰度图像的角点检测 基于二值图像的角点检测 基于轮廓曲线的角点检测而基于灰度图像的角点检测又可分为基于梯度、基于模板、基于梯度和基于模板组合三类方法。其中基于模板的方法主要考虑像素邻域点的灰度变化,即图像亮度的变化,将与相邻点亮度对比足够大的点定义为角点。常见的基于模板的角点检测算法有Kitchen-Rosenfeld角点检测算法,harris角点检测算法,KLT角点检测算法以及SUSAN角点检测算法。harris角点检测算法原创 2021-01-29 17:40:23 · 456 阅读 · 0 评论 -
openCV之什么是角点
角点检测(Corner Detection)是计算机视觉中用来获得图像特征的一种方法,广泛应用于运动检测、图像匹配、视频跟踪、三维建模和目标识别等领域,也称为特征点检测。角点通常被定义为两条边的交点,更严格的说法是,角点的局部邻域应该具有两个不同区域的不同方向的边界。在实际的应用中,大多数角点检测方法检测的是拥有特定特征的图像点,而不仅仅是“角点”。这些特征点在图像中有具体的坐标,并具有某些数学特征,如局部最大或最小灰度、某些梯度特征等。在图像处理与计算机视觉中,兴趣点(interest point原创 2021-01-29 16:16:59 · 2526 阅读 · 0 评论 -
OpenCV之模板匹配
模板匹配的概念与原理模板匹配是在一幅图像中寻找与另一幅模板图像最匹配(相似)部分的技术,在OpenCV中,模板匹配由函数MatchTemplate()函数实现。需要注意的是,模板匹配不是基于直方图的,而是通过在输入图像上滑动图像块,对实际的图像块和输入图像进行匹配的一种方法。如图,通过一个人脸图像模板,在整个输入图像上移动这张脸,寻找和这张脸相似的最优匹配。MatchTemplate()函数MatchTemplate()用于匹配出和模板重叠的图像区域,函数原型:void Match原创 2021-01-29 15:15:15 · 2240 阅读 · 0 评论 -
OpenCV之反向投影
如果一幅图像的区域中显示的是一种结构纹理或者一个独特的物体,那么这个区域的直方图可以看作一个概率函数,其表现形式是某个像素数语该纹理或者物体的概率。而反向投影就是一种记录给定图像中的像素点如何适应直方图模型像素分布的一种方法。简单来讲,所谓的反向投影,就是首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在该特征的方法。反向投影的工作原理这里通过H-S肤色直方图来解释反向投影的工作原理。首先通过前面讲到的求出下面图像的H-S肤色直方图。而我们要做的,就是使用模型直方图(代表手.原创 2021-01-28 16:11:27 · 1421 阅读 · 0 评论 -
OpenCV之直方图对比
得到图像的直方图之后,通常要用某些具体的标准来比较两个直方图的相似度。要对两个直方图进行比较,首先必须选择一个衡量直方图相似度的对比标准。OpenCV中用compareHsit()函数来比较两个直方图的相似度。对比直方图:compareHsit()函数compareHsit()函数用于对两幅直方图进行比较,有两个版本,函数原型:double compareHist(InputArray H1, InputArray H2, int mathod)double compareHist(con原创 2021-01-20 16:42:14 · 593 阅读 · 0 评论 -
OpenCV之直方图的计算与绘制
图像直方图直方图广泛的应用于很多计算机视觉应用中,通过标记帧与帧之间显著的边缘和颜色的通解变化,来检测视频场景中的变化。在每个兴趣点设置一个有相近特征的直方图所构成“标签”,用以确定图像中的兴趣点。边缘、色彩、角度等直方图构成了可以被传递给目标识别分类器的通用特征类型。色彩和边缘的直方图序列还可以用来识别网络视频是否被复制。简单来说,直方图就是对数据进行统计的一种方法,并且将统计直组织到一系列事先定义好的bin中。bin为直方图中经常到的概念,可以理解为“直条”或“组距”,其数值是从数据中计算出的特原创 2021-01-20 15:11:17 · 314 阅读 · 0 评论 -
OpenCV之分水岭算法
分水岭算法在许多实际的应用中,我们需要分割图像,但是无法从背景图像中获得有用信息。但是分水岭算法在这方面往往非常有效,它可以将图像中的边缘转化为“山脉”,将均匀区域转化为“山谷”,这样有助于分割目标。分水岭算法是一种记忆拓扑理论的数学形态学的分割方法,其基本思想是把图像看作测地学上的拓扑地貌,图像中每点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的计算过程是一个迭代标注过程。分水岭比较经典的计算方法主要分两个步骤:一个是排序过程,一个原创 2021-01-13 17:23:52 · 664 阅读 · 0 评论 -
OpenCV之使用多边形将轮廓包围
在实际的应用中,常常需要将检测到的轮廓用多边形来表示,主要涉及到以下函数。1、返回外部矩形边界:boundingRect()函数此函数计算并返回指定点集最外面的矩形边界。Rect boundingRect(InptArray points)函数的输入是二维的点集,可以是std::vector或Mat类型。2、寻找最小包围矩形:minAreaRect()函数此函数对于给定的2D点集,寻找可旋转的最小面积的包围矩形。RotatedRect minAreaRect(InputArr原创 2021-01-07 15:06:27 · 630 阅读 · 0 评论 -
OpenCV之寻找凸包
凸包是计算机几何中常见的概念。简单来说,给定二维平面上的点集,凸包就是将最外层的点连接起来构成凸多边形,它是能包含点集中所有点的。理解物体形状或轮廓的一种比较有用的方法是计算一个物体的凸包,然后计算其凸缺陷,很多复杂物体的特性能很好的被这种缺陷表现出来。convexHull()函数OpenCV中用convexHull()函数寻找图像点集中的凸包,函数原型:void convexHull(InputArray points, OutputArray hull, bool clockwise=fa原创 2021-01-07 14:40:18 · 550 阅读 · 0 评论 -
OpenCV之查找并绘制轮廓
在OpenCV中,用findContours()函数从二值图中查找轮廓。原型:void findContourd(InputArray image, OutputArray contours, outputArray hierarchy, int mode, int mrthod, Point offset=Point())参数详解:第一个参数:输入图像,Mat类的对象即可,必须是8位单通道图像。图像的非零像素视为1,0像素值被保留为0,所以图像是二进制。我们可以使用compare()、i原创 2021-01-05 10:31:58 · 479 阅读 · 0 评论 -
OpenCV之图像均衡化
直方图均衡化是灰度图像变换的一个重要应用,它简单高效且易于实现,广泛的应用于图像增强中。图像的像素灰度变化是随机的,直方图的图像高低不齐,直方图均衡化就是使用一定的算法使直方图大值平和的方法。简单来说,直方图均衡化就是通过拉伸像素强度分布范围来增强图像对比度的一种方法。均衡化处理后的图像近似均匀分布。均衡化图像的动态范围扩大了,但其本质是扩大了量化间隔,而量化级别反而减少了,因此原来灰度不同的像素经过处理后可能变的像素,形成了一片相同的灰度区域,各区域之间有明显的边界,从而出现了伪轮廓。在原始图原创 2020-12-31 19:16:22 · 2031 阅读 · 1 评论 -
OpenCV之仿射变换
仿射变换仿射变换,是指在几何中,一个向量空间进行一次线型变换并接上一个平移,变换为另一个向量空间的过程。它保持了二维图形的“平直性”(直线经过变换之后仍然是直线)和“平行性”(直线经过变换之后仍然是直线,且直线上点的相对位置不变)。一个任意的仿射变换都能表示为乘以一个矩阵(线型变换)然后再加上一个矩阵(平移)的形式。仿射变换变换主要有三种:旋转 平移 缩放仿射变换表示的是两幅图像之间的一种映射关系,通常使用2*3的矩阵来表示仿射变换。假如使用矩阵A和B对二维向量X做进行变换,可原创 2020-12-31 15:16:12 · 942 阅读 · 0 评论 -
OpenCV之重映射
重映射,就是吧一幅图像中某位置的像素放置到另一个图像指定位置的过程。为了完成重映射,需要获得一些差值为非整数像素的坐标,因为源图像与目标图像的像素坐标不是一一对应的。一般情况下,我们通过重映射来表达每个像素的位置(x, y),例如:g(x, y) = f(h(x, y))这里的g()是目标图像,f()是源图像,h(x, y)是作用于(x, y)的映射方法函数。例如,对于图像I,按照下面的条件进行重映射:h(x, y) = (I.cols-x, y)图像会按照x轴方向发生翻转。OpenCV原创 2020-12-28 17:00:50 · 322 阅读 · 0 评论 -
OpenCV之Scharr()函数
scharr滤波器主要是配合sobel算子运算的,分别计算x方向或y方向的图像差分,其参数与sobel基本一致。函数原型:void Scharr(InputArray src, OutputArray dst, int ddepth, int dx, int dy, double scale=1, double delta=0, intborderType=BORDER_DEFAULT)参数详解:第一个参数:InoutArray类型的src,属兔图像,Mat类型的即可。 第二个参数:目标原创 2020-12-25 14:24:34 · 1974 阅读 · 0 评论 -
OpenCV之拉普拉斯算子:Laplacian()函数
根据图像处理的原理,二阶导数可以用来进行边缘检测,因为图像是二维的,需要在两个方向上求导,使用Laplacian算子将会使求导过程变得简单。Laplacian算子的定义:需要说明的是,由于Laplacian算子使用了图像梯度,它内部的代码其实是调用了Sobel算子的。让一幅图像减去它的Laplacian算子可以增强它的对比度。Laplacian()函数函数原型:void Laplacian(InputArray src, outputArray dst, int ddepth,原创 2020-12-24 11:21:50 · 12650 阅读 · 3 评论 -
OpenCV之Sobel算子(C++实现)
sobel算子的基本概念Sobel算子是一个主要用于边缘检测的离散微分算子,它结合了高斯平滑和微分求导,用来计算图像灰度函数的近似梯度。在图像的任何一点使用此算子,都会得倒对应的梯度矢量或其法向量。sobel算子的计算过程假设图像为I,分别在x和y方向上计算梯度,其实就是用卷积核在两个方向上与图像进行卷积,以大小为3的卷积核为例:然后在图像上的每一点,结合以上两个结果求出近似梯度:另外也可以用下面更简单的公式替代:函数原型:void Sobel(InputArr原创 2020-12-23 19:51:13 · 4446 阅读 · 1 评论 -
OpenCV之Canny边缘检测(C++实现)
canny算子简介首先看一下最有边缘检测的三个主要评价标准:低错误率:标出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。 高定位性:标识出的边缘要与图像中的实际边缘尽可能接近。 最小响应:图像中的边缘只能标识一次,并且可能存在的图像噪声不应标识为边缘。为了满足这些要求,canny使用的变分法,这是一种寻找满足特定功能的函数的方法。最优检测用4个指数函数项的和表示,但是它非常近似于高斯函数的一阶导数。canny边缘检测的步骤第一步:消除噪声一般情况下,使用高斯平滑滤波器降噪,以原创 2020-12-23 12:03:25 · 17168 阅读 · 3 评论 -
边缘检测的一般步骤
第一步:滤波边缘检测算法主要是基于图像强度的一阶和二阶导数,但是导数通常对噪声很敏感,因此必须通过滤波器来改善与噪声有关的边缘检测器的性能。常见的滤波器主要是高斯滤波,因此采用离散化的高斯函数产生一组归一化的高斯核,然后基于高斯核函数对图像灰度矩阵的每一点进行加权求和。第二步:增强增强边缘的基础是确定图像各点邻域强度的变化值。增强算法可以将图像灰度点邻域强度值有显著变化的点凸显出来。在具体的实现时,可通过计算梯度复制来确定。第三部:检测经过图像的增强,往往邻域中有很多点的幅值比较大,而在原创 2020-12-23 10:59:53 · 3555 阅读 · 0 评论 -
OpenCV之自适应阈值操作:adaptiveThreshold()函数
adaptiveThreshold()函数的作用是对矩阵采用自适应阈值操作,函数原型:double adapTivaThreshold(InputArray src, OutputArray dst, double maxVal, int adaptiveMethod, int thresholdType, int blockSize, double C)第一个参数:InputArray类型的src,输入数组,必须为单通道,8位或32位的浮点型Mat即可。 第二个参数:OutputArray类型原创 2020-12-22 17:13:47 · 3115 阅读 · 0 评论 -
OpenCV之固定阈值操作:threshold()函数
函数threshold()函数是对单通道数组应用的固定阈值操作。该函数的典型应用是对灰度图像进行阈值操作得到二值图像,或者去掉噪声,例如过滤掉很小或者很大像素值的图像点。double threshold(InputArray src, OutputArray dst, double thresh, double maxVal, int type)第一个参数:InputArray类型的src,输入数组,必须为单通道,8位或32位的浮点型Mat即可。 第二个参数:OutputArray类型的dst,原创 2020-12-22 16:31:04 · 789 阅读 · 0 评论 -
图像高斯金字塔
图像金字塔图像金字塔是图像多尺度表达的一种,是一种以多分辨率来解释图像的有效单概念简单的结构。金字塔的底部是待处理图像的高分辨率表示,而顶部是以低分辨率的近似。将一层一层的图像比喻成金字塔,层级越高,图像越小,分辨率越低。常见的图像金字塔主要有两种,分别是:高斯金字塔:用来向下采样,是主要的图像金字塔。 拉普拉斯金字塔:用来从金字塔底层图像重建上层未采样的图像,在数字图像处理中也即是预测残差,可以对图像进行最大程度的还原,配合高斯金字塔一起使用。两者的主要区别在于:高斯金字塔用来向下采样原创 2020-12-22 15:41:43 · 371 阅读 · 0 评论 -
OpenCV之漫水填充与floodFill函数(C++实现)
漫水填充的定义漫水填充是一种用特定的颜色填充连通区域,通常设置可连通像素的上下限以及连通方式来得到不同的填充效果的方法。漫水填充经常被用来标记或分离图像的一部分,以便对其进行进一步非处理或分析,也可以用来对输入图像获取掩膜区域,掩膜会加速处理过程,获只处理掩膜指定的像素点,操作的结果总是某个连续区域。漫水填充的基本思想漫水填充,就是自动的选中与种子点相连的区域,然后将该区域替换成指定的颜色,这个功能经常被用来标记或分离图像的一部分进行处理或分析。漫水填充也可以用来输入图像获取掩膜区域,掩膜会加速原创 2020-12-21 18:40:56 · 3817 阅读 · 0 评论 -
OpenCV之黑帽运算(C++实现)
黑帽运算,是闭运算的结果图与原图像之差,数学表达式是:dst = blackhat(src, element) = close(src, element) - src黑帽运算的效果图突出了比原图轮廓周围更暗的区域,且这一操作与选择的核的大小相关。代码示例:#include <iostream>#include <opencv2/opencv.hpp>#include <opencv2/core/core.hpp>#include <openc原创 2020-12-19 16:46:54 · 489 阅读 · 2 评论 -
OpenCV之顶帽运算(C++实现)
顶帽运算,又被称为礼帽运算,是原图像与开运算结果图之差,数学表达式是:dst = tophat(src, element) = src - open(src, element)因为开运算带来的结果是放大了裂缝或者局部低亮度的区域,因此从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更加明亮的区域,且这一操作与选择的核的大小相关。顶帽操作常用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景,而微小物体比较有规律的情况下,可以使用顶帽运算进行背景提取。代码示例:#inc原创 2020-12-19 16:40:17 · 999 阅读 · 1 评论 -
OpenCV之形态学梯度(C++实现)
图像的形态学梯度是膨胀图与腐蚀图之差,数学表达式是:dst = grad(sec, element) = dilate(src, element) - erode(src, element)对二值图进行这一操作可以得到图像中白色区域的边界,因此可以用形态学梯度来保留物体的边界轮廓。同样有两种方法实现,代码示例:#include <iostream>#include <opencv2/opencv.hpp>#include <opencv2/core/cor原创 2020-12-19 16:30:54 · 1010 阅读 · 1 评论 -
OpenCV之图像闭运算(C++实现)
闭运算是先膨胀后腐蚀的过程,数学表达式为:dst = close(src, element) = erode(dilate(src, element))闭运算能够排除小型黑洞(黑色区域),也有两种方法,代码示例:#include <iostream>#include <opencv2/opencv.hpp>#include <opencv2/core/core.hpp>#include <opencv2/imgproc/imgproc.hpp&g原创 2020-12-19 16:20:10 · 4200 阅读 · 1 评论 -
OpenCV之图像开运算(C++实现)
开运算,其实就是先腐蚀后膨胀的过程,其数学表达式为:dst = open(src, element) = dilate(erode(src, element))开运算可以用来消除小物体,在纤细点处分离物体,并且在平滑较大物体边界的同时不改变其面积。实现图像开运算有两种方法,如下代码示例:#include <iostream>#include <opencv2/opencv.hpp>#include <opencv2/core/core.hpp>#i原创 2020-12-19 16:15:28 · 5270 阅读 · 2 评论 -
OpenCV之图像腐蚀:erode()函数(C++实现)
dilate函数是使用像素邻域内的局部极小运算符来腐蚀图像。函数原型:void erode(InputArray src, OutputArray dst, InputArray kernel, Point anchor=Point(-1, -1), int iterations=1, int borderType=BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue());参数详解:第一个参原创 2020-12-18 16:16:50 · 7937 阅读 · 2 评论 -
OpenCV之图像膨胀:dilate函数(C++实现)
dilate函数是使用像素邻域内的局部极大运算符来膨胀图像。函数原型:void dilate(InputArray src, OutputArray dst, InputArray kernel, Point anchor=Point(-1, -1), int iterations=1, int borderType=BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue());参数详解:第一个原创 2020-12-18 15:34:50 · 5003 阅读 · 2 评论 -
OpenCV之形态学操作:腐蚀与膨胀
形态学操作形态学操作就是基于形状的一系列图像操作。最基本的形态学操作有两种:膨胀(delate)和腐蚀(erode)。膨胀与腐蚀能实现多种多样的功能,主要包括:消除噪声; 分割出独立的图像元素,在图像中连接相邻的元素; 寻找图像中的明显的极大值区域或极小值区域; 求图像的梯度腐蚀和膨胀是对图像中的白色部分(高亮部分)而言的,不是黑色部分。膨胀是对图像中的高亮部分进行膨胀,类似于邻域扩张,效果图拥有比原图更大的高亮区域。腐蚀操作是原图中的高亮部分被腐蚀,类似于“邻域被蚕食”,效果图的高原创 2020-12-17 15:31:06 · 1191 阅读 · 0 评论 -
OpenCV之双边滤波:bilateralFilter()函数(C++实现)
双边滤波函数原型:void bilateralFilter(InputArray src, OutputArray dst, int d, double sigmaColor, douboe sigmaSpace, int borderType=BORDER_DEFAULT)参数详解:第一个参数:InputArray类型的src,输入图像,需要是8位或者浮点型单通道、三通道的图像。 第二个参数:OutputArray类型的dst,需要与输入图像有一样的尺寸和类型。 第三个参数:int类型的原创 2020-12-16 20:00:06 · 3503 阅读 · 1 评论 -
OpenCV之中值滤波:medianBlur()函数(C++实现)
medianBlur函数使用中值滤波来平滑(模糊)处理一张图像。对于多通道图像,它对每个通道单独处理。函数原型:void medianBlur(InputArray src, OutputArray dst, int kszie);参数详解;第一个参数:InputArray类型的src,输入图像,Mat类的对象。该函数对通道是独立处理的,且可以处理1、3或4得到的Mat图像,但是待处理的图像深度应该是CV_8U,CV_16U,CV_32F,但是对于较大孔径尺寸的图像,只能是CV_8U。原创 2020-12-16 19:41:27 · 4225 阅读 · 0 评论 -
openCV之高斯滤波:GaussianBlur()函数(C++实现)
GaussianBlur函数的作用时高斯滤波器来模糊一张图像,对输入的图像src进行高斯滤波后输出dst。函数原型:void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, intborderType=BORDER_DEFAULT)参数详解:第一个参数:InputArray类型的src,输入图像,Mat类的对象。该函数对通道是独立处理的,且可以处理任意通道数的原创 2020-12-16 15:50:44 · 9068 阅读 · 2 评论 -
OpenCV之均值滤波:blur()函数(C++实现)
blur的作用时对输入的图像进行均值滤波后输出。函数原型:void blur(InputArray src, OutputArray dst, Size ksizem Point anchor=Point(-1, -1), int borderType=BORDER_DEFAULT);参数详解:第一个参数:InputArray类型的src,输入图像,Mat类的对象。该函数对通道是独立处理的,且可以处理任意通道数的图像,但是待处理的图像深度应该是CV_8U,CV_16U,CV_16S,CV_原创 2020-12-15 19:21:16 · 2302 阅读 · 0 评论 -
opencv实现多张图像全景拼接
原理对多张图片进行基于SIFT的特征检测算法,如果符合最小拼接要求大的关键点`matchKeypoints`数量,使用`OpenCV-Python`自带的`stitching`方法进行全景拼接,但是对于拼接后的黑边裁剪效果不好,可以修改优化。代码:https://download.youkuaiyun.com/download/xddwz/13695511使用方式python image_stitching.py --images images/scottsdale --output output.p原创 2020-12-15 16:06:07 · 2210 阅读 · 2 评论 -
opencv算法未实现与版权问题的解决
在使用opencv做图像匹配的时候报错:cv2.error: OpenCV(3.4.3) C:\projects\opencv-python\opencv_contrib\modules\xfeatures2d\src\sift.cpp:1207: error: (-213:The function/feature is not implemented) This algorithm is patented and is excluded in this configuration; Set OPEN原创 2020-12-15 14:49:06 · 1112 阅读 · 0 评论 -
解决AttributeError: module ‘cv2.cv2‘ has no attribute ‘bgsegm‘
使用cv2是报错:AttributeError: module 'cv2.cv2' has no attribute 'bgsegm'报错原因:使用的python环境中没有安装扩展包contrib可以通过pip或者conda安装pip install opencv-contrib-python注意:contrib的版本需要和opencv版本一致......原创 2020-12-15 11:03:16 · 10645 阅读 · 9 评论 -
OpenCV之方框滤波:boxFilter函数(C++实现)
boxFilter函数的作用是使用方框滤波来模糊一张图片,函数原型:void boxFilter(InputArray src, OutputArray dst, int ddepth, Size ksize, Point anchor=Point(-1, -1), boolnormalize=true, int borderType=BORDER_DEPAULT)第一个参数:InputArray类型的src,输入图像,Mat类的对象。该函数对通道是独立处理的,且可以处理任意通道数的图像,但...原创 2020-12-14 20:13:44 · 2794 阅读 · 0 评论 -
OpenCV之图像滤波简介
平滑处理也称模糊处理,是一种简单且使用频率很高的图像处理方法。平滑处理的用途很多,最常用的是用来减少图像上的噪点或者失真。图像滤波是指在尽量保证图像细节特征的条件下对目标图像的噪声进行抑制,是图像处理中不可缺少的操作。其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。消除图像中的噪声成分叫做图像的平滑化或滤波操作。信号或图像的能量大部分集中在幅度谱的低频和中频段,而在高频段,有用的信息经常被噪声淹没。因此,一个能降低高频成分幅度的滤波器就能减弱噪声的影响。图像滤波的目的有两个:原创 2020-12-14 15:47:20 · 213 阅读 · 0 评论