与 Photoshop 相比,OpenCV Warpaffine 的质量较低
OpenCV's Warp Affine Has Lower Quality Compared to Photoshop
我想使用 OpenCV 的 cv2.warpAffine
函数从 CelebA 图像(1024x1024 大小)转换和对齐检测到的人脸(320x240 大小),但转换图像的质量明显低于我尝试对齐时的质量在 Photoshop 中手工制作:(左图由 Photoshop 转换,右图由 OpenCV 转换)
我使用了 OpenCV 的所有插值技术,但 none 其中的质量接近 Photoshop。
我使用的代码是:
warped = cv2.warpAffine(image, TRANSFORM_MATRIX, (240, 320), flags=cv2.INTER_AREA)
可能是什么问题导致转换后的图像质量如此低?
如果需要,这里有 Link 原始 1024x1024 图片。
问题及一般解决方案
您正在对信号进行下采样。
方法总是一样的:
- 低通去除高频成分
- resample/decimate
不该做什么
如果你不做低通,你会得到混叠。你注意到了。混叠意味着采样步骤可能会完全错过一些高频分量 (edge/corner/point/...),从而产生那些奇怪的伪像。正确重采样的图像不会完全丢失这种高频特征。
如果在 重采样后执行低通,它不会解决问题,只会隐藏它。伤害已经造成。
如果你对一些由强烈对比的线条组成的规则网格进行下采样,你就可以在这两个方面说服自己。尝试交替使用黑色和白色的单像素线条以获得最佳效果。
实施
PIL 等库在重采样之前隐式执行低通。
OpenCV 不会(总的来说有点)。即使使用 Lanczos 插值(在 OpenCV 中),您也无法跳过低通,因为 OpenCV 的 Lanczos 具有固定系数。
OpenCV有INTER_AREA
,这是一个线性插值,但它另外 对角样本之间区域中的所有像素求和(而不是仅对这四个角进行采样)。这 可以 为您省去额外的低通步骤。
这是 cv.resize(im, (240, 240), interpolation=cv.INTER_AREA)
的结果:
这是 cv.warpAffine(im, M[:2], (240, 240), interpolation=cv.INTER_AREA)
和 M = np.eye(3) * 0.25
的结果(等效缩放):
看来 warpAffine
不能 做 INTER_AREA
。这对你来说很糟糕:/
如果您需要使用 OpenCV 进行下采样,并且它是 2 的幂,您可以使用 pyrDown
。那就是低通和抽取……两倍。重复申请,让你拥有更高的权力。
如果您需要任意下采样并且出于某种原因不喜欢 INTER_AREA
,则必须对输入应用 GaussianBlur
。 Sigma 需要与比例因子(成反比)成比例。高斯滤波器的西格玛和产生的截止频率之间存在某种关系。如果您不想任意选择一个值,您将需要进一步调查。检查 pyrDown
的内核,以及它最匹配的高斯西格玛。对于 0.5 的比例因子,这可能是一个很好的值,其他因素应该(成反比)成比例。
对于简单的缩小,一个高斯模糊就可以了。对于仿射扭曲和更高的变换,您需要应用低通处理,以尊重所查找的每个像素的不同比例,因为它们在源图像中的“支持”不再是正方形,甚至可能不是矩形,但是任意四边形!
我没说什么?
这适用于 向下 采样。如果你 up-sample,做 not lowpass.
我想使用 OpenCV 的 cv2.warpAffine
函数从 CelebA 图像(1024x1024 大小)转换和对齐检测到的人脸(320x240 大小),但转换图像的质量明显低于我尝试对齐时的质量在 Photoshop 中手工制作:(左图由 Photoshop 转换,右图由 OpenCV 转换)
我使用了 OpenCV 的所有插值技术,但 none 其中的质量接近 Photoshop。
我使用的代码是:
warped = cv2.warpAffine(image, TRANSFORM_MATRIX, (240, 320), flags=cv2.INTER_AREA)
可能是什么问题导致转换后的图像质量如此低?
如果需要,这里有 Link 原始 1024x1024 图片。
问题及一般解决方案
您正在对信号进行下采样。
方法总是一样的:
- 低通去除高频成分
- resample/decimate
不该做什么
如果你不做低通,你会得到混叠。你注意到了。混叠意味着采样步骤可能会完全错过一些高频分量 (edge/corner/point/...),从而产生那些奇怪的伪像。正确重采样的图像不会完全丢失这种高频特征。
如果在 重采样后执行低通,它不会解决问题,只会隐藏它。伤害已经造成。
如果你对一些由强烈对比的线条组成的规则网格进行下采样,你就可以在这两个方面说服自己。尝试交替使用黑色和白色的单像素线条以获得最佳效果。
实施
PIL 等库在重采样之前隐式执行低通。
OpenCV 不会(总的来说有点)。即使使用 Lanczos 插值(在 OpenCV 中),您也无法跳过低通,因为 OpenCV 的 Lanczos 具有固定系数。
OpenCV有INTER_AREA
,这是一个线性插值,但它另外 对角样本之间区域中的所有像素求和(而不是仅对这四个角进行采样)。这 可以 为您省去额外的低通步骤。
这是 cv.resize(im, (240, 240), interpolation=cv.INTER_AREA)
的结果:
这是 cv.warpAffine(im, M[:2], (240, 240), interpolation=cv.INTER_AREA)
和 M = np.eye(3) * 0.25
的结果(等效缩放):
看来 warpAffine
不能 做 INTER_AREA
。这对你来说很糟糕:/
如果您需要使用 OpenCV 进行下采样,并且它是 2 的幂,您可以使用 pyrDown
。那就是低通和抽取……两倍。重复申请,让你拥有更高的权力。
如果您需要任意下采样并且出于某种原因不喜欢 INTER_AREA
,则必须对输入应用 GaussianBlur
。 Sigma 需要与比例因子(成反比)成比例。高斯滤波器的西格玛和产生的截止频率之间存在某种关系。如果您不想任意选择一个值,您将需要进一步调查。检查 pyrDown
的内核,以及它最匹配的高斯西格玛。对于 0.5 的比例因子,这可能是一个很好的值,其他因素应该(成反比)成比例。
对于简单的缩小,一个高斯模糊就可以了。对于仿射扭曲和更高的变换,您需要应用低通处理,以尊重所查找的每个像素的不同比例,因为它们在源图像中的“支持”不再是正方形,甚至可能不是矩形,但是任意四边形!
我没说什么?
这适用于 向下 采样。如果你 up-sample,做 not lowpass.