OpenCV+python:自 3.4.2 以来的 HoughLines 累加器访问
OpenCV+python: HoughLines accumulator access since 3.4.2
在 OpenCV 3.4.2 中,添加了 return 由 HoughLines() 编辑的每一行 return 的投票数(累加器值)的选项。在 python 中,这似乎得到支持,并在我的 OpenCV 安装的 python 文档字符串中阅读:
"Each line is represented by a 2 or 3 element vector (ρ, θ) or (ρ, θ, votes) ."
它也包含在 docs 中(有一些损坏的格式)。
但是我找不到 return 3 元素选项 (ρ, θ, 在python.中投票)
这是演示问题的代码:
import numpy as np
import cv2
print('OpenCV should be at least 3.4.2 to test: ', cv2.__version__)
image = np.eye(10, dtype='uint8')
lines = cv2.HoughLines(image, 1, np.pi/180, 5)
print('(number of lines, 1, output vector dimension): ', lines.shape)
print(lines)
产出
OpenCV should be at least 3.4.2 to test: 3.4.2
(number of lines, 1, output vector dimension): (3, 1, 2)
[[[ 0. 2.3212879]]
[[ 1. 2.2340214]]
[[-1. 2.4609141]]]
所需的行为是一个额外的列,其中包含每行收到的票数。有了投票值,可以应用比标准阈值更高级的选项,因此它经常在 SE (here, here, here and here) 上被请求和询问,有时与 HoughCircles() 等效。但问题和答案(如修改源码、重新编译等)均为官方添加前的问题,不适用于当前情况。
从 vanilla OpenCV 3.4.3 开始,您无法使用 Python 中的此功能。
它在 C++ 中的工作原理
首先在implementation of HoughLines
中,我们可以看到选择输出数组类型的代码lines
:
int type = CV_32FC2;
if (lines.fixedType())
{
type = lines.type();
CV_CheckType(type, type == CV_32FC2 || type == CV_32FC3, "Wrong type of output lines");
}
然后我们可以在填充lines
时看到implementation of HoughLinesStandard
中使用的这个参数:
if (type == CV_32FC2)
{
_lines.at<Vec2f>(i) = Vec2f(line.rho, line.angle);
}
else
{
CV_DbgAssert(type == CV_32FC3);
_lines.at<Vec3f>(i) = Vec3f(line.rho, line.angle, (float)accum[idx]);
}
类似代码可见in HoughLinesSDiv
.
基于此,我们需要传入一个_OutputArray
,即固定类型,并在3个通道中存储32bit的浮点数。如何制作固定类型(但不是固定大小,因为算法需要能够调整它的大小)_OutputArray
?我们再看看implementation:
- 泛型
cv::Mat
不是固定类型,cv::UMat
也不是
- 一个选项是
std::vector<cv::Vec3f>
- 另一种选择是
cv::Mat3f
(即cv::Matx<_Tp, m, n>
)
示例代码:
#include <opencv2/opencv.hpp>
int main()
{
cv::Mat image(cv::Mat::eye(10, 10, CV_8UC1) * 255);
cv::Mat2f lines2;
cv::HoughLines(image, lines2, 1, CV_PI / 180, 4); // runs the actual detection
std::cout << lines2 << "\n";
cv::Mat3f lines3;;
cv::HoughLines(image, lines3, 1, CV_PI / 180, 4); // runs the actual detection
std::cout << lines3 << "\n";
return 0;
}
控制台输出:
[0, 2.3212879;
1, 2.2340214;
-1, 2.4609141]
[0, 2.3212879, 10;
1, 2.2340214, 6;
-1, 2.4609141, 6]
Python 包装器的工作原理
让我们看一下包装 HoughLines
函数的自动生成的代码:
static PyObject* pyopencv_cv_HoughLines(PyObject* , PyObject* args, PyObject* kw)
{
using namespace cv;
{
PyObject* pyobj_image = NULL;
Mat image;
PyObject* pyobj_lines = NULL;
Mat lines;
double rho=0;
double theta=0;
int threshold=0;
double srn=0;
double stn=0;
double min_theta=0;
double max_theta=CV_PI;
const char* keywords[] = { "image", "rho", "theta", "threshold", "lines", "srn", "stn", "min_theta", "max_theta", NULL };
if( PyArg_ParseTupleAndKeywords(args, kw, "Oddi|Odddd:HoughLines", (char**)keywords, &pyobj_image, &rho, &theta, &threshold, &pyobj_lines, &srn, &stn, &min_theta, &max_theta) &&
pyopencv_to(pyobj_image, image, ArgInfo("image", 0)) &&
pyopencv_to(pyobj_lines, lines, ArgInfo("lines", 1)) )
{
ERRWRAP2(cv::HoughLines(image, lines, rho, theta, threshold, srn, stn, min_theta, max_theta));
return pyopencv_from(lines);
}
}
PyErr_Clear();
// Similar snippet handling UMat...
return NULL;
}
总而言之,它尝试将 lines
参数中传递的对象转换为 cv::Mat
,然后调用 cv::HoughLines
并将 cv::Mat
作为输出参数。 (如果失败,那么它会用 cv::UMat
尝试同样的事情)不幸的是,这意味着没有办法给 cv::HoughLines
一个固定的类型 lines
,所以从 3.4.3 开始这个Python.
无法访问功能
解决方案
据我所知,唯一的解决方案是修改 OpenCV 源代码并重新构建。
快速破解
这很简单,编辑 implementation of cv::HoughLines
并将默认类型更改为 CV_32FC3
:
int type = CV_32FC3;
然而,这意味着您将始终获得选票(这也意味着 OpenCL 优化(如果存在)将不会被使用)。
更好的补丁
添加一个可选的布尔参数 return_votes
,默认值为 false
。修改代码,当 return_votes
为 true
时,type
被强制为 CV_32FC3
.
CV_EXPORTS_W void HoughLines( InputArray image, OutputArray lines,
double rho, double theta, int threshold,
double srn = 0, double stn = 0,
double min_theta = 0, double max_theta = CV_PI,
bool return_votes = false );
void HoughLines( InputArray _image, OutputArray lines,
double rho, double theta, int threshold,
double srn, double stn, double min_theta, double max_theta,
bool return_votes )
{
CV_INSTRUMENT_REGION()
int type = CV_32FC2;
if (return_votes)
{
type = CV_32FC3;
}
else if (lines.fixedType())
{
type = lines.type();
CV_CheckType(type, type == CV_32FC2 || type == CV_32FC3, "Wrong type of output lines");
}
// the rest...
有一个新的 python 绑定 (opencv 4.5.1)
在 OpenCV 3.4.2 中,添加了 return 由 HoughLines() 编辑的每一行 return 的投票数(累加器值)的选项。在 python 中,这似乎得到支持,并在我的 OpenCV 安装的 python 文档字符串中阅读:
"Each line is represented by a 2 or 3 element vector (ρ, θ) or (ρ, θ, votes) ."
它也包含在 docs 中(有一些损坏的格式)。 但是我找不到 return 3 元素选项 (ρ, θ, 在python.中投票) 这是演示问题的代码:
import numpy as np
import cv2
print('OpenCV should be at least 3.4.2 to test: ', cv2.__version__)
image = np.eye(10, dtype='uint8')
lines = cv2.HoughLines(image, 1, np.pi/180, 5)
print('(number of lines, 1, output vector dimension): ', lines.shape)
print(lines)
产出
OpenCV should be at least 3.4.2 to test: 3.4.2
(number of lines, 1, output vector dimension): (3, 1, 2)
[[[ 0. 2.3212879]]
[[ 1. 2.2340214]]
[[-1. 2.4609141]]]
所需的行为是一个额外的列,其中包含每行收到的票数。有了投票值,可以应用比标准阈值更高级的选项,因此它经常在 SE (here, here, here and here) 上被请求和询问,有时与 HoughCircles() 等效。但问题和答案(如修改源码、重新编译等)均为官方添加前的问题,不适用于当前情况。
从 vanilla OpenCV 3.4.3 开始,您无法使用 Python 中的此功能。
它在 C++ 中的工作原理
首先在implementation of HoughLines
中,我们可以看到选择输出数组类型的代码lines
:
int type = CV_32FC2;
if (lines.fixedType())
{
type = lines.type();
CV_CheckType(type, type == CV_32FC2 || type == CV_32FC3, "Wrong type of output lines");
}
然后我们可以在填充lines
时看到implementation of HoughLinesStandard
中使用的这个参数:
if (type == CV_32FC2)
{
_lines.at<Vec2f>(i) = Vec2f(line.rho, line.angle);
}
else
{
CV_DbgAssert(type == CV_32FC3);
_lines.at<Vec3f>(i) = Vec3f(line.rho, line.angle, (float)accum[idx]);
}
类似代码可见in HoughLinesSDiv
.
基于此,我们需要传入一个_OutputArray
,即固定类型,并在3个通道中存储32bit的浮点数。如何制作固定类型(但不是固定大小,因为算法需要能够调整它的大小)_OutputArray
?我们再看看implementation:
- 泛型
cv::Mat
不是固定类型,cv::UMat
也不是
- 一个选项是
std::vector<cv::Vec3f>
- 另一种选择是
cv::Mat3f
(即cv::Matx<_Tp, m, n>
)
示例代码:
#include <opencv2/opencv.hpp>
int main()
{
cv::Mat image(cv::Mat::eye(10, 10, CV_8UC1) * 255);
cv::Mat2f lines2;
cv::HoughLines(image, lines2, 1, CV_PI / 180, 4); // runs the actual detection
std::cout << lines2 << "\n";
cv::Mat3f lines3;;
cv::HoughLines(image, lines3, 1, CV_PI / 180, 4); // runs the actual detection
std::cout << lines3 << "\n";
return 0;
}
控制台输出:
[0, 2.3212879;
1, 2.2340214;
-1, 2.4609141]
[0, 2.3212879, 10;
1, 2.2340214, 6;
-1, 2.4609141, 6]
Python 包装器的工作原理
让我们看一下包装 HoughLines
函数的自动生成的代码:
static PyObject* pyopencv_cv_HoughLines(PyObject* , PyObject* args, PyObject* kw)
{
using namespace cv;
{
PyObject* pyobj_image = NULL;
Mat image;
PyObject* pyobj_lines = NULL;
Mat lines;
double rho=0;
double theta=0;
int threshold=0;
double srn=0;
double stn=0;
double min_theta=0;
double max_theta=CV_PI;
const char* keywords[] = { "image", "rho", "theta", "threshold", "lines", "srn", "stn", "min_theta", "max_theta", NULL };
if( PyArg_ParseTupleAndKeywords(args, kw, "Oddi|Odddd:HoughLines", (char**)keywords, &pyobj_image, &rho, &theta, &threshold, &pyobj_lines, &srn, &stn, &min_theta, &max_theta) &&
pyopencv_to(pyobj_image, image, ArgInfo("image", 0)) &&
pyopencv_to(pyobj_lines, lines, ArgInfo("lines", 1)) )
{
ERRWRAP2(cv::HoughLines(image, lines, rho, theta, threshold, srn, stn, min_theta, max_theta));
return pyopencv_from(lines);
}
}
PyErr_Clear();
// Similar snippet handling UMat...
return NULL;
}
总而言之,它尝试将 lines
参数中传递的对象转换为 cv::Mat
,然后调用 cv::HoughLines
并将 cv::Mat
作为输出参数。 (如果失败,那么它会用 cv::UMat
尝试同样的事情)不幸的是,这意味着没有办法给 cv::HoughLines
一个固定的类型 lines
,所以从 3.4.3 开始这个Python.
解决方案
据我所知,唯一的解决方案是修改 OpenCV 源代码并重新构建。
快速破解
这很简单,编辑 implementation of cv::HoughLines
并将默认类型更改为 CV_32FC3
:
int type = CV_32FC3;
然而,这意味着您将始终获得选票(这也意味着 OpenCL 优化(如果存在)将不会被使用)。
更好的补丁
添加一个可选的布尔参数 return_votes
,默认值为 false
。修改代码,当 return_votes
为 true
时,type
被强制为 CV_32FC3
.
CV_EXPORTS_W void HoughLines( InputArray image, OutputArray lines,
double rho, double theta, int threshold,
double srn = 0, double stn = 0,
double min_theta = 0, double max_theta = CV_PI,
bool return_votes = false );
void HoughLines( InputArray _image, OutputArray lines,
double rho, double theta, int threshold,
double srn, double stn, double min_theta, double max_theta,
bool return_votes )
{
CV_INSTRUMENT_REGION()
int type = CV_32FC2;
if (return_votes)
{
type = CV_32FC3;
}
else if (lines.fixedType())
{
type = lines.type();
CV_CheckType(type, type == CV_32FC2 || type == CV_32FC3, "Wrong type of output lines");
}
// the rest...
有一个新的 python 绑定 (opencv 4.5.1)