既然我们理解了图像是由排列成网格的像素组成的,我们就需要一种一致的方法来引用任何特定像素的位置。图像坐标系就在此时发挥作用。可以将其想象为图像网格中每个像素都有一个地址。大多数图像处理库和计算机视觉应用使用的坐标系可能与你在数学课上学到的笛卡尔坐标系略有不同。这是标准约定:原点 (0,0): 起点位于图像的左上角。X轴: 此轴水平方向从左到右。x坐标表示列号。Y轴: 此轴垂直方向从上到下。y坐标表示行号。像素的位置通常由一对值 $(x, y)$ 指定,其中 $x$ 是水平位置(列),$y$ 是垂直位置(行),两者均从0开始。digraph G { rankdir=LR; node [shape=plaintext]; subgraph cluster_img { label = "图像网格(高 x 宽)"; style=dashed; bgcolor="#e9ecef"; origin [label="(0, 0)\n左上角", pos="0,3!", color="#f03e3e"]; point_x [label="(x, 0)", pos="1.5,3!"]; point_y [label="(0, y)", pos="0,1.5!"]; point_xy [label="(x, y)", pos="1.5,1.5!"]; max_x [label="(宽度-1, 0)\n右上角", pos="3,3!"]; max_y [label="(0, 高度-1)\n左下角", pos="0,0!"]; max_xy [label="(宽度-1, 高度-1)\n右下角", pos="3,0!"]; // 轴线的隐形节点 x_axis_start [pos="0,3!", shape=point, style=invis]; x_axis_end [label="X轴(列)", pos="3.5,3!", shape=point]; y_axis_start [pos="0,3!", shape=point, style=invis]; y_axis_end [label="Y轴(行)", pos="0,-0.5!", shape=point]; x_axis_start -> x_axis_end [arrowhead=vee, label=" → 宽度", fontcolor="#1c7ed6"]; y_axis_start -> y_axis_end [arrowhead=vee, label=" → 高度", fontcolor="#1c7ed6"]; // 网格线(可选,不显眼) edge [style=dotted, color="#adb5bd"]; point_x -> point_xy; point_y -> point_xy; } }图像坐标系的可视化表示。原点 (0,0) 位于左上角。x坐标向右增加,y坐标向下增加。像素索引访问像素时,需要使用其坐标。如果图像的宽度为 $W$、高度为 $H$,则像素的索引方式如下:x坐标范围从 $0$ 到 $W-1$。y坐标范围从 $0$ 到 $H-1$。例如,在一个宽度为800像素、高度为600像素的图像中 ($W=800, H=600$):左上角像素位于 $(x, y) = (0, 0)$。右上角像素位于 $(x, y) = (799, 0)$。左下角像素位于 $(x, y) = (0, 599)$。右下角像素位于 $(x, y) = (799, 599)$。中间某个像素可能位于 $(x, y) = (400, 300)$。编程约定注意事项尽管 $(x, y)$ 表示法(列,行)很常见,但许多编程库,特别是那些在Python中使用NumPy数组的库(如OpenCV),访问图像数据时,采用数组索引方式,遵循 (行, 列) 或 (y, x) 约定。例如,要访问坐标 $(x, y)$ 处的像素值,你可能会使用 pixel_value = image[y, x] 这样的代码。image[0, 0] 访问左上角像素。在我们800x600的例子中,image[599, 799] 访问右下角像素。坐标 $(x, y)$ 和数组索引 [y, x] 之间的这种差异,是初学者经常感到困惑的地方。请务必查阅你所用特定函数或库的文档,以确认其期望的顺序。在本课程中,讨论坐标时我们通常会使用 $(x, y)$,但在展示涉及数组访问的代码示例时,我们将使用与OpenCV和NumPy等库一致的 [y, x](行,列)格式。理解这个坐标系很重要,因为几乎所有的图像处理操作,从简单的裁剪到复杂的物体检测,都需要你准确地指定和访问像素位置。