OpenCV Image Processing
cv::Mat
Create a cv::Mat
In C++:
1
2
3
4
5
6
7
8
| // method 1
cv::Mat(another_mat);
// method 2
cv::Mat A = (cv::Mat_<uchar>(1,2)<<1,2); //CV_8U
// method 3
cv::Mat image = cv::Mat::zeros(height, width, CV_8UC1);
// method 4: fixed size matrix
cv::Matx31d point(k.pt.x, k.pt.y, 1.0);
|
- Watch out, if you pass another mat directly into
cv::Mat()
constructor, then the new mat will use the same pointer to the underlying data structure. To create a real copy, do cv::Mat.clone()
To copy an image, in python:
1
| output_img = cv2.cvtColor(input_img.copy(), cv2.COLOR_GRAY2RGB)
|
Check Image Size
1
2
3
4
5
6
7
| cv::Mat mat;
int rows = mat.rows();
int cols = mat.cols();
cv::Size s = mat.size();
std::cout<<"image size: "<<img.size<<std::endl;
rows = s.height;
cols = s.width;
|
Find min and max values
1
2
3
| double min, max;
cv::Mat mat
cv::minMaxIdx(mat, &min, &max); // Find min and max depth values
|
cv::Rect
cv::Rect{}
is a commonly used data structure for storing upper left and bottom right coords. In OpenCV, the bottom right corner is not included, the top left corner is included. It can be instantiated mostly in two ways:
1
2
3
4
5
6
7
8
9
| // Instantiating with top left corner and width, and height
auto rec1 = cv::Rect{1,2, 2,2};
std::cout<<"rec 1 br: "<<rec1.br()<<std::endl; // see (3,4)
// Instantiating with top left corner and bottom right corner
// y represents rows, x represents columns
auto rec = cv::Rect{cv::Point(0,0), cv::Point(1,2)};
std::cout<<"Rec 2 br: "<<rec.br()<<"width: "<<rec.width<<std::endl; // see (1,2), width is 1
// see 0, because row 1 and column 2 are not included
std::cout<<"contains: (1,2)"<<rec1.contains(cv::Point(1,2))<<std::endl;
|
- How to set a block of matrix to a certain value in
cv::Mat
1
2
3
4
| constexpr const int radius = 10;
cv::Mat image = cv::Mat::zeros((radius+1) * 2, (radius+1) * 2, CV_8UC1);
cv::Rect second_quadrant(0, 0, radius+1, radius+1);
image(second_quadrant) = 1;
|
1
2
3
4
5
6
7
8
9
10
11
| // integers
cv::Point shift_point(5, 10); // Example shift values
cv::Rect rect(0, 0, 100, 50); // Example rectangle
// float points
cv::Point2f shift_point(5.5f, 10.5f); // Example shift values
cv::Rect rect(0, 0, 100, 50); // Example rectangle
// Convert the rectangle's top-left to Point2f and shift.
rect + cv::Point(shift_point);
cv::Point(shift_point) + rect; // this does not hold
|
Random Access
cv::MatExpr
is an intermediate data type that does not have at()
. So for random access, one needs to:
1
2
3
4
5
6
| cv::Matx31d point(k.pt.x, k.pt.y, 1.0);
// BAD
auto rotated_pt = rotation_matrix * point;
// GOOD
cv::Mat rotated_pt = rotation_matrix * point;
return {rotated_pt.at<int>(0), rotated_pt.at<int>(1)};
|
- Basic Representation of points
cv::Point2f
is 32 bit single precision floating point
cv::Point2i
is 32-bit single precision integer.
- Note:
+
is NOT supported for cv::Point
types
- matrix operations:
- dot product:
a * b
- element-wise product:
cv::multiply(mat1, mat2, result);
- Rotation Vector and Matrix:
1
2
3
4
| cv::Mat r, R;
// cv::Mat_<double>(3,1) is a template function?
r = (cv::Mat_<double>(3,1) << 0, 0, CV_PI/2);
cv::Rodrigues(r, R);
|
- Append another matrix to an existing one
1
2
3
4
5
6
7
| if (!mat1.empty()){
cv::vconcat(mat1, mat2, result); // vertically append mat2 to mat 1 and store it in result
cv::hconcat(mat1, mat2, result); // horizontally append mat2 to mat 1 and store it in result
}
else{
result = mat2;
}
|
1
| - **IMPORTANT: `cv::vconcat(src, nsrc, dst)` requires `src` to be not empty!!**
|
- Calculate
atan2
: cv::fastAtan2(y, x)
. Its accuracy is about 0.3 deg
Integral Image
cv::integral
, which sums up all pixels from the top left corner to the given pixel. The result integral image will have a size [column+1, row+1]
, and the value at (y,x) represents the sum from [0,0] to [y-1, x-1]. Left and top borders of the integral image are padded with 0.
\[sum(X,Y) = \sum_{x<X, y<Y} I(x,y)\]
1
| - `cv::integral()` supports only `cv_8U`, `CV_32S` and `cv_32U`. So use `img.convertTo(img, cv_8U)` if necessary
|
OpenCV Errors
Unsupported combination of source format (=4), and buffer format (=5) in function 'getLinearRowFilter'
means the input type needs to be changed (e.g., int -> float)
Common Conversions
- ROS
geometry_msgs::Pose
$->$ Eigen::Quaterniond
1
2
| geometry_msgs::Pose p;
Eigen::Quaterniond q(pose.rotation());
|
Tricks
- Print a vector on one line
1
2
3
4
5
| // Define the IO format to print on one line
Eigen::IOFormat eigen_1_line_fmt(Eigen::StreamPrecision, Eigen::DontAlignCols, ", ", ", ", "", "", " [", "] ");
// Print the vector using the defined format
std::cout << "p1: " << p1.format(eigen_1_line_fmt) << std::endl;
|