Faster way to perform rectangle merging based on their intersection - c#

this is being used in a motion detection problem.
Basically, I perform a motion detection algorithm on an image, and get a list of blobs, where each blob hopefully corresponds to an object that has moved. However, we have to merge these blobs as there might be many small ones touching each other that should be one large blob.
I merge the blobs together using this algorithm.
//Expand all blobs by 1x1 to ensure that we can use intersection
//blobs is a List<blob>
foreach (Blob blob in blobs)
{
blob.BoundingBox.Inflate(1, 1);
}
bool needToRestartMerging = true;
while (needToRestartMerging == true)
{
int count = blobs.Count;
needToRestartMerging = false;
for (int i = 0; i < count - 1; i++)
{
for (int j = i + 1; j < count; j++)
{
//BoundingBox is a simple System.Drawing.Rectangle
if (blobs[i].BoundingBox.IntersectsWith(blobs[j].BoundingBox))
{
Blob newBlob = blobs[i].Merge(blobs[j]);
blobs.RemoveAt(i);
blobs.RemoveAt(j-1);
blobs.Add(newBlob);
needToRestartMerging = true;
count = blobs.Count;
}
}
}
}
This is how I merge the blobs:
/// <summary>
/// Given a Pixel Location, we resize the Blob so that it is included in the BoundingBox
/// </summary>
/// <param name="x">Pixel XCoordinate</param>
/// <param name="y">Pixel YCoordinate</param>
public void ResizeToPixelLocation(int x, int y)
{
numPixels++;
if (x >= _boundingBox.Right)
{
_boundingBox.Width = x - _boundingBox.X;
}
if (y >= _boundingBox.Bottom)
{
_boundingBox.Height = y - _boundingBox.Y;
}
if (x <= _boundingBox.Left)
{
int oldLeft = _boundingBox.Left;
int xOffset = x - _boundingBox.Left;
_boundingBox.Offset(xOffset, 0);
_boundingBox.Width += (oldLeft - x);
}
if (y <= _boundingBox.Top)
{
int oldTop = _boundingBox.Top;
int yOffset = y - _boundingBox.Top;
_boundingBox.Offset(0, yOffset);
_boundingBox.Height += (oldTop - y);
}
}
/// <summary>
/// Merge this blob with another blob
/// </summary>
/// <param name="blob2">The second blob</param>
/// <returns></returns>
public Blob Merge(Blob blob2)
{
Blob newBlob = new Blob(BoundingBox.X, BoundingBox.Y);
newBlob.ThreadID = this.ThreadID;
newBlob.numPixels = NumPixels + blob2.NumPixels;
newBlob.BoundingBox = BoundingBox;
newBlob.ResizeToPixelLocation(blob2.BoundingBox.X, blob2.BoundingBox.Y);
newBlob.ResizeToPixelLocation(blob2.BoundingBox.Right, blob2.BoundingBox.Bottom);
return newBlob;
}
I may have about 0-150 blobs in all. I'd like to know if there's a faster way to do a blob merging.
Thanks

I would suggest something along the lines of:
mergeConnected(input):
output = new RectangleSet
while input.length > 0 do
nextRect = input.pop()
intersected = output.findIntersected(nextRect)
if intersected then
output.remove(intersected)
input.push(nextRect.merge(intersected))
else
output.insert(nextRect)
done
return output
Every loop iteration either removes from output or adds to output and removes from input, so the total number of loop iterations is no larger than twice the number of output rectangles.
To improve the performance of output.findIntersected, you can represent your set of rectangles as a data structure optimized for intersection searching (as opposed to a plain list). For instance, you can keep a data structure sorted by the maximum X of your rectangles to cull several of them, and then insert rectangles sorted by minimum X. Plain classic solutions, such as kd-trees or adaptive binary trees, could also work, but the insertion/removal time might adversely affect performance.

Related

LibGit2Sharp implementation of showbranch independent

I'm trying to use LibGit2Sharp to recreate the functionality of git show-brach --independent which, according to the docs does this: Among the <reference>s given, display only the ones that cannot be reached from any other <reference>.
My best attempt so far is the following:
List<Commit> GetIndependent(IRepository repo, IEnumerable<Commit> commits)
{
var indep = new List<Commit>();
foreach (var commit in commits)
{
if (repo.Commits.QueryBy(new CommitFilter
{
FirstParentOnly = false,
IncludeReachableFrom = commit,
ExcludeReachableFrom = commits.Where(x => x.Equals(commit) == false)
}).Any())
{
indep.Add(commit);
}
}
return indep;
}
Unfortunately, this becomes astronomically slow as the amount of history increases. It's actually much faster for me to exec git directly, parse the output, and have LibGit2Sharp lookup the resulting SHAs than to use the above code. I assume this has to do with some optimization that Git has but LibGit2 does not. Is this even doing what I want? If so, is there a better way to achieve this in LibGit2Sharp?
I finally found a better way that utilizes merge bases, thanks to this question pointing me in the right direction.
Here's the new code:
/// <summary>
/// Implementation of `git show-branch --indepenent`
///
/// "Among the <reference>s given, display only the ones that cannot be reached from any other <reference>"
/// </summary>
/// <param name="commitsToCheck"></param>
/// <returns></returns>
private List<Commit> GetIndependent(IRepository repo, IEnumerable<Commit> commitsToCheck)
{
var commitList = commitsToCheck.ToList();
for (var i = commitList.Count - 1; i > 0; --i)
{
var first = commitList[i];
for (var j = commitList.Count - 1; j >= 0; --j)
{
if (i == j) continue;
var second = commitList[j];
var mergeBase = repo.ObjectDatabase.FindMergeBase(first, second);
if (first.Equals(mergeBase))
{
// First commit (i) is reachable from second (j), so drop i
commitList.RemoveAt(i);
// No reason to check anything else against this commit
j = -1;
} else if (second.Equals(mergeBase))
{
// Second (j) is reachable from first, so drop j
commitList.RemoveAt(j);
// If this was at a lower index than i, dec i since we shifted one down
if (j < i)
{
--i;
}
}
}
}
return commitList;
}

Point Classification in a set of Bounding Boxes

I have a set of bounding boxes(rectangular) in a 3D space. The bounds of each box are computed and stored in a dictionary named "RegionBounds". Also, a set of points are populated in a List named "PointsToCategorize" Given a point(x,y,z) coordinates from the List populated and a bounding box to be checked in, i can check if the point is inside the box or not. The problem is, this is a big dataset. The number of points to be checked are like 1000 and the no of bounding boxes are like 250-300. So, if i loop through each bounding box for each given point; the total time it takes is like 5-6 minutes. Is there any efficient method that would do the process quicker ? If possible, a small code to do so would be great
public struct iBounds {
public double x1, x2;
public double y1, y2;
public double z1, z2;
}
public struct iPoint {
public double x,y,z
}
Dictionary<String, iBounds> RegionBounds = new Dictionary<String, iBounds>();
List<iPoint> PointsToCategorize = new List<iPoint>();
int no_of_bounding_boxes = 300;
int no_of_points_to_categorize = 1000;
for (int i = 1; i <= no_of_bounding_boxes; i++)
{
String boundingBoxName = "bound_" + i;
iBounds boundingBox = new iBounds
{
x1 = Computed By Some Other method and Formulas,
x2 = Computed By Some Other method and Formulas,
y1 = Computed By Some Other method and Formulas,
y2 = Computed By Some Other method and Formulas,
z1 = Computed By Some Other method and Formulas,
z2 = Computed By Some Other method and Formulas
};
RegionBounds.Add(boundingBoxName, boundingBox);
}
////////////Start of Output section /////////////////////////
for(int i= 1; i < = PointsToCategorize.Count; i++){
foreach(var pair in RegionBounds)
{
String myboxNmame = pair.Key;
iBounds myboxBounds = pair.Value;
Console.WriteLine(PointInside(PointsToCategorize[i],myboxBounds).ToString());
}
}
////////////// End of Output section //////////////////
private bool PointInside(iPoint mypoint, iBounds boxToBeCheckedIn)
{
if (mypoint.x > boxToBeCheckedIn.x1) && (mypoint.x < boxToBeCheckedIn.x2){
if (mypoint.y > boxToBeCheckedIn.y1) && (mypoint.y < boxToBeCheckedIn.y2){
if (mypoint.z > boxToBeCheckedIn.z1) && (mypoint.z < boxToBeCheckedIn.z2){
return true;
}
}
}else{
return false;
}
}
You may want to use a OcTree or a kD-tree data structure, which is way more efficient than iterating through all the boxes.
See also this article at the section 2-D orthogonal range searching, it has a very good resume of available techniques and algorithms, which are easily extendable to 3D

How to search for values in Point3DCollection?

Basically I am using a MeshGeometry3D to load points, positions and normals from an STL file.
The STL file format duplicates points, so I want to first search the MeshGeometry3D.Positions for duplicate before adding the newly read point.
The Mesh.Positions.IndexOf(somePoint3D) does not work, because it compares based on the object reference rather than the X, Y, Z values of the Point3D. This is why I am iterating the entire Collection to manually find duplicates:
//triangle is a custom class containing three vertices of type Point3D
//for each 3 points read from STL the triangle object is reinitialized
vertex1DuplicateIndex = -1;
vertex2DuplicateIndex = -1;
vertex3DuplicateIndex = -1;
for (int q = tempMesh.Positions.Count - 1; q >= 0; q--)
{
if (vertex1DuplicateIndex != -1)
if (tempMesh.Positions[q] == triangle.Vertex1)
vertex1DuplicateIndex = q;
if (vertex2DuplicateIndex != -1)
if (tempMesh.Positions[q] == triangle.Vertex2)
vertex2DuplicateIndex = q;
if (vertex3DuplicateIndex != -1)
if (tempMesh.Positions[q] == triangle.Vertex3)
vertex3DuplicateIndex = q;
if (vertex1DuplicateIndex != -1 && vertex2DuplicateIndex != -1 && vertex3DuplicateIndex != -1)
break;
}
This code is actually very efficient when duplicates are found, but when there is no duplicate the collection is iterated entirely which is very slow for big meshes, with more than a million positions.
Is there another approach on the search?
Is there a way to force Mesh.Positions.IndexOf(newPoint3D) to compare based on value like the Mesh.Positions[index]==(somePoint3D), rather than the reference comparison it is doing now?
I don't know of a built in way to do this, but you could use a hash map to cache the indices of the 3D vectors.
Depending of the quality of your hash functions for the vectors you'll have a 'sort of' constant lookup (no collisions are impossible, but it should be faster than iterating though all the vertex data for each new triangle point).
Using hakononakani's idea I've managed to speed up a bit, using a combination of a HashSet and a Dictionary. The following is a simplified version of my code:
class CustomTriangle
{
private Vector3D normal;
private Point3D vertex1, vertex2, vertex3;
}
private void loadMesh()
{
CustomTriangle triangle;
MeshGeometry3D tempMesh = new MeshGeometry3D();
HashSet<string> meshPositionsHashSet = new HashSet<string>();
Dictionary<string, int> meshPositionsDict = new Dictionary<string, int>();
int vertex1DuplicateIndex, vertex2DuplicateIndex, vertex3DuplicateIndex;
int numberOfTriangles = GetNumberOfTriangles();
for (int i = 0, j = 0; i < numberOfTriangles; i++)
{
triangle = ReadTriangleDataFromSTLFile();
vertex1DuplicateIndex = -1;
if (meshPositionsHashSet.Add(triangle.Vertex1.ToString()))
{
tempMesh.Positions.Add(triangle.Vertex1);
meshPositionsDict.Add(triangle.Vertex1.ToString(), tempMesh.Positions.IndexOf(triangle.Vertex1));
tempMesh.Normals.Add(triangle.Normal);
tempMesh.TriangleIndices.Add(j++);
}
else
{
vertex1DuplicateIndex = meshPositionsDict[triangle.Vertex1.ToString()];
tempMesh.TriangleIndices.Add(vertex1DuplicateIndex);
tempMesh.Normals[vertex1DuplicateIndex] += triangle.Normal;
}
//Do the same for vertex2 and vertex3
}
}
At the end tempMesh will have only unique points. All you have to do is normalize all Normals and you're ready to visualize.
The same can be achieved only with the Dictionary using:
if (!meshPositionsDict.Keys.Contains(triangle.Vertex1.ToString()))
I just like using the HashSet, because it's fun to work with :)
In both cases the final result is a ~60 times faster algorithm than before!

Get Y value across .NET chart series from X value

using C# with .NET chart.
I am trying to graph several waveforms, and I wish to move my mouse across the chart area, and have my tooltip display the Y value of each series in the chart at this X value location.
| at xValue 12 | |
| _ = 3 | |
| * = 2 | * * |
| ________|______________________________*_____ |
| / | * |
| __________*/*********|***************************** |
| * | |
| * | |
|______________________|_____________________________________|
Kind of like this diagram above. Below is a version of my code:
void chart1_MouseMove(object sender, MouseEventArgs e)
{
var pos = e.Location;
_point.X = e.Location.X;
_point.Y = e.Location.Y;
try
{
if ((chart1.ChartAreas[0].AxisX.PixelPositionToValue(e.X) >= 0) && (chart1.ChartAreas[0].AxisX.PixelPositionToValue(e.X) <= max))
{
//Crossair
chart1.ChartAreas[0].CursorX.SetCursorPixelPosition(_point, true);
//Tooltips
double xValue = chart1.ChartAreas[0].AxisX.PixelPositionToValue(e.X);
double yValue = chart1.ChartAreas[0].AxisY.PixelPositionToValue(e.Y);
string all_Data_Values = "";
foreach (var series in chart1.Series)
{
all_Data_Values = all_Data_Values + Environment.NewLine + series.Name + ": " + yValue;
}
tooltip.Show("At " + Math.Truncate(xValue * 1000) / 1000 + all_Data_Values, this.chart1, pos.X - 40, pos.Y - 20);
}
}
catch (Exception exception)
{
//
}
}
This is what I have, and right now it only displays the Y value of my mouse cursor location. I have tried other codes, trying to somehow map the x values into chart1.Series[] but it didn't work either.
(This is in response to the request for code to look up the nearest value given a pixel coord.)
I'm doing it a bit differently from you, because I'm actually setting the chart's "cursor" as the user moves the mouse around, but hopefully this will give you enough information for you to adapt it to your needs...
Here's how I calculate the X axis coord for a client X coord:
private double calcCursorGraphX(int clientX)
{
var xAxis = _chart.ChartAreas[CHART_INDEX].AxisX;
int xRight = (int) xAxis.ValueToPixelPosition(xAxis.Maximum) - 1;
int xLeft = (int) xAxis.ValueToPixelPosition(xAxis.Minimum);
if (clientX > xRight)
{
return xAxis.Maximum;
}
else if (clientX < xLeft)
{
return xAxis.Minimum;
}
else
{
return xAxis.PixelPositionToValue(clientX);
}
}
Given an X value returned from the above method, we can look up the nearest preceeding value:
private int nearestPreceedingValue(double x)
{
var bpData = _chart.Series[SERIES_INDEX].Points;
int bpIndex = bpData.BinarySearch(x, (xVal, point) => Math.Sign(x - point.XValue));
if (bpIndex < 0)
{
bpIndex = ~bpIndex; // BinarySearch() returns the index of the next element LARGER than the target.
bpIndex = Math.Max(0, bpIndex-1); // We want the value of the previous element, so we must decrement the returned index.
} // If this is before the start of the graph, use the first valid data point.
return bpIndex;
}
Then you have an index which you can use to look up the value from _chart.Series[SERIES_INDEX].Points
I'm not sure if this fits with the way that your data is stored in the charts, but that's how I do it.
[EDIT] Here's the missing BinarySearch extension method. Add it to a static class somewhere accessible. Replace the "Contracts" stuff with your own error handling if you're not using Code Contracts.
/// <summary>
/// Searches the entire sorted IList{T} for an element using the specified comparer
/// and returns the zero-based index of the element.
/// </summary>
/// <typeparam name="TItem">The type of the item.</typeparam>
/// <typeparam name="TSearch">The type of the searched item.</typeparam>
/// <param name="list">The list to be searched.</param>
/// <param name="value">The value to search for.</param>
/// <param name="comparer">The comparer that is used to compare the value with the list items.</param>
/// <returns>
/// The zero-based index of item in the sorted IList{T}, if item is found;
/// otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item,
/// or - if there is no larger element - the bitwise complement of Count.
/// </returns>
public static int BinarySearch<TItem, TSearch>(this IList<TItem> list, TSearch value, Func<TSearch, TItem, int> comparer)
{
Contract.Requires(list != null);
Contract.Requires(comparer != null);
int lower = 0;
int upper = list.Count - 1;
while (lower <= upper)
{
int middle = lower + (upper - lower) / 2;
int comparisonResult = comparer(value, list[middle]);
if (comparisonResult < 0)
{
upper = middle - 1;
}
else if (comparisonResult > 0)
{
lower = middle + 1;
}
else
{
return middle;
}
}
return ~lower;
}

C# Changing only part of an array, in random places

I've got a 2D array of int's in a console c# code (7X3 'cells'). The moment I create it, it is initialized with zeros.
I've got to change the value of only five 'cells' into 1, without changing the value of other 'cells'. I know it is a basic thing, but, as an absolute rookie in C#, I can't get it to work, no matter what cell I use. Can you help me?
Thanks in advance.
(Note: As I am a frequent user/contributer, I know it looks like a classic "Do my homework" question and I apologize, but as I'm really stuck on this weenie part (and the rest of my project is OK), I'd appreciate the help).
Here are two ways of solving this issue, the first one; called BruteForceRandomImplementation is easy to implement and understand but quite slow once you attempt to mark a very large number of locations with 1s due to it's brute-force like usage of Random till it fills enough locations.
/// <summary>
/// This method uses a random based algorithm to create a two-dimensional array of [height, width]
/// with exactly locationsToFind locations set to 1.
/// </summary>
/// <param name="height"></param>
/// <param name="width"></param>
/// <param name="locationsToFind"></param>
/// <returns></returns>
public int[,] BruteForceRandomImplementation(int height, int width, int locationsToFind)
{
var random = new Random();
locationsToFind = LimitLocationsToFindToMaxLocations(height, width, locationsToFind);
// Create our two-dimensional array.
var map = new int[height, width];
int locationsFound = 0;
// Randomly set positons to 1 untill we have set locationsToFind locations to 1.
while (locationsFound < locationsToFind)
{
// Get a random Y location - limit the max value to our height - 1.
var randomY = random.Next(height);
// Get a random X location - limit the max value to our width - 1.
var randomX = random.Next(width);
// Find another random location if this location is already set to 1.
if (map[randomY, randomX] == 1)
continue;
// Otherwise set our location to 1 and increment the number of locations we've found.
map[randomY, randomX] = 1;
locationsFound += 1;
}
return map;
}
With the following helper method to keep our locationsToFind range sane:
/// <summary>
/// Limits the locationsToFind variable to the maximum available locations. This avoids attempting to
/// mark more locations than are available for our width and height.
/// </summary>
/// <param name="height"></param>
/// <param name="width"></param>
/// <param name="locationsToFind"></param>
/// <returns></returns>
public int LimitLocationsToFindToMaxLocations(int height, int width, int locationsToFind)
{
return Math.Min(locationsToFind, height * width);
}
My second implementation called ShuffleImplementation is a lot faster when you are marking large numbers of unique locations. It creates a one-dimensional array, fills this with enough 1s to satisify your needs then shuffles this array using the Fisher-Yates shuffle to finally proceed to expand this one-dimensional array into a two-dimensional one:
/// <summary>
/// This method uses a shuffle based algorithm to create a two-dimensional array of [height, width]
/// with exactly locationsToFind locations set to 1.
/// </summary>
/// <param name="height"></param>
/// <param name="width"></param>
/// <param name="locationsToFind"></param>
/// <returns></returns>
public int[,] ShuffleImplementation(int height, int width, int locationsToFind)
{
locationsToFind = LimitLocationsToFindToMaxLocations(height, width, locationsToFind);
// Create a 1 dimensional array large enough to contain all our values.
var array = new int[height * width];
// Set locationToFind locations to 1.
for (int location = 0; location < locationsToFind; location++)
array[location] = 1;
// Shuffle our array.
Shuffle(array);
// Now let's create our two-dimensional array.
var map = new int[height, width];
int index = 0;
// Expand our one-dimensional array into a two-dimensional one.
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
map[y, x] = array[index];
index++;
}
}
return map;
}
/// <summary>
/// Shuffles a one-dimensional array using the Fisher-Yates shuffle.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="arr"></param>
public static void Shuffle<T>(T[] array)
{
var random = new Random();
for (int i = array.Length - 1; i > 0; i--)
{
int n = random.Next(i + 1);
Swap(ref array[i], ref array[n]);
}
}
/// <summary>
/// Swaps two values around.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="valueA"></param>
/// <param name="valueB"></param>
public static void Swap<T>(ref T valueA, ref T valueB)
{
T tempValue = valueA;
valueA = valueB;
valueB = tempValue;
}
Usage:
var map = BruteForceRandomImplementation(7, 3, 5);
Or:
var map = ShuffleImplementation(7, 3, 5);
Depending on which one you want to use. For a good example of the performance difference between the two, try:
int width = 1000;
int height = 1000;
int locationsToFind = (width * height) - 1;
var stopwatch = new Stopwatch();
stopwatch.Start();
BruteForceRandomImplementation(height, width, locationsToFind);
stopwatch.Stop();
Console.WriteLine(string.Format("BruteForceRandomImplementation took {0}ms", stopwatch.ElapsedMilliseconds));
stopwatch.Restart();
ShuffleImplementation(height, width, locationsToFind);
stopwatch.Stop();
Console.WriteLine(string.Format("ShuffleImplementation took {0}ms", stopwatch.ElapsedMilliseconds));
On my laptop BruteForceRandomImplementation took 1205ms and ShuffleImplementation took 67ms or nearly 18x faster.
So you've defined a multi-dimensional array (basically a matrix):
int[,] cells = new int[7,3];
And then you initialize all of the values to 0.
To change a value, you should be able to do it like this, for example:
cells[3,2] = 1;
Is that what you've tried? Are you receiving any exceptions or warnings?
If you're using jagged arrays (arrays of arrays), then it might be like:
int[][] cells = new int[7][3];
And then you can set values:
cells[3][2] = 1;
var array = new int[7,3];
array[5,2] = 1;
array[3,2] = 1;
Keep in mind that the array indices are zero-based, so the valid range of int[7,3] is [0..6,0..2]
int[,] cells = new int[7,3];
cells[3,2] = 1;

Categories

Resources