我必须使用angle = atan2(norm(cross(a,b)),dot(a,b))
, 来计算两个向量之间的角度a,b
,它们是双精度类型,并且norm
对于这种类型是未定义的。我该如何解决这个问题?我需要以这种方式计算两个向量之间的角度。
2 回答
由于@rayryeng 已经成功回答了这个问题,我想通过分享我在Matlab 中调试的经验将我的帖子变成更一般的帖子。我希望任何设法找到这篇文章的人都或多或少地思考一个好的程序员应该具备的习惯。
问题是这样的:“如果我出错了怎么办?”
这是Eric 的一篇出色的文章,他在其中列出了遇到错误并希望摆脱它时的经验法则。它最初被 Stackoverflow 引用,这就是我阅读它的原因。
如果你仍然不知道如何使用你的代码,看看这个人是怎么做的:
精确定位越野车线
(数字应以 0 开头)确保在运行脚本之前,您已
clear
清除所有以前存储的变量,包括臭名昭著的i
和j
's(您永远不应在任何工作区中看到它们)。如果需要任何一个来运行有缺陷的代码,在清除save('buggy.mat','importantvar')
之前clear
和之后。load('buggy.mat')
通过这样做,您可以将错误代码与其他任何可能产生不良影响的代码隔离开来。例如,在之前调用的脚本中,有一行
double = [2,4,6]; % you should never name a variable `double`
在下一个脚本中,你有
>> e = str2num('uint8(200)') e = 200 >> double(e) Index exceeds matrix dimensions. >> >> f = single(2.36) f = 2.3600 >> double(f) Subscript indices must either be real positive integers or logicals. >>
原因
double
不再是内置函数,而是用户定义的变量。不小心起个名字太可惜了!....无论如何,让我们的
clear
工作区摆脱double
.>> clear
仔细阅读错误信息。
现在让我们从 OP 的问题开始。原始代码(修剪)是这样的 -
img = imread('peppers.png'); a = img(300,200,:); b = img(200,300,:); d = norm(cross(a,b));
....因此错误
Undefined function 'norm' for input arguments of type 'uint8'. Error in untitled (line 6) d = norm(cross(a,b));
大多数初学者只对错误消息的第一行感兴趣,仅靠它通常不会提供任何有用的帮助,或者只对红色感兴趣,这会导致著名的问题“我的代码不起作用!”
但三思而后行。您还有 2 行未读!
Error in untitled (line 6)
说我正在运行一个名为的脚本untitled
,并且(第一个)错误位于第 6 行,该行中的代码是d = norm(cross(a,b));
.现在,至少您对自己的代码了解更多——“我的代码
d = norm(cross(a,b));
不起作用!”虽然很可能我们也可能会投票结束这类问题,但它仍然比简单的“它不起作用!”要好得多。
现在我们可以精确定位越野车线
try % this line will raise an error d = norm(cross(a,b)); catch err disp(err.message) end
查看功能
首先,确保内部函数cross
按预期工作 -
>> cross(a,b)
ans(:,:,1) =
0
ans(:,:,2) =
255
ans(:,:,3) =
0
>>
好的。所以现在我们甚至可以将错误范围缩小到外部norm
。
还要提一件事。您始终可以通过在 Google(或任何其他搜索引擎)中键入“matlab函数”(例如“matlab norm”)并单击第一个结果来找到任何内置函数的Mathworks 文档。如果您愿意,您也可以在 Matlab 命令窗口中键入,例如阅读 Matlab 中的文档。当然,我们很高兴在 Stackoverflow 上通过做同样的事情为您提供参考,但这需要更长的时间,因为在这方面,人类总是比搜索引擎慢。doc _function_
doc norm
错误读取Undefined function 'norm' for input arguments of type 'uint8'.
。所以输入norm
不应该是uint8
无符号的 8 位整数。但它应该是什么?
% why `norm` "does not work"?
% this line runs perfectly well
norm(cross([1,2,3], [4,5,6]))
% so what is working?
class([1,2,3]) % so `norm` works for `double`
我们现在可以做的一件事是转换a
和b
双精度。现在让我们试试吧。
% try fixing 'uint8' error
a2 = double(a);
b2 = double(b);
whos a b % now they are double, which `norm` should work for
try
% this line will raise an error
d = norm(cross(a2,b2));
catch err
disp(err.message)
end
现在错误变为Input must be 2-D.
. 输入有什么问题?
% what is "must be 2-D" error?
size(a2) % a2 is 3-D
disp(b2) % b2 is also 3-D
这会在命令窗口中给出输出
ans =
1 1 3
(:,:,1) =
255
(:,:,2) =
150
(:,:,3) =
0
在 OP 的问题中,他/她试图计算一些关于色差的东西(据我所知),这涉及RGB 空间中两个颜色向量之间的角度。所以需要向量。使用imread
时,图像的每个像素在矩阵中存储为 3 个元素,前 2 维是其物理位置,第 3 维是 RGB 通道分量。因此,颜色为 rgb[255,150,0] 的像素(200,300)由我们存储在变量中,b
其中是一个 3-D 向量。
通过了解我们需要什么以及Matlab 可以做什么,我们可以将这两点合二为一。我们需要 和 的叉积的范数a
,b
而有用信息(3 个分量值)存储在第 3 维中。Matlab 可以计算向量的叉积的范数及其所有信息在一维。(这里,“维度”是指 Matlab 变量的维度;在其第一维中具有 3 个元素的向量在物理上是 3-D 向量)。
经过三思,我们现在可以调试我们的代码——只需将所有 3 个元素放入第一个维度。
% so we want the 3 elements in the 3rd dimension become in the 1st dim
a3 = squeeze(a2);
b3 = reshape(b2,numel(b2),[]);
try
d = norm(cross(a3,b3));
catch err
disp(err.message)
end
d
奖励:如果默认情况下 Matlab 将 3-D 向量视为“1-D 数组”,那么很可能该cross
函数没有正常工作。让我们做个检查——
>> clear
>> a = [1,2,3]
a =
1 2 3
>> b=[4,5,6]
b =
4 5 6
>> cross(a,b)
ans =
-3 6 -3
>>
结果应该和我们手工计算得到的一样。
现在,如果我们将组件放入变量的第三维 -
>> clear
>> a(1,1,:)=[1,2,3]
a(:,:,1) =
1
a(:,:,2) =
2
a(:,:,3) =
3
>> b(1,1,:)=[4,5,6]
b(:,:,1) =
4
b(:,:,2) =
5
b(:,:,3) =
6
>> cross(a,b)
ans(:,:,1) =
-3
ans(:,:,2) =
6
ans(:,:,3) =
-3
>>
....似乎还可以。cross
也将结果放入第三维。事实上,Mathworks 的文档说
如果 A 和 B 是向量,那么它们的长度必须为 3。
如果 A 和 B 是矩阵或多维数组,则它们必须具有相同的大小。在这种情况下,交叉函数将 A 和 B 视为三元素向量的集合。该函数计算沿大小等于 3 的第一个数组维度的对应向量的叉积。
最后,对于任何想通过编程做某事的人来说,一件事总是正确的——在编写代码时要小心谨慎。
在您的评论中,您向我们展示了您实际上是如何写出角度计算的,这与您在帖子中的方式不同。
atan2(norm(cross(I(i,j,:),I_avg)),dot(I(i,j,:),I_avg));
I
是您正在加载的图像。由于您设置子集的方式,我假设它是颜色I
。因为I
是一个 3D 矩阵,doingI(i,j,:)
会给你一个1 x 1 x 3
向量,而实际上这必须是一个 1D 向量。 norm
无法识别此结构,这就是您收到此错误的原因。因此,您需要使用squeeze
删除单例维度,使其成为3 x 1
向量,而不是1 x 1 x 3
向量。因此,您需要重写您的代码,以便改为执行此操作。请记住,在您的评论中,angle
总是在for
循环内部被覆盖,因此您可能希望保存每个像素的结果。有了这个,您可能想要创建一个 2D 角度数组来存储这些结果。换句话说:
I=imread('thesis.jpg');
I = double(I);
angles = zeros(m,n);
I_avg = squeeze(I_avg); %// Just in case
for i=1:m
for j=1:n
pixels = squeeze(I(i,j,:)); %// Add this statement and squeeze
angles(i,j) = atan2(norm(pixels,I_avg)),dot(pixels,I_avg)); %// Change
end
end
小注
MATLAB 有一个名为的内置函数angle
,用于确定从原点到复平面中复数的角度。不建议您调用您的变量angle
,因为这会无意中覆盖angle
函数,并且您从此时开始创建的任何其他代码都可能依赖于该实际angle
函数,并且您会得到意想不到的结果。
另一个小提示
不推荐使用i
and作为循环变量。j
这些字母是为复数保留的,这可能会产生无意的结果。看看这个问题并在这里由Shai发布- Using i and j as variables in Matlab。因此,建议您改用其他变量名称。