四叉树编码存储的实现
四叉树编码
这里是使用数组去模拟影像。
点击下载源码 (有几个样例数据,程序源码,一个简单实用录屏)
先写2个类 QuadNode,QuadTree 2个类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using QuadTreeDemo.QuadNodes; namespace QuadTreeDemo.QuadNodes { public class QuadNode { public QuadNode() { this.Rectangle = new Rectangle(); this.Childs = new QuadNode[4]; } public QuadNode(QuadNode other) { this.Rectangle = other.Rectangle; this.Childs = other.Childs; } public QuadNode( Rectangle rect ,QuadNode[] childs) { this.Rectangle = rect; this.Childs = childs; } /// <summary> /// 块的矩形 /// </summary> public Rectangle Rectangle { get; set; } /// <summary> /// 四个子节点 /// </summary> public QuadNode[] Childs { get; set; } /// <summary> /// 块的编码 /// </summary> public string Code{get;set;} /// <summary> /// 块的值 /// </summary> public int Value{get;set;} } }
using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace QuadTreeDemo.QuadNodes { /// <summary> /// 四叉树类 /// </summary> public class QuadTree { /// <summary> /// 构造函数 /// </summary> /// <param name="root">根节点</param> /// <param name="depth">树的深度</param> public QuadTree(QuadNode root, int depth) { this.Root = root; this.Depth = depth; } /// <summary> /// 构造函数 /// </summary> public QuadTree() { this.Root = new QuadNode(); this.Depth = 0; } /// <summary> /// 获取树的根节点 /// </summary> public QuadNode Root { get; set; } /// <summary> /// 获取树的深度 /// </summary> public int Depth { get; set; } /// <summary> /// 构建四叉树 /// </summary> /// <param name="depth">深度</param> /// <param name="rect">节点的矩形</param> /// <param name="list">数组</param> /// <returns></returns> public static QuadTree BuildQuadtree(int depth, Rectangle rect, List<List<int>> list) { QuadTree tree = new QuadTree(); tree.Depth = depth; tree.Root.Code = "C";//初始化编码是空 BuildSubTree(tree.Root, depth, rect, list); return tree; } /// <summary> /// 构建四叉子树 /// </summary> /// <param name="node">父节点</param> /// <param name="depth">深度</param> /// <param name="rect">矩形</param> /// <param name="list">数组</param> private static void BuildSubTree(QuadNode node, int depth, Rectangle rect, List<List<int>> list) { if (depth != 0) { if (IsAllSameValue(list, rect)) { //这个区域全部相同的话就不用继续分割了 node.Rectangle = rect; node.Code = node.Code; } else { //执行分割 node.Childs = new QuadNode[4] { new QuadNode(), new QuadNode(), new QuadNode(), new QuadNode() }; Rectangle[] subRectangles = DivideRectange(rect);//将传入的矩形进行分割 #region 执行分割 if (IsAllSameValue(list, subRectangles[0])) { //如果这个区域像素值全相等 node.Childs[0].Code = node.Code + "0";//编码为父节点的编码+当前区域编码 node.Childs[0].Rectangle = subRectangles[0];//子节点的矩形设置 node.Childs[0].Value = list[subRectangles[0].Y][subRectangles[0].X];//值设置 } else { node.Childs[0].Code = node.Code + "0";//设置编码,供子节点使用 BuildSubTree(node.Childs[0], depth - 1, subRectangles[0], list);//递归调用创建编码函数 } if (IsAllSameValue(list, subRectangles[1])) { node.Childs[1].Code = node.Code + "1"; node.Childs[1].Rectangle = subRectangles[1]; node.Childs[1].Value = list[subRectangles[1].Y][subRectangles[1].X]; } else { node.Childs[1].Code = node.Code + "1"; BuildSubTree(node.Childs[1], depth - 1, subRectangles[1], list); } if (IsAllSameValue(list, subRectangles[2])) { node.Childs[2].Code = node.Code + "2"; node.Childs[2].Rectangle = subRectangles[2]; node.Childs[2].Value = list[subRectangles[2].Y][subRectangles[2].X]; } else { node.Childs[2].Code = node.Code + "2"; BuildSubTree(node.Childs[2], depth - 1, subRectangles[2], list); } if (IsAllSameValue(list, subRectangles[3])) { node.Childs[3].Code = node.Code + "3"; node.Childs[3].Rectangle = subRectangles[3]; node.Childs[3].Value = list[subRectangles[3].Y][subRectangles[3].X]; } else { node.Childs[3].Code = node.Code + "3"; BuildSubTree(node.Childs[3], depth - 1, subRectangles[3], list); } #endregion } } } /// <summary> /// 判断矩形类的元素是不是全相等 /// </summary> /// <param name="list">数组</param> /// <param name="rect">矩形</param> /// <returns></returns> private static bool IsAllSameValue(List<List<int>> list, Rectangle rect) { int sameValue = (list[rect.Top][rect.Left]);//假定相同值是第一个 for (int i = rect.Top; i < rect.Bottom; i++) { for (int j = rect.Left; j < rect.Right; j++) { if (list[i][j] != sameValue) { return false; //有一个不相等的就直接返回false } } } return true;//如果数组中找不到一个不同的(也就是全部相同),那就返回true; } /// <summary> /// 分割矩形区域 /// </summary> /// <param name="rect">矩形</param> /// <returns>分割后的4个矩形</returns> private static Rectangle[] DivideRectange(Rectangle rect) { /* |------------|-----------| | [0] | [1] | |------------|-----------| | [2] | [3] | |------------|-----------| */ Rectangle[] retRectangles = new Rectangle[4]; retRectangles[1] = new Rectangle((rect.Left + rect.Right) / 2, rect.Top, rect.Width / 2, rect.Width / 2); retRectangles[0] = new Rectangle(rect.Left, rect.Top, rect.Width / 2, rect.Width / 2); retRectangles[2] = new Rectangle(rect.Left, (rect.Top + rect.Bottom) / 2, rect.Width / 2, rect.Width / 2); retRectangles[3] = new Rectangle((rect.Left + rect.Right) / 2, (rect.Top + rect.Bottom) / 2, rect.Width / 2, rect.Width / 2); return retRectangles; } //public static String TraverseTree(QuadTree node) //{ // StringBuilder sb = new StringBuilder(); // QuadNode root = node.Root; // if (root.Childs[0] != null) // { // TraverseSubTree(root.Childs[0], sb); // } // if (root.Childs[1] != null) // { // TraverseSubTree(root.Childs[1], sb); // } // if (root.Childs[2] != null) // { // TraverseSubTree(root.Childs[2], sb); // } // if (root.Childs[3] != null) // { // TraverseSubTree(root.Childs[3], sb); // } // return sb.ToString(); //} /// <summary> /// 遍历树 /// </summary> /// <param name="node">树</param> /// <returns></returns> public static List<QuadNode> TraverseTree(QuadTree node) { List<QuadNode> list = new List<QuadNode>(); QuadNode root = node.Root; if (root.Childs[0] != null) { TraverseSubTree(root.Childs[0], list);//调用遍历子树函数 } if (root.Childs[1] != null) { TraverseSubTree(root.Childs[1], list); } if (root.Childs[2] != null) { TraverseSubTree(root.Childs[2], list); } if (root.Childs[3] != null) { TraverseSubTree(root.Childs[3], list); } return list; } //private static void TraverseSubTree(QuadNode node, StringBuilder sb) //{ // if (!string.IsNullOrEmpty(node.Code) && node.Rectangle.Width !=0) // { // sb.AppendLine(string.Format("{0},{1},{2},{3},{4})", node.Code, node.Rectangle.X, node.Rectangle.Y, // node.Rectangle.Width * node.Rectangle.Height ,node.Value )); // } // if (node.Childs[0] != null) // { // TraverseSubTree(node.Childs[0], sb); // } // if (node.Childs[1] != null) // { // TraverseSubTree(node.Childs[1], sb); // } // if (node.Childs[2] != null) // { // TraverseSubTree(node.Childs[2], sb); // } // if (node.Childs[3] != null) // { // TraverseSubTree(node.Childs[3], sb); // } //} /// <summary> /// 遍历子树 /// </summary> /// <param name="node">树节点</param> /// <param name="list">list集合</param> private static void TraverseSubTree(QuadNode node, List<QuadNode> list) { //这个地方必须加width!=0的判断。 if (!string.IsNullOrEmpty(node.Code) && node.Rectangle.Width != 0) { list.Add(node); } if (node.Childs[0] != null) { TraverseSubTree(node.Childs[0], list);//递归调用遍历子树 } if (node.Childs[1] != null) { TraverseSubTree(node.Childs[1], list); } if (node.Childs[2] != null) { TraverseSubTree(node.Childs[2], list); } if (node.Childs[3] != null) { TraverseSubTree(node.Childs[3], list); } } } }
做一个winform的界面 如下图:
窗体代码如下:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.IO; using QuadTreeDemo.QuadNodes; namespace QuadTreeDemo { public partial class LoadArrayFrm : Form { private List<List<int>> _saveList;//数组文件 private List<QuadNode> _saveQuadNodeList;//四叉树节点集合 public LoadArrayFrm() { InitializeComponent(); } /// <summary> /// 加载数组到RichTxt上去 /// </summary> private void LoadArrayToRichTxt() { StringBuilder sb = new StringBuilder(); sb.Append(_saveList.Count).Append(",").AppendLine(_saveList[0].Count.ToString());//图像大小信息 for (int i = 0; i < _saveList.Count; i++)//图像行 { StringBuilder suBuilder = new StringBuilder(); for (int j = 0; j < _saveList[i].Count; j++)//图像列 { sb.AppendFormat("{0:d3},",_saveList[i][j] ); } sb.Append(suBuilder.ToString().TrimEnd(',')); sb.Append("\r\n");//换行 } richTextBox1.Text = sb.ToString(); //设置richtext内容 } /// <summary> /// 从文件中加载数组 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ToolStripMenuItemLoadArrayFromFile_Click(object sender, EventArgs e) { LoadArrayFile(); } /// <summary> /// 加载数组文件 /// </summary> private void LoadArrayFile() { var richTextBox = richTextBox1; if (richTextBox != null) richTextBox.Clear(); OpenFileDialog dlg = new OpenFileDialog(); dlg.Title = @"加载数组";//设置标题 dlg.Filter = @"文本文件(*.txt)|*.txt";//文件过滤 dlg.Multiselect = false;//禁止多选 if (dlg.ShowDialog() == DialogResult.OK)//确定 { string filename = dlg.FileName;//获取选择的文件名 _saveList = new List<List<int>>(); try { string[] lines = File.ReadAllLines(filename);//读取文件内容到字符串数组中 int xSize = int.Parse(lines[0].Split(',')[0]);//先获取数组的大小信息 int ySize = int.Parse(lines[0].Split(',')[1]); for (int i = 0; i < xSize; i++)//行 { List<int> subList = new List<int>();//列 string[] subLines = lines[i + 1].Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);//利用逗号进行分割 for (int j = 0; j < ySize; j++) { subList.Add(int.Parse(subLines[j])); } _saveList.Add(subList); } LoadArrayToRichTxt();//将数组加载到richtext上去 } catch (Exception) { MessageBox.Show(@"加载失败!"); //throw; } } } /// <summary> /// 保存数组文件 /// </summary> private void SaveArrayFile() { SaveFileDialog dlg = new SaveFileDialog(); dlg.Title = @"保存数组到文件"; dlg.Filter = @"文本文件(*.txt)|*.txt"; if (dlg.ShowDialog() == DialogResult.OK) { string filename = dlg.FileName; try { File.WriteAllText(filename, richTextBox1.Text, Encoding.UTF8); } catch (Exception) { MessageBox.Show(@"加载失败", @"提示"); } } } /// <summary> /// 加载编码文件 /// </summary> private void LoadCodeFile() { dataGridView1.Rows.Clear(); OpenFileDialog dlg = new OpenFileDialog(); dlg.Title = @"加载编码"; dlg.Filter = @"文本文件(*.txt)|*.txt"; dlg.Multiselect = false; if (dlg.ShowDialog() == DialogResult.OK) { List<QuadNode> list = new List<QuadNode>(); string filename = dlg.FileName; StreamReader sr = new StreamReader(filename);//读文件流 string line; while (!string.IsNullOrEmpty(line =sr.ReadLine()))//读取的不是空行继续 { string[] tmpStrings = line.Split(',');//将读入行进行分割 QuadNode node = new QuadNode { Code = tmpStrings[0], Rectangle = new Rectangle(int.Parse(tmpStrings[1]), int.Parse(tmpStrings[2]), (int) Math.Sqrt(int.Parse(tmpStrings[3])), (int) Math.Sqrt(int.Parse(tmpStrings[3]))), Value = int.Parse(tmpStrings[4]) }; //提取编码信息 //提取数组值信息 list.Add(node);//构建好的node加入到list中去 } _saveQuadNodeList = list; //给全局list赋值用于后续的编码查询操作。 FillDataView(list);//填充DataView } } /// <summary> /// 保存编码文件 /// </summary> private void SaveCodeFile() { SaveFileDialog dlg = new SaveFileDialog(); dlg.Title = @"保存编码文件";//标题 dlg.Filter = @"文本文件(*.txt)|*.txt";//过滤 if (dlg.ShowDialog() == DialogResult.OK) { string filename = dlg.FileName;//文件名 try { StreamWriter fs = new StreamWriter(filename);//获取写入流 foreach (DataGridViewRow row in dataGridView1.Rows) { if (row != null && row.Cells["code"].Value != null) fs.WriteLine("{0},{1},{2},{3},{4}", row.Cells["code"].Value, row.Cells["point"].Value.ToString().Split(',')[0], row.Cells["point"].Value.ToString().Split(',')[1], row.Cells["blockSize"].Value, row.Cells["value"].Value); //一定格式的保存编码文件(,分割) } fs.Close(); MessageBox.Show(@"保存成功", @"提示"); } catch (Exception) { MessageBox.Show(@"保存出现异常",@"提示"); } } } /// <summary> /// 填充DataView /// </summary> /// <param name="list"> list集合</param> private void FillDataView(List<QuadNode> list) { int rowId = 0; foreach (QuadNode item in list) { //给dataView赋值 int index = dataGridView1.Rows.Add(); dataGridView1.Rows[index].Cells["id"].Value = rowId++; dataGridView1.Rows[index].Cells["code"].Value = item.Code; dataGridView1.Rows[index].Cells["point"].Value = string.Format("{0},{1}", item.Rectangle.X, item.Rectangle.Y); dataGridView1.Rows[index].Cells["blockSize"].Value = item.Rectangle.Width * item.Rectangle.Width; dataGridView1.Rows[index].Cells["value"].Value = item.Value; } } /// <summary> /// 执行编码 /// </summary> private void PerformCode() { Rectangle rect = new Rectangle(0, 0, _saveList.Count, _saveList.Count); // int depth = list.Count; int depth = GetOriginalDepth(GetOriginalDepth(_saveList.Count)); QuadTree tree = QuadTree.BuildQuadtree(depth, rect, _saveList);//获取一个树 _saveQuadNodeList = QuadTree.TraverseTree(tree);//遍历树获得一个list集合 FillDataView(_saveQuadNodeList);//list集合填充到DataView进行展示。 } /// <summary> /// 执行反编码 /// </summary> private void PerformInverseCode() { if (_saveList != null) { _saveList.Clear(); } //构建树节点集合 List<QuadNode> nodelList = new List<QuadNode>(); foreach (DataGridViewRow row in dataGridView1.Rows) { if (row != null && row.Cells["code"].Value != null) { QuadNode node = new QuadNode(); node.Rectangle = new Rectangle(); node.Code = row.Cells["code"].Value.ToString(); string[] points = row.Cells["point"].Value.ToString().Split(','); int x = int.Parse(points[0]); int y = int.Parse(points[1]); int width = (int) Math.Sqrt(double.Parse(row.Cells["blockSize"].Value.ToString())); node.Rectangle = new Rectangle(x, y, width, width); node.Value = int.Parse(row.Cells["value"].Value.ToString()); nodelList.Add(node); } } //获取树节点编码长度最长的编码,计算数组大小 int listSize = (int)Math.Pow(2, nodelList.Max(r => r.Code.Length)-1); _saveList = new List<List<int>>();//创建一个list(二维数组) for (int i = 0; i < listSize; i++) { List<int> subList = new List<int>(); for (int j = 0; j < listSize; j++) { subList.Add(0);//给数组设置默认值0 } _saveList.Add(subList);//添加一行数据 } //通过树节点集合给二维数组进行赋值 foreach (QuadNode node in nodelList) { int x = node.Rectangle.X;//矩形的左上角X int y = node.Rectangle.Y;//矩形的左上角Y int width = node.Rectangle.Width; int pixelValue = node.Value; //下面的循环的这个矩形区域的值都是一样的。 for (int i = y; i < y+width; i++)//最小行号 ->最大行号 { for (int j = x; j < x + width; j++)//最小列号 ->最大列号 { _saveList[i][j] = pixelValue; } } } //得到二维数组了,进行格式化显示到RichText上去。 StringBuilder sb =new StringBuilder(); for (int i = 0; i < _saveList.Count; i++) { for (int j = 0; j < _saveList[i].Count; j++) { sb.AppendFormat( "{0:d3},",_saveList[i][j]); } sb.AppendLine(); } richTextBox1.Text = sb.ToString(); } /// <summary> /// 执行编码查询 /// </summary> private void PerformSelectCode() { labelInputCode.Visible = true; textBoxInputCode.Visible = true; textBoxSelectResult.Visible = true; textBoxSelectResult.Enabled = false; textBoxSelectResult.Text = string.Empty;//执行前先清空结果文本框 textBoxSelectResult.BackColor = DefaultBackColor;//获取控件的默认颜色 if (string.IsNullOrEmpty(textBoxInputCode.Text))//如果无输入提示 { MessageBox.Show(@"请输入你要查询的编码",@"提示"); textBoxInputCode.Focus(); return; } IEnumerable< QuadNode> nodes= _saveQuadNodeList.Where(r => r.Code == textBoxInputCode.Text.ToUpper()); var quadNodes = nodes as QuadNode[] ?? nodes.ToArray(); if (quadNodes.Any())//如果有结果 { QuadNode node = quadNodes.ToList()[0]; //进行显示 textBoxSelectResult.Text = string.Format("起始坐标:({0},{1}),块大小:{2},像素值:{3}", node.Rectangle.X, node.Rectangle.Y, node.Rectangle.Width*node.Rectangle.Width, node.Value); } else { textBoxSelectResult.Text = @"编码不存在!"; textBoxSelectResult.BackColor=Color.Red;//设置文本框的颜色为红色 } } /// <summary> /// 保存数组文件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ToolStripMenuItemSaveArrarToFile_Click(object sender, EventArgs e) { SaveArrayFile(); } /// <summary> /// 加载编码文件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ToolStripMenuItemLoadCodeFile_Click(object sender, EventArgs e) { LoadCodeFile(); } /// <summary> /// 保存编码好的文件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ToolStripMenuItemSaveCodeFile_Click(object sender, EventArgs e) { SaveCodeFile(); } /// <summary> /// 执行编码 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ToolStripMenuItemPerformCode_Click(object sender, EventArgs e) { PerformCode(); } /// <summary> /// 从影像宽获取初始的深度 /// </summary> /// <param name="width"></param> /// <returns></returns> private int GetOriginalDepth(int width) { if (width >= 4096) { return -1;//影像过大 } int depth = 0; Dictionary<int,int> dic = new Dictionary<int, int>(); dic.Add(1,1); dic.Add(2,2); dic.Add(3, 4); dic.Add(4, 8);//传入的值是8的话,应该返回的dept是4. dic.Add(5, 16); dic.Add(6, 32); dic.Add(7, 64); dic.Add(8, 128); dic.Add(9, 256); dic.Add(10, 512); dic.Add(11, 1024); dic.Add(12, 2048); dic.Add(13, 4096); foreach (KeyValuePair<int,int> item in dic) { if (width > item.Value) { depth = item.Key; } } return depth + 1; } /// <summary> /// 窗体加载 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void LoadArrayFrm_Load(object sender, EventArgs e) { } /// <summary> /// 执行反编码 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ToolStripMenuItemPerformInverseCode_Click(object sender, EventArgs e) { richTextBox1.Clear(); PerformInverseCode(); } /// <summary> /// 执行编码查询 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ToolStripMenuItemPerformSelectCode_Click(object sender, EventArgs e) { } /// <summary> /// 加载数组文件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonRichTextLoad_Click(object sender, EventArgs e) { LoadArrayFile(); } /// <summary> /// 清空richText /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonRichTextClear_Click(object sender, EventArgs e) { richTextBox1.Clear(); } /// <summary> /// 清空DataView /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonDataViewClear_Click(object sender, EventArgs e) { dataGridView1.Rows.Clear(); } /// <summary> /// 保存数组文件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonRichTextSave_Click(object sender, EventArgs e) { SaveArrayFile(); } /// <summary> ///加载编码文件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonDataViewLoad_Click(object sender, EventArgs e) { LoadCodeFile(); } /// <summary> /// 执行编码 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonPerfromCode_Click(object sender, EventArgs e) { PerformCode(); } /// <summary> /// 执行反编码 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonPerformInverseCode_Click(object sender, EventArgs e) { PerformInverseCode(); } /// <summary> /// 执行编码查询 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonSelectCode_Click(object sender, EventArgs e) { PerformSelectCode(); } /// <summary> /// 保存编码文件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonDataViewSave_Click(object sender, EventArgs e) { SaveCodeFile(); } } }
posted on 2016-01-26 13:36 LinuxPanda 阅读(2359) 评论(0) 编辑 收藏 举报