注意:我将这个问题放在MATLAB和Python标签中,因为我最擅长这些语言。然而,我欢迎任何语言的解决方案。
问题前言
我使用鱼眼镜头拍摄了一张图像。该图像由许多正方形组成的模式构成。我想要做的是检测每个正方形的质心,然后使用这些点来执行图像的去畸变 - 具体而言,我正在寻找正确的畸变模型参数。需要注意的是,并非所有正方形都需要被检测到。只要大部分被检测到,那就完全可以...但这不是本帖的重点。我已经编写了参数估计算法,但问题是它需要在图像中出现共线的点。
我想要问的基本问题是,在给定这些点的情况下,最佳的分组方式是什么,以便每个组都包含水平线或垂直线?
我的问题背景
关于我提出的问题并不是很重要,但如果您想了解我的数据来源并进一步理解我提出的问题,请阅读以下内容。如果您不感兴趣,那么您可以直接跳转到下面的问题设置部分。
下面是我处理的一个图像示例: 这是一张960 x 960的图片。原图分辨率更高,但我对图像进行了下采样以便更快地处理。如您所见,图像中散布着许多方形图案。此外,我计算的质心是相对于上述下采样的图像的。
我设置的用于检索质心的流程如下: 1. 进行Canny边缘检测 2. 关注一个区域的兴趣,以最小化误报。该区域基本上是没有任何覆盖其中一侧的黑色胶带的正方形。 3. 找到所有不同的封闭轮廓 4. 对于每个不同的封闭轮廓... a. 进行Harris角点检测 b. 确定结果是否具有4个角点 c. 如果是,则此轮廓属于一个正方形,并找到该形状的质心 d. 如果不是,则跳过此形状 5. 将步骤#4中检测到的所有质心放置在矩阵中以供进一步检查。
这是一个与上面图像相关的示例结果。每个检测到的正方形都有四个点,根据其相对于正方形本身的位置进行颜色编码。对于我检测到的每个质心,我会在图像中心点写下一个ID。 使用上述图像,有37个检测到的正方形。
问题设置
假设我有一些存储在N x 3矩阵中的图像像素点。前两列是x(水平)和y(垂直)坐标,在图像坐标空间中,y坐标是反转的,这意味着正的y向下移动。第三列是与该点相关联的ID。
这是一些用MATLAB编写的代码,它将这些点绘制到2D网格上,并使用矩阵的第三列标记每个点。如果您阅读了上面的背景,这些是由我上面概述的算法检测到的点。
data = [ 475. , 605.75, 1.;
571. , 586.5 , 2.;
233. , 558.5 , 3.;
669.5 , 562.75, 4.;
291.25, 546.25, 5.;
759. , 536.25, 6.;
362.5 , 531.5 , 7.;
448. , 513.5 , 8.;
834.5 , 510. , 9.;
897.25, 486. , 10.;
545.5 , 491.25, 11.;
214.5 , 481.25, 12.;
271.25, 463. , 13.;
646.5 , 466.75, 14.;
739. , 442.75, 15.;
340.5 , 441.5 , 16.;
817.75, 421.5 , 17.;
423.75, 417.75, 18.;
202.5 , 406. , 19.;
519.25, 392.25, 20.;
257.5 , 382. , 21.;
619.25, 368.5 , 22.;
148. , 359.75, 23.;
324.5 , 356. , 24.;
713. , 347.75, 25.;
195. , 335. , 26.;
793.5 , 332.5 , 27.;
403.75, 328. , 28.;
249.25, 308. , 29.;
495.5 , 300.75, 30.;
314. , 279. , 31.;
764.25, 249.5 , 32.;
389.5 , 249.5 , 33.;
475. , 221.5 , 34.;
565.75, 199. , 35.;
802.75, 173.75, 36.;
733. , 176.25, 37.];
figure; hold on;
axis ij;
scatter(data(:,1), data(:,2),40, 'r.');
text(data(:,1)+10, data(:,2)+10, num2str(data(:,3)));
同样地,在Python中,使用
numpy
和matplotlib
,我们有:import numpy as np
import matplotlib.pyplot as plt
data = np.array([[ 475. , 605.75, 1. ],
[ 571. , 586.5 , 2. ],
[ 233. , 558.5 , 3. ],
[ 669.5 , 562.75, 4. ],
[ 291.25, 546.25, 5. ],
[ 759. , 536.25, 6. ],
[ 362.5 , 531.5 , 7. ],
[ 448. , 513.5 , 8. ],
[ 834.5 , 510. , 9. ],
[ 897.25, 486. , 10. ],
[ 545.5 , 491.25, 11. ],
[ 214.5 , 481.25, 12. ],
[ 271.25, 463. , 13. ],
[ 646.5 , 466.75, 14. ],
[ 739. , 442.75, 15. ],
[ 340.5 , 441.5 , 16. ],
[ 817.75, 421.5 , 17. ],
[ 423.75, 417.75, 18. ],
[ 202.5 , 406. , 19. ],
[ 519.25, 392.25, 20. ],
[ 257.5 , 382. , 21. ],
[ 619.25, 368.5 , 22. ],
[ 148. , 359.75, 23. ],
[ 324.5 , 356. , 24. ],
[ 713. , 347.75, 25. ],
[ 195. , 335. , 26. ],
[ 793.5 , 332.5 , 27. ],
[ 403.75, 328. , 28. ],
[ 249.25, 308. , 29. ],
[ 495.5 , 300.75, 30. ],
[ 314. , 279. , 31. ],
[ 764.25, 249.5 , 32. ],
[ 389.5 , 249.5 , 33. ],
[ 475. , 221.5 , 34. ],
[ 565.75, 199. , 35. ],
[ 802.75, 173.75, 36. ],
[ 733. , 176.25, 37. ]])
plt.figure()
plt.gca().invert_yaxis()
plt.plot(data[:,0], data[:,1], 'r.', markersize=14)
for idx in np.arange(data.shape[0]):
plt.text(data[idx,0]+10, data[idx,1]+10, str(int(data[idx,2])), size='large')
plt.show()
我们得到:
回到问题
如您所见,这些点更多或少按照网格模式排列,并且我们可以在这些点之间形成线。具体来说,您可以看到可以水平和垂直地形成线。
例如,如果您参考我问题背景中的图像,我们可以看到有5组点可以以水平方式分组。例如,点23、26、29、31、33、34、35、37和36形成一组。点19、21、24、28、30和32形成另一组,依此类推。同样,在垂直意义上,我们可以看到点26、19、12和3形成一组,点29、21、13和5形成另一组,依此类推。
需要问的问题
我的问题是:有什么方法可以成功地将点在水平和垂直方向上分别分组,而不管点的方向如何?
条件
每条线上必须至少有三个点。如果少于三个点,则不符合要求。因此,点36和10不符合纵向线的条件,同样孤立的点23也不应该被视为一条竖向线,但它是第一个水平分组的一部分。
上面的标定图案可以处于任何方向。然而,对我处理的东西来说,你能得到的最糟糕的方向是在背景部分所看到的。
预期输出
输出将是一对列表,其中第一个列表具有元素,每个元素都为形成水平线的点ID序列。同样,第二个列表具有元素,每个元素都为形成垂直线的点ID序列。
因此,水平序列的预期输出将类似于以下内容:
MATLAB
horiz_list = {[23, 26, 29, 31, 33, 34, 35, 37, 36], [19, 21, 24, 28, 30, 32], ...};
vert_list = {[26, 19, 12, 3], [29, 21, 13, 5], ....};
Python
horiz_list = [[23, 26, 29, 31, 33, 34, 35, 37, 36], [19, 21, 24, 28, 30, 32], ....]
vert_list = [[26, 19, 12, 3], [29, 21, 13, 5], ...]
我所尝试的
在算法上,我尝试撤销这些点所经历的旋转。我执行了主成分分析,并尝试投影基于计算的正交基向量的点,以便点大致位于一个直角坐标系网格上。
一旦我有了这个,就只需要进行一些扫描线处理,在此过程中,您可以根据水平或垂直坐标的差异对点进行分组。您可以按照x
或y
值对坐标进行排序,然后检查这些排序后的坐标,并寻找大的变化。一旦遇到这种变化,那么就可以将变化之间的点合并在一起形成线条。针对每个维度执行此操作将为您提供水平或垂直分组。
关于PCA,在MATLAB和Python中,我做了以下工作:
MATLAB
%# Step #1 - Get just the data - no IDs
data_raw = data(:,1:2);
%# Decentralize mean
data_nomean = bsxfun(@minus, data_raw, mean(data_raw,1));
%# Step #2 - Determine covariance matrix
%# This already decentralizes the mean
cov_data = cov(data_raw);
%# Step #3 - Determine right singular vectors
[~,~,V] = svd(cov_data);
%# Step #4 - Transform data with respect to basis
F = V.'*data_nomean.';
%# Visualize both the original data points and transformed data
figure;
plot(F(1,:), F(2,:), 'b.', 'MarkerSize', 14);
axis ij;
hold on;
plot(data(:,1), data(:,2), 'r.', 'MarkerSize', 14);
Python
import numpy as np
import numpy.linalg as la
# Step #1 and Step #2 - Decentralize mean
centroids_raw = data[:,:2]
mean_data = np.mean(centroids_raw, axis=0)
# Transpose for covariance calculation
data_nomean = (centroids_raw - mean_data).T
# Step #3 - Determine covariance matrix
# Doesn't matter if you do this on the decentralized result
# or the normal result - cov subtracts the mean off anyway
cov_data = np.cov(data_nomean)
# Step #4 - Determine right singular vectors via SVD
# Note - This is already V^T, so there's no need to transpose
_,_,V = la.svd(cov_data)
# Step #5 - Transform data with respect to basis
data_transform = np.dot(V, data_nomean).T
plt.figure()
plt.gca().invert_yaxis()
plt.plot(data[:,0], data[:,1], 'b.', markersize=14)
plt.plot(data_transform[:,0], data_transform[:,1], 'r.', markersize=14)
plt.show()
上述代码不仅重新投影了数据,还在单个图中同时绘制了原始点和投影点。但是,当我尝试重新投影我的数据时,得到的绘图如下:
红色点是原始图像坐标,蓝色点是重新投影到基向量上的点,以尝试消除旋转。但它仍然没有完全完成任务。与这些点相关仍存在一些方向性,如果我尝试使用扫描线算法,水平跟踪下面的线或垂直跟踪侧面的线时,点会被错误地分组,这是不正确的。
也许我在过度思考这个问题,但是如果您有任何见解,将不胜感激。如果答案确实非常好,我会倾向于提供高额赏金,因为我已经卡在这个问题上很长时间了。
希望我的问题没有太啰嗦。如果您不知道如何解决这个问题,那么无论如何感谢您花费的时间阅读我的问题。
期待您可能拥有的任何见解。非常感谢!
scatter
:) 我会记住的,谢谢你的阅读! - rayryeng