OpenCV-获取图像上起点到终点间所有点坐标
最近做算法,需要用到检测图像上起点到终点间的图像灰度数据做筛选,就需要用到遍历图像起点到终点的所有像素点的坐标,在网上搜的算法要么讲的不清晰,要么算法有编译错误,所以自己重新写了一份算法,发布在这里,希望对大家有所帮助。
算法原理
算法原理比较简单,就是先得到起点到终点的直线方程,然后按照方程,从起点到终点将X方向的坐标带入方程,求解Y方向的坐标值,最后存储传出。
起点到终点的直线可以分为三种:水平直线,竖直直线,倾斜直线。对于水平直线和竖直直线,坐标点求解比较简单。
水平直线Y坐标相同,X坐标由起点到终点遍历++;
竖直直线X坐标相同,Y坐标由起点到终点遍历++;
倾斜直线,直线方程:Y = K * X + B,将起点坐标和终点坐标带入方程求解未知量K、B,得到直线方程,按照起点到终点遍历坐标点,将X方向的数据带入方程,可以求解Y方向坐标,然后传出数据即可。
算法代码
/*
函数功能:
计算起点到终点直线上的所有点坐标
输入:
cv::Point _iptPtStart 起点坐标
cv::Point _iptPTEnd 终点坐标
输出:
std::vector<cv::Point> &optVecPos 起点到终点所有点坐标
返回值:
无
*/
void CalculatePointPos(cv::Point _iptPtStart, cv::Point _iptPtEnd, std::vector<cv::Point> &_optVecPos)
{
// step0: 检测输入参数,输出参数重置
_optVecPos.clear();
if (_iptPtStart == _iptPtEnd)
{ // 如果输入和输出是同一个点,直接传出输出
_optVecPos.push_back(_iptPtStart);
return;
}
// step1: 判断点坐标,如果起点和终点的x或者y不相等,计算斜率;如果X或者Y坐标相等,直接计算结果
std::vector<cv::Point> tempVecPoint;
// 斜率截距式 直线公式:y = k * x + b
float fLineSlope = 0.; // 直线斜率
float fLineIntercept = 0.; // 直线截距
if (_iptPtStart.x == _iptPtEnd.x)
{ // 起点和终点为垂直
if (_iptPtStart.y < _iptPtEnd.y)
{
for (int y = _iptPtStart.y + 1; y < _iptPtEnd.y; y++)
{
tempVecPoint.push_back(cv::Point(_iptPtStart.x, y));
}
}
else
{
for (int y = _iptPtStart.y + 1; y > _iptPtEnd.y; y--)
{
tempVecPoint.push_back(cv::Point(_iptPtStart.x, y));
}
}
}
else if (_iptPtStart.y == _iptPtEnd.y)
{ // 起点和终点为水平
if (_iptPtStart.x < _iptPtEnd.x)
{
for (int x = _iptPtStart.x + 1; x < _iptPtEnd.x; x++)
{
tempVecPoint.push_back(cv::Point(x, _iptPtStart.y));
}
}
else
{
for (int x = _iptPtStart.x + 1; x > _iptPtEnd.x; x--)
{
tempVecPoint.push_back(cv::Point(x, _iptPtStart.y));
}
}
}
else
{ // 起点到终点是一条倾斜直线
fLineSlope = (float)(_iptPtEnd.y - _iptPtStart.y) / (_iptPtEnd.x - _iptPtStart.x);
fLineIntercept = _iptPtStart.y - _iptPtStart.x * fLineSlope; // 截距
// step2: 按照斜率和X坐标,每次步进为1,计算Y方向的坐标,四舍五入求Y点坐标
if (_iptPtStart.x < _iptPtEnd.x)
{ // 起点在终点的左侧
for (int x = _iptPtStart.x; x < _iptPtEnd.x; x++)
{
float y = x * fLineSlope + fLineIntercept;
tempVecPoint.push_back(cv::Point(x, int(y + 0.5)));
}
}
else
{ // 起点在终点的右侧
for (int x = _iptPtStart.x; x > _iptPtEnd.x; x--)
{
float y = x * fLineSlope + fLineIntercept;
tempVecPoint.push_back(cv::Point(x, int(y + 0.5)));
}
}
}
// step3: 输出点坐标
_optVecPos = tempVecPoint;
}
测试工程代码
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <string>
#include <vector>
#include <iostream>
using namespace std;
using namespace cv;
void CalculatePointPos(cv::Point _iptPtStart, cv::Point _iptPtEnd, std::vector<cv::Point> &_optVecPos);
int main()
{
cv::Mat imgSrc = cv::imread("test.bmp", 0); // 读取一张图片,也可以创建一个空图片
cv::Mat imgSrc; // 创建空白图片
imgSrc.create(cv::Size(1000, 1000), CV_8UC1);
cv::Mat DrawImg;
cv::cvtColor(imgSrc, DrawImg, cv::COLOR_GRAY2BGR);
cv::Point ptStart = cv::Point(800, 800);
cv::Point ptEnd = cv::Point(600, 600);
std::vector<cv::Point> vecPos;
CalculatePointPos(ptStart, ptEnd, vecPos);
cv::line(DrawImg, ptStart, ptEnd, cv::Scalar(0, 255, 0), 1);
for (int i = 0; i < vecPos.size(); i++)
{
cv::circle(DrawImg, cv::Point(vecPos[i]), 0.1, cv::Scalar(0, 0, 255));
}
return 0;
}
/*
函数功能:
计算起点到终点直线上的所有点坐标
输入:
cv::Point _iptPtStart 起点坐标
cv::Point _iptPTEnd 终点坐标
输出:
std::vector<cv::Point> &optVecPos 起点到终点所有点坐标
返回值:
无
*/
void CalculatePointPos(cv::Point _iptPtStart, cv::Point _iptPtEnd, std::vector<cv::Point> &_optVecPos)
{
// step0: 检测输入参数,输出参数重置
_optVecPos.clear();
if (_iptPtStart == _iptPtEnd)
{ // 如果输入和输出是同一个点,直接传出输出
_optVecPos.push_back(_iptPtStart);
return;
}
// step1: 判断点坐标,如果起点和终点的x或者y不相等,计算斜率;如果X或者Y坐标相等,直接计算结果
std::vector<cv::Point> tempVecPoint;
// 斜率截距式 直线公式:y = k * x + b
float fLineSlope = 0.; // 直线斜率
float fLineIntercept = 0.; // 直线截距
if (_iptPtStart.x == _iptPtEnd.x)
{ // 起点和终点为垂直
if (_iptPtStart.y < _iptPtEnd.y)
{
for (int y = _iptPtStart.y + 1; y < _iptPtEnd.y; y++)
{
tempVecPoint.push_back(cv::Point(_iptPtStart.x, y));
}
}
else
{
for (int y = _iptPtStart.y + 1; y > _iptPtEnd.y; y--)
{
tempVecPoint.push_back(cv::Point(_iptPtStart.x, y));
}
}
}
else if (_iptPtStart.y == _iptPtEnd.y)
{ // 起点和终点为水平
if (_iptPtStart.x < _iptPtEnd.x)
{
for (int x = _iptPtStart.x + 1; x < _iptPtEnd.x; x++)
{
tempVecPoint.push_back(cv::Point(x, _iptPtStart.y));
}
}
else
{
for (int x = _iptPtStart.x + 1; x > _iptPtEnd.x; x--)
{
tempVecPoint.push_back(cv::Point(x, _iptPtStart.y));
}
}
}
else
{ // 起点到终点是一条倾斜直线
fLineSlope = (float)(_iptPtEnd.y - _iptPtStart.y) / (_iptPtEnd.x - _iptPtStart.x);
fLineIntercept = _iptPtStart.y - _iptPtStart.x * fLineSlope; // 截距
// step2: 按照斜率和X坐标,每次步进为1,计算Y方向的坐标,四舍五入求Y点坐标
if (_iptPtStart.x < _iptPtEnd.x)
{ // 起点在终点的左侧
for (int x = _iptPtStart.x; x < _iptPtEnd.x; x++)
{
float y = x * fLineSlope + fLineIntercept;
tempVecPoint.push_back(cv::Point(x, int(y + 0.5)));
}
}
else
{ // 起点在终点的右侧
for (int x = _iptPtStart.x; x > _iptPtEnd.x; x--)
{
float y = x * fLineSlope + fLineIntercept;
tempVecPoint.push_back(cv::Point(x, int(y + 0.5)));
}
}
}
// step3: 输出点坐标
_optVecPos = tempVecPoint;
}
运行结果如下图所示:
欢迎交流学习,有不当之处欢迎批评指正。