opencv - 具有非整数缩放的 OpenCV 双线性下采样
问题描述
我需要帮助来确定 OpenCV 用于使用非线性缩放因子进行图像下采样的算法/实现。
我知道这个问题已经问过几次了,但大多数答案似乎与 OpenCV 的实现不匹配(例如,使用 OpenCV 时这个答案不正确:https ://math.stackexchange.com/questions/48903/2d-array -下采样和上采样使用双线性插值)。
最小问题表述:
我想使用双线性插值将分辨率为 4x4 的图像下采样为分辨率为 3x3 的图像。我对插值系数感兴趣。
python中的示例:
img = np.asarray([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]]).astype(np.float32)
img_resized = cv2.resize(img, (3, 3), 0, 0, cv2.INTER_LINEAR).astype(np.float32)
print(img)
# [[ 1. 2. 3. 4.]
# [ 5. 6. 7. 8.]
# [ 9. 10. 11. 12.]
# [13. 14. 15. 16.]]
print(img_resized)
# [[ 1.8333333 3.1666667 4.5 ]
# [ 7.166667 8.5 9.833333 ]
# [12.5 13.833333 15.166666 ]]
插值系数:
经过大量的反复试验,我发现了 OpenCV 用于这种特定情况的插值系数。
对于 3x3 图像的角点:
1.8333333 = 25/36 * 1 + 5/36 * 2 + 5/36 * 5 + 1/36 * 6
4.5000000 = 25/36 * 4 + 5/36 * 3 + 5/36 * 8 + 1/36 * 7
12.5000000 = 25/36 * 13 + 5/36 * 9 + 5/36 * 14 + 1/36 * 10
15.1666666 = 25/36 * 16 + 5/36 * 15 + 5/36 * 12 + 1/36 * 11
对于 3x3 图像的中间点:
8.5 = 1/4 * 6 + 1/4 * 7 + 1/4 * 10 + 1/4 * 11
对于 3x3 图像的剩余 4 个点:
3.1666667 = 5/12 * 2 + 5/12 * 3 + 1/12 * 6 + 1/12 * 7
7.1666667 = 5/12 * 5 + 5/12 * 9 + 1/12 * 6 + 1/12 * 10
9.8333333 = 5/12 * 8 + 5/12 * 12 + 1/12 * 7 + 1/12 * 11
13.833333 = 5/12 * 14 + 5/12 * 15 + 1/12 * 10 + 1/12 * 11
问题:
有人可以帮我理解这些插值系数吗?它们是如何计算的?我试图阅读 cv::resize() 函数的源代码,但它对我没有多大帮助:S
解决方案
在玩弄了各种测试用例之后,我想我知道 OpenCV 如何选择样本点位置的答案了。正如@ChrisLuengo 在评论中指出的那样,OpenCV 在下采样之前似乎没有应用低通滤波器,而是仅使用(双)线性插值。
(可能的)解决方案:
假设我们有一个 5x5 的图像,其像素位置用下图中的蓝色圆圈表示。我们现在想将其下采样为 3x3 或 4x4 的图像,并且需要在原始图像网格中找到新的下采样图像的采样位置。
似乎 OpenCV 对原始图像网格使用 1 的像素距离,对新的图像网格使用 (OLD_SIZE / NEW_SIZE) 的像素距离,因此这里是 5/3 和 5/4。此外,它在中心点对齐两个网格。因此,OpenCV 的确定性采样算法可以可视化如下:
可视化 5x5 到 3x3:
可视化 5x5 到 4x4:
示例代码(Python 2.7):
import numpy as np
import cv2
# 1. H_W is the height & width of the original image, using uniform H/W for this example
# resized_H_W is the height & width of the resized image, using uniform H/W for this example
H_W = 5
resized_H_W = 4
# 2. Create original image & Get OpenCV resized image:
img = np.zeros((H_W, H_W)).astype(np.float32)
counter = 1
for i in range(0, H_W):
for j in range(0, H_W):
img[i, j] = counter
counter += 1
img_resized_opencv = cv2.resize(img, (resized_H_W, resized_H_W), 0, 0, cv2.INTER_LINEAR).astype(np.float32)
# 3. Get own resized image:
img_resized_own = np.zeros((resized_H_W, resized_H_W)).astype(np.float32)
for i in range(0, resized_H_W):
for j in range(0, resized_H_W):
sample_x = (1.0 * H_W) / 2.0 - 0.50 + (i - (1.0 * resized_H_W - 1.0) / 2.0) * (1.0 * H_W) / (1.0 * resized_H_W)
sample_y = (1.0 * H_W) / 2.0 - 0.50 + (j - (1.0 * resized_H_W - 1.0) / 2.0) * (1.0 * H_W) / (1.0 * resized_H_W)
pixel_top_left = img[int(np.floor(sample_x)), int(np.floor(sample_y))]
pixel_top_right = img[int(np.floor(sample_x)), int(np.ceil(sample_y))]
pixel_bot_left = img[int(np.ceil(sample_x)), int(np.floor(sample_y))]
pixel_bot_right = img[int(np.ceil(sample_x)), int(np.ceil(sample_y))]
img_resized_own[i, j] = (1.0 - (sample_x - np.floor(sample_x))) * (1.0 - (sample_y - np.floor(sample_y))) * pixel_top_left + \
(1.0 - (sample_x - np.floor(sample_x))) * (sample_y - np.floor(sample_y)) * pixel_top_right + \
(sample_x - np.floor(sample_x)) * (1.0 - (sample_y - np.floor(sample_y))) * pixel_bot_left + \
(sample_x - np.floor(sample_x)) * (sample_y - np.floor(sample_y)) * pixel_bot_right
# 4. Print results:
print "\n"
print "Org. image: \n", img
print "\n"
print "Resized image (OpenCV): \n", img_resized_opencv
print "\n"
print "Resized image (own): \n", img_resized_own
print "\n"
print "MSE between OpenCV <-> Own: ", np.mean(np.square(img_resized_opencv - img_resized_own))
print "\n"
免责声明:
这只是我通过约 10 个测试用例测试的理论。我并不声称这是 100% 正确的。
推荐阅读
- excel - 如何一次删除多行而不会出现有关“范围类的删除方法”的错误?
- php - 插入 SQL 时时间戳输入不正确
- python - 如何锁定文件以防止操作系统中其他应用程序的写操作?
- amazon-web-services - Elastic Beanstalk 已停止记录到 CloudWatch
- python - 用单行代码在python中打印列表元素
- r - na.fail.default(as.ts(x)) 中的错误:每当我尝试运行周期图函数时,对象中的值都会丢失
- java - 需要有关使用 Android 模式检测车牌号的帮助
- javascript - AngularJS - 重置数组时出现 $digest 错误
- bootstrap-4 - 打开开发人员工具时引导垂直滚动条消失
- python - 如何将字符或数字添加到python中的变量?