英文:
how to color in outlined text in opencv C++
问题
我想给图像中显示的字符上色。我尝试过使用 findContours()
来获取字符的轮廓。然后我循环遍历图像的每一个点,使用 pointPolygonTest()
检查点是否位于字符轮廓内。如果为真,就给该点上色。然而,pointPolygonTest()
似乎始终返回假,因为最终没有添加颜色。
英文:
I would like give color to the character shown in the image.enter image description here
Something I've tried is using findcontours() to gather contours of the characters. Then I loop through every single point of the image to check if a point lies in the character contours using pointpolygontest(). If true, assign color to the point. However, pointpolygontest() seems to return false all the time because no color was added after all.
答案1
得分: 3
以下是您要翻译的部分:
-
If you think of the lines in your input image as connected components, the areas we want to fill are holes in the connected components. Specifically they are the top-level holes; i.e., we do not want to fill in features like the internal lines in the letter O or D, etc. Our general strategy will be to find these holes with
cv::findContours
and then fill them withcv::floodFill
. -
findContours
expects one channel input that is white on a black background. Your input has three channels, is black on white, and is anti-aliased, so we must clean up the image with various color conversion, thresholding, and inversion calls. Once we have a binary image that is white on black, we callfindContours
inRETR_TREE
mode. We need the full hierarchy tree because we need to find the top-level holes.findContours
returns hierarchy information as a vector of quadruples listing the indices of the nth contour's previous, next, first child, and parent contours. To find the "top-level holes" we want any contour that has a parent but that does not have a grandparent. -
To actually fill in these holes we are going to use
floodFill
but that means we need a seed that is definitely in each area we want to fill. Because the contour coordinates will be lists of "on" pixel locations, a topmost point (x,y) on some hole contour will be above the actual hole, but (x,y+1) will be a suitable seed for flood-filling. -
We then convert the uninverted binary image back to three channels and flood-fill at our seed locations in red. How you prepare your output here depends on your actual use case. In this case, I am making sample output for a StackOverflow answer so I do something simple. You may want to preserve the anti-aliasing in the original image, etc., and do something more complicated, but the following generates an aliased image as a proof-of-concept:
这是您要翻译的代码部分:
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <limits>
cv::Point find_top_hole_point(const std::vector<cv::Point>& hole_pts) {
cv::Point top_point = { 0, std::numeric_limits<int>::max() };
for (const auto& pt : hole_pts) {
if (pt.y < top_point.y) {
top_point = pt;
}
}
return top_point + cv::Point(0, 1);
}
std::vector<cv::Point> get_point_in_holes(cv::Mat img) {
// invert for findContours call
cv::Mat work;
cv::bitwise_not(img, work);
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(work, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
std::vector<cv::Point> hole_points;
for (int i = 0; i < static_cast<int>(contours.size()); ++i) {
auto parent = hierarchy[i][3];
// toplevel holes have a parent but no grandparent...
if ((parent >= 0) && (hierarchy[parent][3] < 0)) {
hole_points.push_back(find_top_hole_point(contours[i]));
}
}
return hole_points;
}
int main() {
cv::Mat img = cv::imread("C:\\test\\alphabet.png");
// convert to 3-channel gray scale
cv::Mat work;
cv::cvtColor(img, work, cv::COLOR_BGR2GRAY);
// convert to 1-channel black and white
cv::threshold(work, work, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
// get seeds for flood-filling
// (topmost points in top-level holes)
auto hole_points = get_point_in_holes(work);
// convert back to 3-channel color;
cv::cvtColor(work, work, cv::COLOR_GRAY2BGR);
// fill top-level holes with red
for (const auto& pt : hole_points) {
cv::floodFill(work, pt, cv::Scalar(0, 0, 255));
}
cv::imshow("output_image", work);
cv::waitKey(0);
}
希望这对您有所帮助。
英文:
If you think of the lines in your input image as connected components, the areas we want to fill are holes in the connected components. Specifically they are the top-level holes; i.e., we do not want to fill in features like the internal lines in the letter O or D, etc. Our general strategy will be to find these holes with cv::findContours
and then fill them with cv::floodFill
.
findContours
expects one channel input that is white on a black background. Your input has three channels, is black on white, and is anti-aliased, so we must clean up the image with various color conversion, thresholding, and inversion calls. Once we have a binary image that is white on black, we call findContours
in RETR_TREE
mode. We need the full hierarchy tree because we need to find the top-level holes. findContours
returns hierarchy information as a vector of quadruples listing the indices of the nth contour's previous, next, first child, and parent contours. To find the "top-level holes" we want any contour that has a parent but that does not have a grandparent.
To actually fill in these holes we are going to use floodFill
but that means we need a seed that is definitely in each area we want to fill. Because the contour coordinates will be lists of "on" pixel locations, a topmost point (x,y) on some hole contour will be above the actual hole, but (x,y+1) will be a suitable seed for flood-filling.
We then convert the uninverted binary image back to three channels and flood-fill at our seed locations in red. How you prepare your output here depends on your actual use case. In this case I am making sample output for a StackOverflow answer so I do something simple. You may want to preserve the anti-aliasing in the original image, etc., and do something more complicated, but the following generates an aliased image as a proof-of-concept:
Code below:
<!-- language: C++ -->
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <limits>
cv::Point find_top_hole_point(const std::vector<cv::Point>& hole_pts) {
cv::Point top_point = { 0,std::numeric_limits<int>::max() };
for (const auto& pt : hole_pts) {
if (pt.y < top_point.y) {
top_point = pt;
}
}
return top_point + cv::Point(0, 1);
}
std::vector<cv::Point> get_point_in_holes(cv::Mat img) {
// invert for findContours call
cv::Mat work;
cv::bitwise_not(img, work);
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(work, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
std::vector<cv::Point> hole_points;
for (int i = 0; i < static_cast<int>(contours.size()); ++i) {
auto parent = hierarchy[i][3];
// toplevel holes have a parent but no grandparent...
if ((parent >= 0) && (hierarchy[parent][3] < 0)) {
hole_points.push_back(find_top_hole_point(contours[i]));
}
}
return hole_points;
}
int main() {
cv::Mat img = cv::imread("C:\\test\\alphabet.png");
// convert to 3-channel gray scale
cv::Mat work;
cv::cvtColor(img, work, cv::COLOR_BGR2GRAY);
// convert to 1-channel black and white
cv::threshold(work, work, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
// get seeds for flood-filling
// (topmost points in top-level holes)
auto hole_points = get_point_in_holes(work);
// convert back to 3-channel color;
cv::cvtColor(work, work, cv::COLOR_GRAY2BGR);
// fill top-level holes with red
for (const auto& pt : hole_points) {
cv::floodFill(work, pt, cv::Scalar(0, 0, 255));
}
cv::imshow("output_image", work);
cv::waitKey(0);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论