MATLAB在图像处理中的应用

图像处理概念、算法和MATLAB

高音符号、Unicode、SVG、字符串、Bézier曲线、kron、隐式扩展和多义性

今天我将向你们展示如何在MATLAB中绘制这个高音谱号:

我的发现和实现过程涉及Unicode字符、SVG文件、字符串处理、贝塞尔曲线、,克隆亚麻,隐式扩展,以及polyshape.我觉得很有趣,也学到了一些东西,所以我想和大家分享。

作为个人项目,我为法国号制作专门的指法图表。当然,我是用MATLAB来实现的。对于其中一个图表,我希望能够精确且高质量地放置几个高音谱号符号。我就是这样开始这项任务的。

首先,我想知道是否有一个用于高音谱号(也称为G谱号)的Unicode符号FileFormat.info,上面有一个关于高音谱号的信息页面:

但我不确定直接使用Unicode字符是否真的适合我。Unicode字符的精确位置和大小太依赖于使用的字体。但线“Outline(作为SVG文件)”在那个信息页面上吸引了我的眼球。也许我可以用这个?

我对SVG文件了解不多,但我下载了该文件,并在文本编辑器中查看了它。似乎没什么可说的:

显然,我需要更多地了解SVG路径1.我找到了这个w3.org详细解释SVG路径的页面。幸运的是,由于我只想尝试一个快速原型,高音谱号大纲文件中的路径只包含几个命令:

  • “M”-移动到某个位置
  • “L”-画一条线到一个位置
  • 用三个点画二次贝塞尔曲线段
  • “Z”-关闭当前曲线

我当然不想仅仅为了做一个原型而编写一个完全通用的SVG路径解析器。相反,我使用一些MATLAB字符串处理功能将路径字符串分解为易于处理的块。这是我正在处理的字符串。

负载谱号路谱号路
clef_path = "M57.9375 421.875 Q50.3438 421.875 44.7188 418.2188 Q39.0938 414.5625 36.2812 407.6719 Q33.4688 400.7812 33.4688 393.75 Q37.9688 376.3125 42.75 373.2188 Q47.5312 370.125 53.7188 370.2188 Q59.625 370.4062 63.8438 372.5156 Q68.0625 374.625 70.7344 378.7031 Q73.4062 382.7812 73.4062 389.8125 Q73.4062397.125 70.7344 401.2031 q68.0625 405.2812 64.2656 407.3906 q60.4688 409.5 54.8438 409.5 q52.0312 409.5 508.6562 q48.0938 407.8125 45.8438 405.5625 q46.9688 409.7812 50.2031 412.1719 q53.4375 414.5625 57.9375 414.5625 q65.3906 414.5625 71.0859 409.6406 q76.7812 404.7188 80.3672 391.5 q83.9531 378.2812 84.7969 360 l81 360 q60.1875 36046.4062 355.7812 q32.625 351.5625 24.1875 39.4688 q15.75 327.375 15.75 309.9375 q15.75 296.4375 20.8125 279 q27 264.375 36.2812 252 q45.5625 239.625 59.625 226.6875 l67.9219 217.9688 l64.125 204.1875 q57.9375 182.25 55.9688 172.9688 q54 157.5 q54 149.625 56.5312 139.2188 q59.0625 128.8125 66.9375 124.0312 q74.8125 119.25 80.4375 119.25Q87.1875 119.25 93.6562 125.1562 Q100.125 131.0625 103.2188 142.5938 Q106.3125 154.125 106.3125 165.375 Q106.3125 174.375 104.625 184.7812 Q102.9375 195.1875 98.0156 205.0312 Q93.0938 214.875 85.7812 223.3125 L83.5312 225.9844 Q86.0625 241.1719 88.875 258.75 L90.8438 272.9531 L95.0625 272.8125 Q105.1875 272.8125 113.3438 277.6641 Q121.5 282.5156 125.4375 292.0078 Q129.375 301.5 129.375 314.4375 Q129.375 327.9375 126.2812 336.5156 Q123.1875 345.0938 115.6641 351 Q108.1406 356.9062 96.0469 358.875 Q94.9219 378.8438 90.2812 394.1719 Q85.6406 409.5 77.4141 415.6875 Q69.1875 421.875 57.9375 421.875 ZM80.4375 344.8125 L84.9375 344.6719 L84.9375 336.0938 Q84.9375 327.2344 84.0234 316.3359 Q83.1094 305.4375 80.8594 288.8438 Q77.3438 290.25 74.25 293.4141 Q71.1562 296.5781 69.3281 300.5859 Q67.5 304.5938 67.5 307.9688 Q67.5 317.5312 69.6094 323.5781 Q71.7188 329.625 77.625 335.25 L73.125 336.9375 Q67.2188 333.8438 63.7031 330.0469 Q60.1875 326.25 58.5 320.7656 Q56.8125 315.2812 56.8125 306.5625 Q56.8125 300.0938 59.5547 293.625 Q62.2969 287.1562 66.9375 282.375 Q71.5781 277.5938 78.8906 275.2031 L77.0625 264.375 Q74.5312 249.0469 72.7031 239.4844 Q63.2812 251.0156 56.8125 260.1562 Q52.3125 267.1875 48.6562 275.625 Q45 284.0625 43.875 292.2188 Q42.75 300.375 42.75 308.8125 Q42.75 318.9375 46.9688 328.5 Q51.1875 338.0625 60.1875 341.4375 Q69.1875 344.8125 80.4375 344.8125 ZM96.1875 343.125 Q103.5 340.5938 106.875 336.2344 Q110.25 331.875 111.6562 326.4609 Q113.0625 321.0469 113.0625 314.4375 Q113.0625 307.125 111.0938 300.375 Q109.125 293.625 104.7656 290.25 Q100.4062 286.875 92.8125 286.875 L92.5312 286.875 Q94.5 302.4844 95.3438 313.9453 Q96.1875 325.4062 96.1875 334.6875 L96.1875 343.125 ZM79.4531 206.4375 Q86.9062 198.9844 90.7031 192.9375 Q94.5 186.8906 96.75 179.3672 Q99 171.8438 99 165.375 Q99 157.5 97.5938 152.4375 Q96.1875 147.375 92.8125 143.1562 Q89.4375 138.9375 84.9375 138.9375 Q81.5625 139.2188 78.1875 141.6094 Q74.8125 144 73.2656 147.7969 Q71.7188 151.5938 71.7188 154.9688 Q71.7188 159.4688 72.5625 169.7344 Q73.4062 180 77.625 198.5625 L79.4531 206.4375 Z"

我首先将每个命令放在单独的一行上。

命令=“MQLZ”;对于K = 1:length(commands) ck = string(commands(K));Clef_path = replace(Clef_path,ck,newline + ck);终止

然后我将字符串按换行符分开,形成一个字符串数组。

谱号路径=分裂线(谱号路径);谱号路径(1:15)
ans = 15×1 string array "" "M57.9375 421.875 "" Q50.3438 421.875 44.7188 418.2188 "" Q39.0938 414.5625 36.2812 407.6719 "" Q33.4688 400.7812 33.4688 393.75 "" Q33.4688 387.2812 35.7188 381.7969 "" Q37.9688 376.3125 42.75 373.2188 "" Q47.5312 370.125 53.7188 370.2188 "" Q59.625 370.4062 63.8438 372.5156 "" Q68.0625 374.625 70.7344 378.7031 "" q73.4062 382.7812 73.4062 389.8125 " " q73.4062 397.125 70.7344 401.2031 " " q68.0625 405.2812 64.2656 407.3906 " " q60.4688 409.5 54.8438 409.5 " " q52.0312 409.5 50.0625 408.6562 "

现在我需要解释一下那些“Q”命令。它们表示使用三个控制点(当前点和“Q”字符后面列出的两个附加点)绘制二次贝塞尔曲线。我不熟悉绘制贝塞尔曲线的数学,但我很快就发现Mike Garrity的老图形博客文章关于这个主题。这里是一个简短的解释片段和来自那篇旧文章的代码。

P1 = [5;-10);P2 = [18;18];P3 = [45;15);cla placelabel (P1,“P_1”);placelabel (P2,“P_2”);placelabel (P3,“P_3”); xlim([0 50])轴平等的

[迈克的解释]我们仍然有$t$从0到1,但我们将使用二阶多项式,像这样:

$P(t) = (1-t)^2 P_1 + 2 (1-t) t_2 + t^2 P_3$

这些被称为伯恩斯坦多项式的基础

我们可以使用克朗函数来计算它们。

t = linspace (0, 1101);P = kron ((1 - t) ^ 2, P1) + kron (2 * (1 - t)。* t, P2) + kron (t ^ 2, P3);持有情节(P (1:), (2,:))

注意,得到的曲线从$P_1$开始,到$P_3$结束。在这中间,它向$P_2$移动,但没有到达。[Mike的解释结束]

这个克隆亚麻这个函数计算的是克罗内克张量积。然而,由于三年前在MATLAB中引入了隐式展开,这更容易通过简单的元素乘法计算。

t=t;P=((1-t)。^2.*P1)+(2.*1-t.*t.*P2)+(t.^2.*P3);

所以,这就是我需要为SVG路径中的每个“Q”命令执行的计算。不过,我不认为我需要101个点来绘制每个贝塞尔曲线段;让我们使用21个点。另外,我将把方向更改为列。

t=linspace(0,1,21)';

现在让我们建立起来xY基于每个SVG路径命令的向量。

x = 0 (0,1);y = 0 (0,1);x_current = 0;y_current = 0;对于k = 1:长度(clef_path)如果谱号路径(k)==""继续终止%获取后面的命令字符和数值向量%的命令。命令=extractBefore(谱号路径(k),2);余数=extractAfter(谱号路径(k),1);值=str2double(拆分(余数));开关命令情况下“M”%移动到某个位置。在向量中放置一个NaN值点。X = [X;南);Y = [Y;南);x_current =值(1);y_current =值(2);x(结束+ 1,- 1)= x_current;y(结束+ 1,- 1)= y_current;情况下“L”绘制从当前点到指定点的线段%点。x_current =值(1);y_current =值(2);x(结束+ 1,- 1)= x_current;y(结束+ 1,- 1)= y_current;情况下“Q”使用当前点绘制二次贝塞尔曲线段%另外两个点作为控制点。Pt1 = [x_current y_current];Pt2 = [values(1) values(2)];Pt3 = [values(3) values(4)];分= ((1 - t)。^2 * pt1 + (2*(1-t))*t .* pt2) +...(t ^ 2 * pt3);X = [X;分(:1)];Y = [Y;分(:,2)];x_current =值(3);y_current =值(4);情况下“Z”待办事项:曲线闭合逻辑终止终止

现在,让我们深呼吸,看看我们得到了什么!

情节(x, y)

太近了!但是倒过来,奇怪地被压扁了。这两个问题都很容易解决。

平等的ij

更近了!但是我们如何得到一个填充形状??那就是polyshape出现的原因。我们有xY包含四段的向量值。四个部分是外曲线,加上高音谱号中三个孔或空隙的轮廓。的构造函数polyshape知道如何处理这类数据。它可以自动计算出哪些曲线是外边界,哪些曲线是内边界。由此产生的polyshape对象可以很容易地绘制。

笔记:下面的重复点警告可以安全地忽略。如果我做的不是快速原型,我会更仔细地构建数据以避免它。

给你。您可以在MATLAB中绘制一个高音谱号。

你会用你新发现的力量做什么?

p=多形(x,y);绘图(p)轴平等的ij
警告:polyhape有重复的顶点、交叉或其他可能产生不准确或意外结果的不一致。已修改输入数据以创建定义良好的多晶体。
函数str placelabel (pt)%用于演示Bezier曲线绘制的代码的实用函数x=pt(1);y=pt(2);h=直线(x,y);h、 标记='.'; h=文本(x,y,str);h、 水平对齐=“中心”; h、 垂直排列=“底”;终止




发布与MATLAB®R2019b

|
  • 打印
  • 发送电子邮件

评论

要留下评论,请点击在这里登录到您的MathWorks帐户或创建一个新帐户。