向量延长线上的像素扫描
扫描原理:
1、当向量角度在45~135、225~315之间时,按X轴方向进行扫描,即:已知X轴坐标,求Y轴的值,从而得到延长线上的目标坐标。
2、当向量角度在0~45、315~360、135~225之间时,按Y轴方向进行扫描,即:已知Y轴坐标,求X轴的值,从而得到延长线上的目标坐标。

单线扫描效果:

360度任意向量扫描效果:

C#演示代码如下:
using OpenCvSharp;
using System;
namespace demo
{
internal class Program
{
// 计算顺时针角度:0~360
private static double VectorLine(Vec4i A, Vec4i B)
{
Point P11 = new Point(A[0], A[1]);
Point P12 = new Point(A[2], A[3]);
Point P21 = new Point(B[0], B[1]);
Point P22 = new Point(B[2], B[3]);
double ax = P11.X - P12.X;
double ay = P11.Y - P12.Y;
double bx = P21.X - P22.X;
double by = P21.Y - P22.Y;
double x = Math.Sqrt(ax * ax + ay * ay);
double y = Math.Sqrt(bx * bx + by * by);
double ang = 180.0 * Math.Acos((ax * bx + ay * by) / (x * y)) / Math.PI;
if (B[2] < B[0]) ang = 360 - ang;
return 360 - ang;
}
/// <summary>
/// 绘制沿途坐标点
/// </summary>
/// <param name="image"></param>
/// <param name="start"></param>
/// <param name="end"></param>
private static void LongLine(Mat image, Point start, Point end)
{
// 起点和终点
Cv2.Circle(image, start, 4, new Scalar(0, 255, 0), 2);
Cv2.Circle(image, end, 4, new Scalar(0, 0, 255), 2);
// 垂直基线
Vec4i BaseLine = new Vec4i(image.Width / 2, image.Height / 2, image.Width / 2, image.Height / 2 + 300);
// 需要求角度的线
Vec4i EndLine = new Vec4i(start.X, start.Y, end.X, end.Y);
// 计算夹角
double Ang = VectorLine(BaseLine, EndLine);
int difX = end.X - start.X;
// 沿X轴扫描
if (((45.0 <= Ang && Ang <= 135.0) || (225.0 <= Ang && Ang <= 315.0)) && difX != 0)
{
decimal K = (decimal)(end.Y - start.Y) / (decimal)difX;
int x = start.X;
while (x > 0 && x < image.Width)
{
x = start.X > end.X ? x - 1 : x + 1;
int y = difX == 0 ? start.Y : (int)(K * (x - start.X) + start.Y);
if (y <= 0 || y >= image.Height || x <= 0 || x >= image.Width)
break;
// 像素点间隔绘制,提高绘制速度
if (x % 2 == 0)
continue;
Cv2.Circle(image, new Point(x, y), 1, new Scalar(255, 255, 255), -1);
}
}
// 沿Y轴扫描
else
{
int y = start.Y;
decimal K = difX == 0 ? 0 : (decimal)(end.Y - start.Y) / (decimal)difX;
while (y > 0 && y < image.Height)
{
y = start.Y > end.Y ? y - 1 : y + 1;
int x = K == 0 ? start.X : (int)((y - start.Y) / K + start.X);
if (y <= 0 || y >= image.Height || x <= 0 || x >= image.Width)
break;
// 像素点间隔绘制,提高绘制速度
if (y % 2 == 0)
continue;
Cv2.Circle(image, new Point(x, y), 1, new Scalar(0, 255, 255), -1);
}
}
Mat endImg = new Mat();
Cv2.Resize(image, endImg, new Size(image.Cols * 0.5, image.Rows * 0.5), 0, 0, InterpolationFlags.Area);
Cv2.ImShow("image", endImg);
Cv2.WaitKey(1);
}
static void Main(string[] args)
{
Mat image = new Mat(new Size(800, 800), MatType.CV_8UC3, new Scalar(0, 0, 0));
Point start = new Point(image.Width / 2, image.Height / 2);
// 循环目标坐标
for (int i = 200; i < 600; i++)
{
Point end1 = new Point(i, 200);
LongLine(image, start, end1);
Point end2 = new Point(i, 600);
LongLine(image, start, end2);
Point end3 = new Point(200, i);
LongLine(image, start, end3);
Point end4 = new Point(600, i);
LongLine(image, start, end4);
}
}
}
}
Python版本:
import numpy as np
import math
import cv2
# 计算顺时针角度:0~360
def VectorLine(Line1, Line2):
P11 = (Line1[0], Line1[1])
P12 = (Line1[2], Line1[3])
P21 = (Line2[0], Line2[1])
P22 = (Line2[2], Line2[3])
ax = P11[0] - P12[0]
ay = P11[1] - P12[1]
bx = P21[0] - P22[0]
by = P21[1] - P22[1]
x = math.sqrt(ax * ax + ay * ay)
y = math.sqrt(bx * bx + by * by)
ang = (180.0 * math.acos((ax * bx + ay * by) / (x * y))) / math.pi
if Line2[2] < Line2[0]:
ang = 360 - ang
return 360 - ang
# 绘制沿途坐标点
def LongLine(image, start, end):
imgH = image.shape[0]
imgW = image.shape[1]
# 起点和终点
image = cv2.circle(image, (int(start[0]), int(start[1])), 4, (255, 0, 0), 2, 8, 0)
image = cv2.circle(image, (int(end[0]), int(end[1])), 4, (0, 255, 0), 2, 8, 0)
# 垂直基线
BaseLine = (imgW / 2, imgH / 2, imgW / 2, imgH / 2 + 300)
# 需要求角度的线
EndLine = (start[0], start[1], end[0], end[1])
# 计算夹角
Ang = VectorLine(BaseLine, EndLine)
difX = end[0] - start[0]
# 沿X轴扫描
if ((45.0 <= Ang <= 135.0) or (225.0 <= Ang <= 315.0)) and difX != 0:
K = (end[1] - start[1]) / difX
x = start[0]
while 0 < x < imgW:
x = x - 1 if start[0] > end[0] else x + 1
y = start[1] if difX == 0 else int(K * (x - start[0]) + start[1])
if y <= 0 or y >= imgH or x <= 0 or x >= imgW:
break
if x % 2 == 0:
continue
image = cv2.circle(image, (int(x), int(y)), 1, (255, 255, 255), 1, 8, 0)
# 沿Y轴扫描
else:
y = start[1]
K = 0 if difX == 0 else (end[1] - start[1]) / difX
while 0 < y < imgH:
y = y - 1 if start[1] > end[1] else y + 1
x = start[0] if K == 0 else int((y - start[1]) / K + start[0])
if y <= 0 or y >= imgH or x <= 0 or x >= imgW:
break
if y % 2 == 0:
continue
image = cv2.circle(image, (int(x), int(y)), 1, (0, 255, 255), 1, 8, 0)
cv2.imshow("image", image)
cv2.waitKey(1)
if __name__ == '__main__':
mat = np.ones((800, 800, 3), np.uint8) * 255
mat[:] = 0
s = (mat.shape[1] / 2, mat.shape[0] / 2)
for i in range(200, 600):
LongLine(mat, s, (i, 200))
LongLine(mat, s, (i, 600))
LongLine(mat, s, (200, i))
LongLine(mat, s, (600, i))
cv2.waitKey(0)