c++(qt) + opencv 四叉树分割文本文字,也可用在图像上

2 篇文章 0 订阅

直接贴效果

把一个文本文字分成多个矩形,只要有像素的就分成一个小矩形

四叉树的类及原理大家可以从他那下载,我只是搬运工,那才是原创哥,我这边用了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;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值