我有一个带有实值顶点(x0,y0)
,(x1,y1)
,(x2,y2)
,(x3,y3)
的矩形,它可以在平面中以任意角度定向。我正在寻找一种有效的方法来查找(或迭代)所有像素(即1x1方格),这些像素至少部分位于此矩形内。
对于正交定向的矩形执行此操作非常简单,检查矩形内是否有任何特定像素也很简单。我可以检查矩形边界框内的每个像素,但在最坏的情况下,当只有O(n)在目标矩形内时,我会检查O(n ^ 2)像素。 (这是当目标矩形为45度并且非常长而窄时。)
答案 0 :(得分:2)
您可以计算x方向的范围(最小x坐标的底限,最大x坐标的上限)。对于该范围内的每个x,您可以计算y方向上的范围。在一般情况下,您需要考虑几种不同的情况,具体取决于矩形的方向。
本质上,你有一个最左边的点,一个最右边的点,一个上点和一个下点。 y1
将从最左边开始,从最低点开始,到最右边点结束。 y2
将改为高点。
要包含所有触摸像素,我们需要在所有方向上看半个像素。我选择使用每个像素的中心作为坐标。这样可以让您更加自然地看到最终图像。
以下是一些用于演示的F#代码:
let plot_rectangle p0 p1 p2 p3 =
seq {
// sort by x-coordinate
let points = List.sortBy fst [p0; p1; p2; p3]
let pLeft, pMid1, pMid2, pRight =
points.[0], points.[1], points.[2], points.[3]
// sort 2 middle points by y-coordinate
let points = List.sortBy snd [pMid1; pMid2]
let pBottom, pTop = points.[0], points.[1]
// Easier access to the coordinates
let pLeftX, pLeftY = pLeft
let pRightX, pRightY = pRight
let pBottomX, pBottomY = pBottom
let pTopX, pTopY = pTop
let pMid1X, pMid1Y = pMid1
let pMid2X, pMid2Y = pMid2
// Function: Get the minimum Y for a given X
let getMinY x0 y0 x1 y1 x =
let slope = (y1 - y0)/(x1 - x0)
// Step half a pixel left or right, but not too far
if slope >= 0.0 then
let xl = max x0 (x - 0.5)
y0 + slope * (xl - x0)
|> round
|> int
else
let xr = min x1 (x + 0.5)
y0 + slope * (xr - x0)
|> round
|> int
// Function: Get the maximum Y for a given X
let getMaxY x0 y0 x1 y1 x =
let slope = (y1 - y0)/(x1 - x0)
// Step half a pixel left or right, but not too far
if slope >= 0.0 then
let xr = min x1 (x + 0.5)
y0 + slope * (xr - x0)
|> round
|> int
else
let xl = max x0 (x - 0.5)
y0 + slope * (xl - x0)
|> round
|> int
let x1 = int (pLeftX + 0.5)
let x2 = int (pRightX + 0.5)
for x = x1 to x2 do
let xf = float x
if xf < pMid1X then
// Phase I: Left to Top and Bottom
// Line from pLeft to pBottom
let y1 = getMinY pLeftX pLeftY pBottomX pBottomY xf
// Line from pLeft to pTop
let y2 = getMaxY pLeftX pLeftY pTopX pTopY xf
for y = y1 to y2 do
yield (x, y)
elif xf < pMid2X && pMid1Y < pMid2Y then
// Phase IIa: left/bottom --> top/right
// Line from pBottom to pRight
let y1 = getMinY pBottomX pBottomY pRightX pRightY xf
// Line from pLeft to pTop (still)
let y2 = getMaxY pLeftX pLeftY pTopX pTopY xf
for y = y1 to y2 do
yield (x, y)
elif xf < pMid2X && pMid1Y >= pMid2Y then
// Phase IIb: left/top --> bottom/right
// Line from pLeft to pBottom (still)
let y1 = getMinY pLeftX pLeftY pBottomX pBottomY xf
// Line from pTop to pRight
let y2 = getMaxY pTopX pTopY pRightX pRightY xf
for y = y1 to y2 do
yield (x, y)
else
// Phase III: bottom/top --> right
// Line from pBottom to pRight
let y1 = getMinY pBottomX pBottomY pRightX pRightY xf
// Line from pTop to pRight
let y2 = getMaxY pTopX pTopY pRightX pRightY xf
for y = y1 to y2 do
yield (x, y)
}
示例:
答案 1 :(得分:1)
你能用格雷厄姆扫描吗? 您可以使用5个点(像素+ 4个顶点)的集合,然后检查4个顶点是否定义凸包的边界。这将是最坏的O(n log n),这对于大n的n ^ 2是显着的改进。 或者,二维范围树可能就足够了,但我认为这仍然是n log n
编辑:
实际上,您可以使用4个顶点之间的角度来创建4个“范围”,其中可能存在像素,然后只取这4个范围的交点。这将是一个恒定的时间操作,并检查像素是否在此范围内也是恒定时间 - 只需将它与每个顶点的角度与上述角度组进行比较。
作为另一种选择,使用4条边界线(相邻顶点之间的线)并在它们之间“行走”。一旦你击中了线,任何向下的点都不会位于这个边界内,等等。这是矩形内像素数量的O(n),应该可以通过简单的广度优先搜索来轻松解决
答案 2 :(得分:0)
这是基于MizardX's answer的一些Python代码,它完全符合我的要求:
#!/usr/bin/python
import math
def minY(x0, y0, x1, y1, x):
if x0 == x1:
# vertical line, y0 is lowest
return int(math.floor(y0))
m = (y1 - y0)/(x1 - x0)
if m >= 0.0:
# lowest point is at left edge of pixel column
return int(math.floor(y0 + m*(x - x0)))
else:
# lowest point is at right edge of pixel column
return int(math.floor(y0 + m*((x + 1.0) - x0)))
def maxY(x0, y0, x1, y1, x):
if x0 == x1:
# vertical line, y1 is highest
return int(math.ceil(y1))
m = (y1 - y0)/(x1 - x0)
if m >= 0.0:
# highest point is at right edge of pixel column
return int(math.ceil(y0 + m*((x + 1.0) - x0)))
else:
# highest point is at left edge of pixel column
return int(math.ceil(y0 + m*(x - x0)))
# view_bl, view_tl, view_tr, view_br are the corners of the rectangle
view_bl = (0.16511327500123524, 1.2460844930844697)
view_tl = (1.6091354363329917, 0.6492542948962687)
view_tr = (1.1615128085358943, -0.4337622756706583)
view_br = (-0.2825093527958621, 0.16306792251754265)
pixels = []
# find l,r,t,b,m1,m2
view = [ view_bl, view_tl, view_tr, view_br ]
l, m1, m2, r = sorted(view, key=lambda p: (p[0],p[1]))
b, t = sorted([m1, m2], key=lambda p: (p[1],p[0]))
lx, ly = l
rx, ry = r
bx, by = b
tx, ty = t
m1x, m1y = m1
m2x, m2y = m2
xmin = 0
ymin = 0
xmax = 10
ymax = 10
# outward-rounded integer bounds
# note that we're clamping the area of interest to (xmin,ymin)-(xmax,ymax)
lxi = max(int(math.floor(lx)), xmin)
rxi = min(int(math.ceil(rx)), xmax)
byi = max(int(math.floor(by)), ymin)
tyi = min(int(math.ceil(ty)), ymax)
x1 = lxi
x2 = rxi
for x in range(x1, x2):
xf = float(x)
if xf < m1x:
# Phase I: left to top and bottom
y1 = minY(lx, ly, bx, by, xf)
y2 = maxY(lx, ly, tx, ty, xf)
elif xf < m2x:
if m1y < m2y:
# Phase IIa: left/bottom --> top/right
y1 = minY(bx, by, rx, ry, xf)
y2 = maxY(lx, ly, tx, ty, xf)
else:
# Phase IIb: left/top --> bottom/right
y1 = minY(lx, ly, bx, by, xf)
y2 = maxY(tx, ty, rx, ry, xf)
else:
# Phase III: bottom/top --> right
y1 = minY(bx, by, rx, ry, xf)
y2 = maxY(tx, ty, rx, ry, xf)
y1 = max(y1, byi)
y2 = min(y2, tyi)
for y in range(y1, y2):
pixels.append((x,y))
print pixels
输出:
[(0, 0), (0, 1), (1, 0)]