I have a polygon, consisting of 2D points representing pixel coordinates, in an internal data structure. I need this polygon as an HALCON region (HRegion). The conversion is supposed to happen like that:
HTuple hCols, hRows;
for (auto n = 0; n < nNodes; ++n)
{
auto v2dNode = GetNode(n);
hCols.Append(v2dNode.GetX());
hRows.Append(v2dNode.GetY());
}
HalconCpp::HObject hContour;
HalconCpp::GenContourPolygonXld(&hContour, hRows, hCols);
HalconCpp::HObject hRegion;
HalconCpp::GenRegionContourXld(hContour, &hRegion, "filled");
Whereas the contour (HContour) is valid, according to Halcon Variable Inspect, the created region (hRegion) seems to be empty. HRegion::IsInitialized returns true, but HRegion::AreaCenter would return zero for both area and position, which is clearly wrong.
There's constructor versions of these function calls (e.g. GenContourPolygonXld), too, using the "iconic" types HXLDCont and HRegion, which result in an incorrect region as well.
What I also tried is to serialize the contour, save it in an file and load it in HDevelop. There, the corresponding code does create a valid region:
open_file('D:/HContour.mvt', 'input_binary', hFile)
fread_serialized_item(hFile, hSer)
deserialize_xld(hContour, hSer)
close_file(hFile)
gen_region_contour_xld(hContour, hRegion, 'filled')
area_center(hRegion, Area, Row, Column)
In C# I also loaded that contour file and tried to create the corresponding region. That approach resulted in an incorrectly empy region however, too:
HObject hObj;
using (var hFile = new HFile(#"D:\\HContour.mvt", "input_binary"))
{
FreadSerializedItem(hFile, out var hSerialized);
DeserializeXld(out hObj, hSerialized);
}
var hContour = new HXLDCont(hObj);
var hRegion = hContour.GenRegionContourXld("filled");
var area = hRegion.AreaCenter(out double row, out var col);
Console.WriteLine($"Area: {area}, Center: {col}|{row}");
The Halcon version is 12.0.3.
Is there a bug in the library, or am I doing it wrong in the C++ and C# code?
Edit:
Before any Halcon code is executed, the following settings are made:
HalconCpp::ResetObjDb(5000, 5000, 1);
HalconCpp::SetSystem("clip_region", "false");
HalconCpp::SetSystem("store_empty_region", "true");
All coordinates are in a valid range, and regions are not clipped.
The contour that has been used for testing is this.
Could it be that your region is partially outside of predefined region work space. Meaning that some of the pixels have coordinates less than zero?
If that's the case, all you need to do before loading is run this command:
set_system ('clip_region', 'false')
Related
For each word I am creating an object of LocationTextExtractionStrategy class to get its coordinates but the problem is each time I pass a word it is returning coordinates of all the chunks of that word present in pdf. How can i get coordinates of the word present at specific position or in a specific line?
I found a code somewhere
namespace PDFAnnotater
{
public class RectAndText
{
public iTextSharp.text.Rectangle Rect;
public string Text;
public RectAndText(iTextSharp.text.Rectangle rect, string text)
{
this.Rect = rect;
this.Text = text;
}
}
public class MyLocationTextExtractionStrategy : LocationTextExtractionStrategy
{
public List<RectAndText> myPoints = new List<RectAndText>();
public string TextToSearchFor { get; set; }
public System.Globalization.CompareOptions CompareOptions { get; set; }
public MyLocationTextExtractionStrategy(string textToSearchFor, System.Globalization.CompareOptions compareOptions = System.Globalization.CompareOptions.None)
{
this.TextToSearchFor = textToSearchFor;
this.CompareOptions = compareOptions;
}
public override void RenderText(TextRenderInfo renderInfo)
{
base.RenderText(renderInfo);
var startPosition = System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(renderInfo.GetText(), this.TextToSearchFor, this.CompareOptions);
//If not found bail
if (startPosition < 0)
{
return;
}
var chars = renderInfo.GetCharacterRenderInfos().Skip(startPosition).Take(this.TextToSearchFor.Length).ToList();
//Grab the first and last character
var firstChar = chars.First();
var lastChar = chars.Last();
//Get the bounding box for the chunk of text
var bottomLeft = firstChar.GetDescentLine().GetStartPoint();
var topRight = lastChar.GetAscentLine().GetEndPoint();
//Create a rectangle from it
var rect = new iTextSharp.text.Rectangle(
bottomLeft[Vector.I1],
bottomLeft[Vector.I2],
topRight[Vector.I1],
topRight[Vector.I2]
);
this.myPoints.Add(new RectAndText(rect, this.TextToSearchFor));
}
}
}
I am passing words from an array to check for its coordinates. The problem is that RenderText() method is automatically called again and again for each chunk and returns the list of coordinates of the word present at different places in the pdf. For example if i need coordinate of '0' it is returning 23 coordinates. What should I do or modify in the code to get the exact coordinate of the word?
Your question is a bit confusing.
How can I get coordinates of the word present at specific position
In that statement you're basically saying "How can I get the coordinates of something that I already know the coordinates of?" Which is redundant.
I'm going to interpret your question as "How can I get the coordinates of a word, if I know the approximate location?"
I'm not familiar with C#, but I assume there are methods similar to the ones in Java for working with Rectangle objects.
Rectangle#intersects(Rectangle other)
Determines whether or not this Rectangle and the specified Rectangle intersect.
and
Rectangle#contains(Rectangle other)
Tests if the interior of the Shape entirely contains the specified Rectangle2D.
Then the code becomes trivially easy.
You use LocationTextExtractionStrategy to fetch all the iText based rectangles
you convert them to native rectangle objects (or write your own class)
for every rectangle you test whether the given search region contains that rectangle, keeping only those that are within the search region
If you want to implement your second use-case (getting the location of a word if you know the line) then there are two options:
you know the rough coordinates of the line
you want this to work given a line number
For option 1:
build a search region. Use the bounds of the page to get an idea of the width (since the line could stretch over the entire width), and add some margin y)-coordinates (to account for font differences, subscript and superscript, etc)
Now that you have a search region, this reverts to my earlier answer.
For option 2:
you already have the y coordinate of every word
round those (to the nearest multiple of fontsize)
build a Map where you keep track of how many times a certain y-coordinate is used
remove any statistical outliers
put all these values in a List
sort the list
This should give you a rough idea of where you can expect a given line(number) to be.
Of course, similar to my earlier explanation, you will need to take into account some padding and some degree of flexibility to get the right answer.
I am drawing a line on a graph from numbers read from a text file. There is a number on each line of the file which corresponds to the X co-ordinate while the Y co-ordinate is the line it is on.
The requirements have now changed to include "special events" where if the number on the line is followed by the word special a spike will appear like image below:
Currently the only way I can find is to use a line for each spike, however there could be a large of these special events and so needs to be modular. This seems an efficient and bad way to program it.
Is it possible to add the spikes to the same graph line? Or is it possible to use just one additional line and have it broken (invisible) and only show where the spikes are meant to be seen?
I have looked at using bar graphs but due to other items on the graph I cannot.
The DataPoints of a Line Chart are connected so it is not possble to really break it apart. However each segment leading to a DataPoint can have its own color and that includes Color.Transparent which lends itself to a simple trick..
Without adding extra Series or Annotations, your two questions can be solved like this:
To simply add the 'spikes' you show us in the 2nd graph, all you need to do is to insert 2 suitable datapoints, the 2nd being identical to the point the spike is connected to.
To add an unconnected line you need to 'jump' to its beginning by adding one extra point with a transparent color.
Here are two example methods:
void addSpike(Series s, int index, double spikeWidth)
{
DataPoint dp = s.Points[index];
DataPoint dp1 = new DataPoint(dp.XValue + spikeWidth, dp.YValues[0]);
s.Points.Insert(index+1, dp1);
s.Points.Insert(index+2, dp);
}
void addLine(Series s, int index, double spikeDist, double spikeWidth)
{
DataPoint dp = s.Points[index];
DataPoint dp1 = new DataPoint(dp.XValue + spikeDist, dp.YValues[0]);
DataPoint dp2 = new DataPoint(dp.XValue + spikeWidth, dp.YValues[0]);
DataPoint dp0 = dp.Clone();
dp1.Color = Color.Transparent;
dp2.Color = dp.Color;
dp2.BorderWidth = 2; // optional
dp0.Color = Color.Transparent;
s.Points.Insert(index + 1, dp1);
s.Points.Insert(index + 2, dp2);
s.Points.Insert(index + 3, dp0);
}
You can call them like this:
addSpike(chart1.Series[0], 3, 50d);
addLine(chart1.Series[0], 6, 30d, 80d);
Note that they add 2 or 3 DataPoints to the Points collection!
Of course you can set the Color and width (aka BorderWidth) of the extra lines as you wish and also include them in the params list..
If you want to keep the points collection unchanged you also can simply create one 'spikes series' and add the spike points there. The trick is to 'jump' to the new points with a transparent line!
A little about the application;
The application allows the user to draw and save polygons onto bing maps WPF api.
The piece of code we are interested in is finding weather a point is whithin the polygon or not. The following function simply loops through the LocationCollection of the polygon on the bing map, and creates a SqlGeography object (OpenGisGeographyType.Polygon) which is an instance of a polygon.
We then convert the mouse click into SqlGeography object (OpenGisGeographyType.Point) by latitude and longitude and use the SqlGeography.STIntersection to find if our point lies within the polygon.
As seen in the picture, even when the point is outside the polygon, SqlGeography.STIntersection still returns a point of intersection. (You can tell this in the picture as I set a label to "Within Delivery Area" or "Customer our of Area" depending on what polygonSearch() function returned.
The right example in the picture has the expected results when a location is tested within the polygon.
The left example in the picture contains the unexpected results - Which indicates that a point is within a polygon , when it clearly is not!
NOTES:
I Use SqlGeography (myShape) to put the shapes on the map, so I know
the shape is being constructed with proper verticies.
I use SqlGeography (myPoint) to put the pin on the map, so I know the
pin is being tested at the correct verticies.
THIS ONLY FAILS ON LARGE POLYGONS
Below I give the peice of code which creates the polygon in memory, as well as converts the mouse click event to lat, longitude. ( I have included the polygon verticies in comments so that this can be looked at without the need of the bing api, just replace the for loop with the comments above it ) Although, you will need to reference Microsoft.SqlServer.Types.dll to create the SqlGeography objects. Its free with SQL Express 2008 , and can be found in C:\Program Files (x86)\Microsoft SQL Server\100\SDK\Assemblies
public bool polygonSearch2(LocationCollection points, double lat, double lon)
{
SqlGeography myShape = new SqlGeography();
SqlGeographyBuilder shapeBuilder = new SqlGeographyBuilder();
// here are the verticies for the location collection if you want to hard code and try
//shapeBuilder.BeginFigure(47.4275329011347, -86.8136038458706);
//shapeBuilder.AddLine(36.5102408627967, -86.9680936860962);
//shapeBuilder.AddLine(37.4928909385966, -80.2884061860962);
//shapeBuilder.AddLine(38.7375329179818, -75.7180936860962);
//shapeBuilder.AddLine(48.0932596736361, -83.7161405610962);
//shapeBuilder.AddLine(47.4275329011347, -86.8136038458706);
//shapeBuilder.EndFigure();
//shapeBuilder.EndGeography();
// Here I just loop through my points collection backwards to create the polygon in the SqlGeography object
for (int i = points.Count - 1; i >= 0; i--)
{
if (i == 0)
{
shapeBuilder.AddLine(points[i].Latitude, points[i].Longitude);
shapeBuilder.EndFigure();
shapeBuilder.EndGeography();
continue;
}
if (i == points.Count - 1)
{
shapeBuilder.SetSrid(4326);
shapeBuilder.BeginGeography(OpenGisGeographyType.Polygon);
shapeBuilder.BeginFigure(points[i].Latitude, points[i].Longitude);
continue;
}
else
{
shapeBuilder.AddLine(points[i].Latitude, points[i].Longitude);
}
}
myShape = shapeBuilder.ConstructedGeography;
// Here I am creating a SqlGeography object as a point (user mouse click)
SqlGeography myPoint = new SqlGeography();
SqlGeographyBuilder pointBuilder = new SqlGeographyBuilder();
pointBuilder.SetSrid(4326);
pointBuilder.BeginGeography(OpenGisGeographyType.Point);
// Should pass, which it does
// Lat: lat = 43.682110574649791 , Lon: -79.79005605528323
// Should fail, but it intersects??
// Lat: 43.682108149690094 , Lon: -79.790037277494889
pointBuilder.BeginFigure(lat, lon);
pointBuilder.EndFigure();
pointBuilder.EndGeography();
myPoint = pointBuilder.ConstructedGeography;
SqlGeography result = myShape.STIntersection(myPoint);
if (result.Lat.IsNull)
return false;
else
return true;
}
Any help at all is much appreciated, I am starting to drive my boss nuts with this problem >.<
Could this have anything to do with the SRID?
I fixed this by converting all my polygon lat / long to a Point object on the screen using LocationToViewPortpoint function, as well as the point I'm testing for intersection, and use the X and Y values instead of lat / long in my STIntersects.
I'm using C# charts to display/compare some data. I changed the graph scale to logarithmic (as my data points have huge differences) but since logarithmic scaling doesn't support zero values, I want to just add an empty point (or skip a data point) for such cases. I have tried the following but non works and all crashes:
if (/*the point is zero*/)
{
// myChart.Series["mySeries"].Points.AddY(null);
// or
// myChart.Series["mySeries"].Points.AddY();
// or just skip the point
}
Is it possible to add an empty point or just skip a point?
I found two ways to solve the problem.
One is using double.NaN (suddenly!):
if (myChart.ChartAreas[0].AxisY.IsLogarithmic && y == 0)
myChart.Series["mySeries"].Points.AddY(double.NaN);
// or ...Points.Add(double.NaN)
This looks like zero
And in my case it didn't crash with following SeriesChartTypes:
Column, Doughnut, FastPoint, Funnel, Kagi, Pie, Point, Polar, Pyramid, Radar, Renko, Spline, SplineArea, StackedBar, StackedColumn, ThreeLineBreak
The other way is a built-in concept of an empty point:
if (myChart.ChartAreas[0].AxisY.IsLogarithmic && y == 0)
myChart.Series["mySeries"].Points.Add(new DataPoint { IsEmpty = true });
This looks like a missing point (a gap):
And in my case it didn't crash with following SeriesChartTypes:
Area, Bar, Column, Doughnut, FastPoint, Funnel, Line, Pie, Point, Polar, Pyramid, Radar, Renko, Spline, SplineArea, StackedArea, StackedArea100, StackedBar, StackedBar100, StackedColumn, StackedColumn100, StepLine, ThreeLineBreak
The 2nd approach feels like the right (by design) one. The 1st one looks like a hack, that accidentally appears to work.
You can do a trick. You obviously have to clear you series. When you start adding points to an empty points collection x-coordinates are generated automatically starting at 1. You can create surrogate x yourself and skip some x-values.
int x = 0;
foreach(var y in yValues)
{
x++;
if (myChart.ChartAreas[0].AxisY.IsLogarithmic && y == 0)
continue;
myChart.Series["mySeries"].Points.AddXY(x, y);
}
With bar-like chart types it will look like a missing value.
I have a function where I try to find a matching Point between 2 collections of 4 Points each, but sometimes the function reports the collections do not share a common Point even though in the debugger I see they do. is the debugger not showing me the full precision of the points so I do not see the difference? or is there something else going on here? here's the code to blame:
public static Point CorrectForAllowedDrawArea(Point previousDisplayLocation, Point newDisplayLocation, Rect displayLimitedArea, Rect newBoundingBox)
{
// get area that encloses both rectangles
Rect enclosingRect = Rect.Union(displayLimitedArea, newBoundingBox);
// get corners of outer rectangle, index matters for getting opposite corner
var outsideCorners = new[] { enclosingRect.TopLeft, enclosingRect.TopRight, enclosingRect.BottomRight, enclosingRect.BottomLeft }.ToList();
// get corners of inner rectangle
var insideCorners = new[] { displayLimitedArea.TopLeft, displayLimitedArea.TopRight, displayLimitedArea.BottomRight, displayLimitedArea.BottomLeft }.ToList();
// get the first found corner that both rectangles share
Point sharedCorner = outsideCorners.FirstOrDefault((corner) => insideCorners.Contains(corner));
// find the index of the opposite corner
int oppositeCornerIndex = (outsideCorners.IndexOf(sharedCorner) + 2) % 4;
on the last line 'sharedCorner' is sometimes set to default(Point) even though both Point collections appear to share 1 Point.
EDIT: I should mention if I place the debugger back to the top of the function and restart it still does not find the matching point. I should also mention that this function uses the Point class of the System.Windows namespace and not of the System.Drawing namespace! Thanks for pointing this out to me in the comments.
We really need to see what the definition of insideCorners.Contains(corner) is, but I suspect that your problem is due to the inherent inaccuracies with floating point numbers.
You cannot compare two floating point values like this:
if (a == b)
{
// Values are equal
}
especially if either a or b are calculated values.
You'll need to implement something along the lines of:
if (Math.Abs(a - b) < some_small_value)
{
// Values are equal
}