说明
在目标检测算法应用中,需要在图片上画位置框以及对应的类别。一旦画面中的目标比集中,文字会出现重叠现象,不易观察。故提出一种方法用于减少文字重叠现象。
思路
- 先规划一个目标框位置附近所有可以画图的区域。
- 对可选区域和已经写下文字的区域做iou,找到iou为0或者iou最小的可选区域作为写文字区域并加入已写入文字区域。
说明
在写文字的时候最好将目标框按照面积有小到大排序。优先画面积比较小的框的文字。对于面积比较大的框有比较多的选择空间。
代码
hpp
#ifndef POSITION_HPP
#define POSITION_HPP
#include <vector>
#include <tuple>
namespace ptn{
struct PutTextBox{
int xmin_;
int ymin_;
int xmax_;
int ymax_;
PutTextBox () = default;
PutTextBox(int xmin, int ymin, int xmax, int ymax) : xmin_(xmin), ymin_(ymin), xmax_(xmax), ymax_(ymax) {}
};
float area_percent(const PutTextBox& a, const PutTextBox& b);
float iou(const PutTextBox& a, const PutTextBox& b);
class Position{
public:
std::vector<std::tuple<int, int>> calculate_all(
std::vector<PutTextBox> all_boxes,
int width,
int height,
int text_width,
int text_height,
int baseline);
std::tuple<int, int> calculate(
PutTextBox box,
int width,
int height,
int text_width,
int text_height,
int baseline);
void clear();
std::vector<PutTextBox> find_all_draw_postion(
PutTextBox& box,
int width,
int height,
int text_width,
int text_height);
void cal_suitable_position(
std::vector<PutTextBox>& suitable_position,
int& x,
int& y);
private:
std::vector<PutTextBox> mark_position;
int zero_box_num = 1;
};
}
#endif
cpp
#include "position.hpp"
#include <iostream>
namespace ptn{
float area_percent(const PutTextBox& a, const PutTextBox& b)
{
int b_area = (b.ymax_ - b.ymin_) * (b.xmax_ - b.xmin_);
int xmin = std::max(a.xmin_, b.xmin_);
int xmax = std::min(a.xmax_, b.xmax_);
int ymin = std::max(a.ymin_, b.ymin_);
int ymax = std::min(a.ymax_, b.ymax_);
int w = std::max(std::min(a.xmax_, b.xmax_) - std::max(a.xmin_, b.xmin_), 0);
int h = std::max(std::min(a.ymax_, b.ymax_) - std::max(a.ymin_, b.ymin_), 0);
int over_area = w * h;
if (b_area == 0 || over_area == 0)
{
return 0;
}
return 1.0 * over_area / b_area * 100;
}
float iou(const PutTextBox& a, const PutTextBox& b)
{
int a_area = (a.ymax_ - a.ymin_) * (a.xmax_ - a.xmin_);
int b_area = (b.ymax_ - b.ymin_) * (b.xmax_ - b.xmin_);
int xmin = std::max(a.xmin_, b.xmin_);
int xmax = std::min(a.xmax_, b.xmax_);
int ymin = std::max(a.ymin_, b.ymin_);
int ymax = std::min(a.ymax_, b.ymax_);
int w = std::max(std::min(a.xmax_, b.xmax_) - std::max(a.xmin_, b.xmin_), 0);
int h = std::max(std::min(a.ymax_, b.ymax_) - std::max(a.ymin_, b.ymin_), 0);
int over_area = w * h;
if (a_area == 0 || b_area == 0 || over_area == 0)
{
return 0.f;
}
return 1.0 * over_area / (b_area + a_area - over_area) * 100;
}
std::vector<PutTextBox> Position::find_all_draw_postion(
PutTextBox& box,
int width,
int height,
int text_width,
int text_height)
{
std::vector<PutTextBox> suitable_position;
int xmin = std::max(0, box.xmin_);
int ymin = std::max(0, box.ymin_);
int xmax = std::min(width, box.xmax_);
int ymax = std::min(height, box.ymax_);
PutTextBox all{0, 0, width, height};
int btwl = std::max(xmax - xmin, text_width);
PutTextBox top_left(xmin, ymin - text_height, xmin + btwl, ymax);
PutTextBox bottom_left(xmin, ymin, xmin + btwl, ymax + text_height);
int btwr = std::min(xmax - xmin - text_width, 0);
PutTextBox top_right(xmin + btwr, ymin - text_height, xmax, ymax);
PutTextBox bottom_right(xmin + btwr, ymin, xmax, ymax + text_height);
int lrh = std::max(ymax - ymin, text_height);
PutTextBox left(xmin - text_width, ymin, xmax, ymin + lrh);
PutTextBox right(xmin, ymin, xmax + text_width, ymin + lrh);
if (area_percent(all, top_left) == 100)
{
int bxmin = top_left.xmin_;
int bymin = top_left.ymin_;
int bxmax = top_left.xmin_ + text_width;
int bymax = top_left.ymin_ + text_height;
suitable_position.emplace_back(std::move(PutTextBox(bxmin, bymin, bxmax, bymax)));
}
if (area_percent(all, top_right) == 100)
{
int bxmin = top_right.xmin_;
int bymin = top_right.ymin_;
int bxmax = top_right.xmin_ + text_width;
int bymax = top_right.ymin_ + text_height;
suitable_position.emplace_back(std::move(PutTextBox(bxmin, bymin, bxmax, bymax)));
}
if (area_percent(all, right) == 100)
{
int box_height = ymax - ymin;
int num = box_height / text_height;
for (int i = 0; i < num; i++)
{
int bxmin = right.xmax_ - text_width;
int bymin = right.ymin_ + i * text_height;
int bxmax = right.xmax_;
int bymax = right.ymin_ + i * text_height + text_height;
suitable_position.emplace_back(std::move(PutTextBox(bxmin, bymin, bxmax, bymax)));
}
}
if (area_percent(all, left) == 100)
{
int box_height = ymax - ymin;
int num = box_height / text_height;
for (int i = 0; i < num; i++)
{
int bxmin = left.xmin_;
int bymin = left.ymin_ + i * text_height;
int bxmax = left.xmin_ + text_width;
int bymax = left.ymin_ + i * text_height + text_height;
suitable_position.emplace_back(std::move(PutTextBox(bxmin, bymin, bxmax, bymax)));
}
}
if (area_percent(all, bottom_left) == 100)
{
int bxmin = bottom_left.xmin_;
int bymin = bottom_left.ymax_ - text_height;
int bxmax = bottom_left.xmin_ + text_width;
int bymax = bottom_left.ymax_;
suitable_position.emplace_back(std::move(PutTextBox(bxmin, bymin, bxmax, bymax)));
}
if (area_percent(all, bottom_right) == 100)
{
int bxmin = bottom_right.xmin_;
int bymin = bottom_right.ymax_ - text_height;
int bxmax = bottom_right.xmin_ + text_width;
int bymax = bottom_right.ymax_;
suitable_position.emplace_back(std::move(PutTextBox(bxmin, bymin, bxmax, bymax)));
}
if (suitable_position.size() == 0)
{
int bxmin = xmin;
int bymin = ymin;
int bxmax = xmin + text_width;
int bymax = ymin + text_height;
suitable_position.emplace_back(std::move(PutTextBox(bxmin, bymin, bxmax, bymax)));
}
return suitable_position;
}
void Position::cal_suitable_position(
std::vector<PutTextBox>& suitable_position,
int& x,
int& y)
{
if (mark_position.size() == 0)
{
mark_position.push_back(suitable_position[0]);
x = suitable_position[0].xmin_;
y = suitable_position[0].ymin_;
return;
}
PutTextBox box;
float all_max_iou = 0.f;
for (const auto& sp : suitable_position)
{
float max_iou = 0.f;
for (const auto& mp : mark_position)
{
float percent = iou(sp, mp);
if (percent > max_iou)
{
max_iou = percent;
}
}
/**
* 遍历完所有已经标记过的地区,最大iou依然为0,说明这个地方可以写文字
*/
if (max_iou == 0)
{
box = sp;
break;
}
else
{
if (max_iou > all_max_iou)
{
all_max_iou = max_iou;
box = sp;
}
}
}
x = box.xmin_;
y = box.ymin_;
mark_position.push_back(box);
}
std::vector<std::tuple<int, int>> Position::calculate_all(
std::vector<PutTextBox> all_boxes,
int width,
int height,
int text_width,
int text_height,
int baseline)
{
std::vector<std::tuple<int, int>> result;
int zero_box_num = 1;
for (auto& box : all_boxes)
{
result.push_back(calculate(box, width, height, text_width, text_height, baseline));
}
clear();
return result;
}
std::tuple<int, int> Position::calculate(PutTextBox box, int width, int height, int text_width, int text_height, int baseline)
{
std::tuple<int, int> result;
if (box.xmax_ < box.xmin_ || box.ymax_ < box.ymin_)
{
result = std::make_tuple(-1, -1);
}
else if (box.xmin_ == 0 && box.ymin_ == 0 && box.xmax_ == 0 && box.ymax_ == 0)
{
int x = (width - text_width) / 2;
int y = 0 + zero_box_num * text_height;
zero_box_num += 1;
result = std::make_tuple(x, y);
}
else
{
std::vector<PutTextBox> draw_positions = find_all_draw_postion(box, width, height, text_width, text_height + baseline);
int x = 0;
int y = 0;
cal_suitable_position(draw_positions, x, y);
result = std::make_tuple(x, y + text_height);
}
return result;
}
void Position::clear()
{
std::vector<PutTextBox>().swap(mark_position);
zero_box_num = 1;
}
}
网友评论