public Node(Tree tree, int x, int y, int width, int height) { if (width < 1 || height < 1) { throw new ArgumentOutOfRangeException(); } this.mX = x; this.mY = y; this.mWidth = width; this.mHeight = height; // Test if this node requires splitting. if (tree.IsNodeSplitRequired(this) == false) { // Assign colour - this is a leaf. mColour = tree.GetColourForNode(this); } else { // Split into four (half width, half height) sections. int newWidth = width / 2; int newHeight = height / 2; mChildNodes = new Node[4] { null, null, null, null }; Node childNW = new Node(tree, x, y, newWidth, newHeight); mChildNodes[Node.NorthWest] = childNW; Node childSW = new Node(tree, x, y + newHeight, newWidth, newHeight); mChildNodes[Node.SouthWest] = childSW; Node childSE = new Node(tree, x + newWidth, y + newHeight, newWidth, newHeight); mChildNodes[Node.SouthEast] = childSE; Node childNE = new Node(tree, x + newWidth, y, newWidth, newHeight); mChildNodes[Node.NorthEast] = childNE; } }
private void recursivelyGenerateTable(Node current, TableData tbd) { if (current.IsLeaf == false) { // If not a leaf, introduce a nested table. Table tbl = new Table(); tbl.BackgroundColour = tbd.BackgroundColour; tbd.ChildElement = tbl; // Add children to this nested table in NW, NE, SW, SE order. TableRow tbr = null; for (int i = 0; i < 4; ++i) { if (i % 2 == 0) { tbr = new TableRow(); tbl.Rows.Add(tbr); } TableData tbdd = new TableData(); tbr.Cells.Add(tbdd); recursivelyGenerateTable(current.ChildNodes[i], tbdd); } } else { // If a leaf, simply set the colour on the current element. tbd.BackgroundColour = current.Colour; tbd.Height = current.Height; tbd.Width = current.Width; tbd.ColumnSpan = 1; } }
public bool IsNodeSplitRequired(Node node) { // Minimum width check. if (node.Width / 2 < mMinSize) { return false; } // Minimum height check. if (node.Height / 2 < mMinSize) { return false; } // Variance check. float variance = 0; unsafe { byte* imagePtr; // Calculate node average per channel. imagePtr = (byte*) (mImage.Scan0 + (mImage.Stride * node.Y) + (node.X * 3)); float tR = 0, tG = 0, tB = 0; for (int i = 0; i < node.Height; ++i) { for (int j = 0; j < node.Width; ++j) { // Bitmap data held in BGR. tR += imagePtr[2]; tG += imagePtr[1]; tB += imagePtr[0]; imagePtr += 3; } imagePtr += mImage.Stride - (node.Width * 3); } tR /= node.Area; tG /= node.Area; tB /= node.Area; // Now calculate variance over all channels. imagePtr = (byte*) (mImage.Scan0 + (mImage.Stride * node.Y) + (node.X * 3)); for (int i = 0; i < node.Height; ++i) { for (int j = 0; j < node.Width; ++j) { // Bitmap data held in BGR. float cR = imagePtr[2] - tR; float cG = imagePtr[1] - tG; float cB = imagePtr[0] - tB; variance += (cR * cR) + (cG * cG) + (cB * cB); imagePtr += 3; } imagePtr += mImage.Stride - (node.Width * 3); } } // If avg. variance is over threshold (multiplied by 3 due to 3 channels) split. if (variance >= mThreshold * mThreshold * 3 * node.Area) { return true; } return false; }
/// <summary> /// Gets the average colour for this noe from the image. /// </summary> /// <returns> /// The colour for node. /// </returns> /// <param name='node'> /// Node. /// </param> public Color GetColourForNode(Node node) { int tR = 0, tG = 0, tB = 0; unsafe { byte* imagePtr = (byte*) (mImage.Scan0 + (mImage.Stride * node.Y) + (node.X * 3)); for (int i = 0; i < node.Height; ++i) { for (int j = 0; j < node.Width; ++j) { // Bitmap data held in BGR. tR += imagePtr[2]; tG += imagePtr[1]; tB += imagePtr[0]; imagePtr += 3; } imagePtr += mImage.Stride - (node.Width * 3); } } tR /= node.Area; tG /= node.Area; tB /= node.Area; return Color.FromArgb(tR, tG, tB); }
public Tree(BitmapData img, int minSize, int threshold) { if (minSize < 1) { throw new ArgumentOutOfRangeException(); } this.mMinSize = minSize; this.mImage = img; this.mThreshold = threshold; this.mRootNode = new Node(this, 0, 0, img.Width, img.Height); }