直接贴效果
把一个文本文字分成多个矩形,只要有像素的就分成一个小矩形
四叉树的类及原理大家可以从他那下载,我只是搬运工,那才是原创哥,我这边用了opencv分割下图片
场景管理:四叉树算法C++实现_踏莎行的博客-CSDN博客_c++四叉树
main.cpp
#include "Stdafx.h"
#include <QApplication>
#include <QMainWindow>
#include <QTextCodec>
#include <QDebug>
#include <cmath>
#include <ctime>
#include <QLabel>
#include <cstdlib>
#include <QWidget>
//https://blog.csdn.net/zhiwei121/article/details/100941934 opencv 4叉树
//https://blog.csdn.net/u012234115/article/details/47156289
//https://zhuanlan.zhihu.com/p/83722268 I:\fourtree\SimpleCraft-master
//https://zhuanlan.zhihu.com/p/32633541 https://github.com/lilypuye/cppLight2d/archive/refs/heads/master.zip
//https://blog.csdn.net/qq811299838/article/details/116501824
#include "QTGuitest.h"
#include <iostream>
#include <queue>
#include "object.h"
#include "quad_tree_node.h"
#include "quad_tree_node.cpp"
//https://download.csdn.net/download/weixin_43235206/12503218?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-download-2%7Edefault%7ECTRLIST%7EPaid-1.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant_t0.none-task-download-2%7Edefault%7ECTRLIST%7EPaid-1.pc_relevant_default&utm_relevant_index=1
using namespace std;
int nMaxLevel;
QuadTreeNode<Object> *quadTree = NULL;
const int MinBoxSize = 10; // 最小网格大小
void VisitItem(Mat& image, QuadTreeNode<Object> *quadTreeItemVist)
{
if (quadTreeItemVist)
{
//坐标和长宽属性,左上角为锚点
float x = quadTreeItemVist->x;
float y = quadTreeItemVist->y;
float width = quadTreeItemVist->width;
float height = quadTreeItemVist->height;
int nlevel = quadTreeItemVist->level;
int nmaxLevel = quadTreeItemVist->maxLevel;
if (width<MinBoxSize*1.5)//MinBoxSize
{
//画出当前的矩形
Rect rect(x, y, width, height);//左上坐标(x,y)和矩形的长(x)宽(y)
cv::rectangle(image, rect, Scalar(255, 0, 0), 1, LINE_8, 0);
}
if (quadTreeItemVist->upLeftNode)
{
VisitItem(image,quadTreeItemVist->upLeftNode);
}
if (quadTreeItemVist->upRightNode)
{
VisitItem(image, quadTreeItemVist->upRightNode);
}
if (quadTreeItemVist->bottomLeftNode)
{
VisitItem(image, quadTreeItemVist->bottomLeftNode);
}
if (quadTreeItemVist->bottomRightNode)
{
VisitItem(image, quadTreeItemVist->bottomRightNode);
}
}
}
Object* resetTree(Mat& image, int nlevel,int left, int top, int width, int nHeight)
{
bool bFind;
int nchannels = image.channels();
if (nchannels == 3)
{//3通道
int nImageWidth = width + left;
int nImageHeight = nHeight + top;
for (int x = left; x < nImageWidth; ++x)
{
for (int y = top; y < nImageHeight; ++y)
{
cv::Vec3b & pixel = image.at<cv::Vec3b>(y, x);
if (pixel[2] < 5)
{//黑色
}
else
{//白色
Object* objReturn = NULL;
//生成一个新的结点
if (quadTree ==NULL)
{
quadTree = new QuadTreeNode<Object>(left, top, width, nHeight, 1, nMaxLevel, ROOT, nullptr);
}
else
{
Object* obj = new Object(left, top, width, nHeight);
quadTree->InsertObject(obj);
objReturn = obj;
}
bFind = true;
nlevel++;
//把当前图形再割成,4份
int cx = width / 2;
int cy = nHeight / 2;
Object* objLeftTop = resetTree( image, nlevel,left, top, cx, cy);
Object* objRightTop = resetTree( image, nlevel, left+ cx, top, cx, cy);
Object* objLeftBottom = resetTree( image, nlevel, left, top+cy, cx, cy);
Object* objRightBottom = resetTree( image, nlevel, left + cx, top + cy, cx, cy);
if (objLeftTop)
{
quadTree->InsertObject(objLeftTop);
}
if (objRightTop)
{
quadTree->InsertObject(objRightTop);
}
if (objLeftBottom)
{
quadTree->InsertObject(objLeftBottom);
}
if (objRightBottom)
{
quadTree->InsertObject(objRightBottom);
}
return objReturn;
}
}
}
}
//没查找到有白色的字体时
return NULL;
}
int main(int argc, char *argv[])
{
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
Q_IMPORT_PLUGIN(QJpegPlugin)
//Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin);
QApplication::addLibraryPath("plugins");
QApplication a(argc, argv);
QTGuitest w;
Mat src = imread("I:\\QtLine\\procress\\test.png", CV_LOAD_IMAGE_UNCHANGED);
Mat dst = src.clone();
int nWidth= dst.cols;
int nHeight = dst.rows;
nMaxLevel = nWidth / MinBoxSize;
if (nMaxLevel > nHeight / MinBoxSize)
{
nMaxLevel = nWidth / MinBoxSize;
}
nMaxLevel++;
//转成二值,白色表示有数据
//分割成多块,并看那快是否有效像素,然后进行分割成小的组成部分
//? new QuadTreeNode<Object>(0, 0, dst.cols, dst.rows, 1, nMaxLevel, ROOT, nullptr);
resetTree( dst,1,0,0, dst.cols, dst.rows);
//quadTree->InsertObject(new Object(50, 50, 100, 100));
//quadTree->InsertObject(new Object(25, 25, 50, 50));
//quadTree->InsertObject(new Object(62.5, 12.5, 25, 25));
//quadTree->InsertObject(new Object(62.5, 62.5, 25, 25));
//quadTree->InsertObject(new Object(63.5, 63.6, 25, 25));
//quadTree->InsertObject(new Object(125, 25, 50, 50));
//quadTree->InsertObject(new Object(112.5, 62.5, 25, 25));
//quadTree->RemoveObjectsAt(100, 0, 110, 110);
VisitItem(dst,quadTree);
cv::imshow("SrcImg", dst);
list<Object *> resObjects = quadTree->GetObjectsAt(0, 0, 200, 200);
cout << resObjects.size() << endl;
for (auto &t : resObjects)
cout << t->x << ' ' << t->y << ' ' << t->width << ' ' << t->height << endl;
delete quadTree;
system("pause");
w.show();
return a.exec();
}
quad_tree_node.cpp:
#include "stdafx.h"
#include "quad_tree_node.h"
template <typename T>
QuadTreeNode<T>::QuadTreeNode(
float _x, float _y, float _width, float _height,
int _level, int _maxLevel,
QuadType _quadType,
QuadTreeNode *_parent) :
x(_x),
y(_y),
width(_width),
height(_height),
level(_level),
maxLevel(_maxLevel),
quadType(_quadType)
{
parent = _parent;
upRightNode = nullptr;
upLeftNode = nullptr;
bottomLeftNode = nullptr;
bottomRightNode = nullptr;
}
template <typename T>
QuadTreeNode<T>::~QuadTreeNode()
{
if (level == maxLevel)
return;
//如果不是叶子节点,就销毁子节点
parent = nullptr;
}
template <typename T>
bool QuadTreeNode<T>::IsContain(float px, float py, float w, float h, T *object) const
{
if (object->x >= px
&&object->x + object->width <= px + w
&&object->y >= py
&&object->y + object->height <= py + h)
return true;
return false;
}
template <typename T>
bool QuadTreeNode<T>::IsContain(float px, float py, float w, float h, QuadTreeNode<T> *quadTreeNode) const
{
if (quadTreeNode->x >= px
&&quadTreeNode->x + quadTreeNode->width <= px + w
&&quadTreeNode->y >= py
&&quadTreeNode->y + quadTreeNode->height <= py + h)
return true;
return false;
}
template <typename T>
void QuadTreeNode<T>::InsertObject(T *object)
{
//如果是叶子节点,则存在叶子节点
if (level == maxLevel)
{
objects.push_back(object);
return;
}
//非叶子节点,如果下层节点可以包含该对象,则递归构建子节点并插入对象,边构建边插入
if (IsContain(x + width / 2, y, width / 2, height / 2, object))
{
if (!upRightNode) //避免重复创建覆盖掉原来的节点
upRightNode = new QuadTreeNode(x + width / 2, y, width / 2, height / 2, level + 1, maxLevel, UP_RIGHT, this);//如果没有子节点就创建子节点,parent节点是当前节点
upRightNode->InsertObject(object);
return;
}
else if (IsContain(x, y, width / 2, height / 2, object))
{
if (!upLeftNode)
upLeftNode = new QuadTreeNode(x, y, width / 2, height / 2, level + 1, maxLevel, UP_LEFT, this);
upLeftNode->InsertObject(object);
return;
}
else if (IsContain(x, y + height / 2, width / 2, height / 2, object))
{
if (!bottomLeftNode)
bottomLeftNode = new QuadTreeNode(x, y + height / 2, width / 2, height / 2, level + 1, maxLevel, BOTTOM_LEFT, this);
bottomLeftNode->InsertObject(object);
return;
}
else if (IsContain(x + width / 2, y + height / 2, width / 2, height / 2, object))
{
if (!bottomRightNode)
bottomRightNode = new QuadTreeNode(x + width / 2, y + height / 2, width / 2, height / 2, level + 1, maxLevel, BOTTOM_RIGHT, this);
bottomRightNode->InsertObject(object);
return;
}
//下层节点不能完全包含改对象,则插入到当前非叶子节点
//这个判断也可以省去
if (IsContain(x, y, width, height, object))
objects.push_back(object);
}
template <typename T>
std::list<T *> QuadTreeNode<T>::GetObjectsAt(float px, float py, float w, float h)
{
std::list<T *> resObjects;
//如果当前节点完全被包含,把当前节点存的对象放到列表末尾,空链表也行
if (IsContain(px, py, w, h, this))
{
resObjects.insert(resObjects.end(), objects.begin(), objects.end());
//最后一层
if (level == maxLevel)
return resObjects;
}
//如果有下层节点就把下层节点包含的对象加进来
if (upRightNode)
{
std::list<T *> upRightChild;
upRightChild = upRightNode->GetObjectsAt(px, py, w, h);
resObjects.insert(resObjects.end(), upRightChild.begin(), upRightChild.end());
}
if (upLeftNode)
{
std::list<T *> upLeftChild;
upLeftChild = upLeftNode->GetObjectsAt(px, py, w, h);
resObjects.insert(resObjects.end(), upLeftChild.begin(), upLeftChild.end());
}
if (bottomLeftNode)
{
std::list<T *> bottomLeftChild;
bottomLeftChild = bottomLeftNode->GetObjectsAt(px, py, w, h);
resObjects.insert(resObjects.end(), bottomLeftChild.begin(), bottomLeftChild.end());
}
if (bottomRightNode)
{
std::list<T *> bottomRightChild;
bottomRightChild = bottomRightNode->GetObjectsAt(px, py, w, h);
resObjects.insert(resObjects.end(), bottomRightChild.begin(), bottomRightChild.end());
}
return resObjects;
}
template <typename T>
void QuadTreeNode<T>::RemoveObjectsAt(float px, float py, float w, float h)
{
//如果本层节点被包含则删除本层节点的对象
//这个判断主要是对根节点起作用,其他子节点实际在上层都做了判断
if (IsContain(px, py, w, h, this))
{
//清除本节点层的对象
objects.clear();
//最后一层
if (level == maxLevel)
return;
}
//如果有子节点且被包含就销毁子节点,注意别产生野指针
//其实只要上层被包含了,下层肯定被包含,代码还需改进
if (upRightNode&&IsContain(px, py, w, h, upRightNode))
{
upRightNode->RemoveObjectsAt(px, py, w, h);
delete upRightNode;
upRightNode = nullptr;
}
if (upLeftNode&&IsContain(px, py, w, h, upLeftNode))
{
upLeftNode->RemoveObjectsAt(px, py, w, h);
delete upLeftNode;
upLeftNode = nullptr;
}
if (bottomLeftNode&&IsContain(px, py, w, h, bottomLeftNode))
{
bottomLeftNode->RemoveObjectsAt(px, py, w, h);
delete bottomLeftNode;
bottomLeftNode = nullptr;
}
if (bottomRightNode&&IsContain(px, py, w, h, bottomRightNode))
{
bottomRightNode->RemoveObjectsAt(px, py, w, h);
delete bottomRightNode;
bottomRightNode = nullptr;
}
}
quad_tree_node.h头文件:
/*
//四叉树节点类,用头节点代表四叉树
//坐标系坐上角为原点,左往右为x轴递增,上往下y轴递增
//本四叉树的策略是:1,插入时动态分配节点和删除节点,不是满树;2,当矩形区域完全包含某个节点时才获取或剔除;3,对象放在完全包含它的区域节点内,非根节点也存储对象
*/
#pragma once
#include <list>
//四叉树类型枚举
enum QuadType
{
ROOT, //根
UP_RIGHT, //象限Ⅰ
UP_LEFT, //象限Ⅱ
BOTTOM_LEFT, //象限Ⅲ
BOTTOM_RIGHT //象限Ⅳ
};
template <typename T>
class QuadTreeNode
{
public:
QuadTreeNode(float _x, float _y, float _width, float _height, int _level, int _maxLevel, QuadType _quadType, QuadTreeNode *_parent);
~QuadTreeNode();
//坐标和长宽属性,左上角为锚点
float x;
float y;
float width;
float height;
int level; //当前深度
int maxLevel; //最大深度
QuadTreeNode *upRightNode;
QuadTreeNode *upLeftNode;
QuadTreeNode *bottomLeftNode;
QuadTreeNode *bottomRightNode;
public:
void InsertObject(T *object); //插入对象
std::list<T *> GetObjectsAt(float px, float py, float w, float h); //查询对象,获得一片区域里的对象链表,此处只考虑完全包含的
void RemoveObjectsAt(float px, float py, float w, float h); //删除对象,删除一片区域里的对象和节点,此处只考虑完全包含的
private:
bool IsContain(float px, float py, float w, float h, T *object) const; //判断某个区域是否包含某对象
bool IsContain(float px, float py, float w, float h, QuadTreeNode<T> *quadTreeNode) const; //重载,判断某个区域是否包含某个节点
private:
std::list<T *> objects; //节点数据队列
//父、子节点,分四个象限
QuadTreeNode *parent;
//节点类型
QuadType quadType;
};
object.cpp
#include "stdafx.h"
#include "object.h"
Object::Object(float _x,float _y,float _width,float _height):
x(_x),
y(_y),
width(_width),
height(_height)
{
}
Object::~Object()
{
}
boject.h
/*
//被管理的对象类
*/
#pragma once
class Object
{
public:
Object(float _x,float _y,float _width,float _height);
~Object();
public:
//对象的属性,例如坐标和长宽,以左上角为锚点
float x;
float y;
float width;
float height;
};