ML.Net Rounding Float32 Results to 0 or 1 - c#

using System.Drawing;
using Microsoft.ML;
using Microsoft.ML.Data;
using Microsoft.ML.Transforms.Image;
namespace OnnxTest;
public static class Program
{
public static void Main(string[] args)
{
var tags = File.ReadLines(#"C:\Users\da3ds\Downloads\deepdanbooru-v3-20211112-sgd-e28\tags.txt");
var imageLocation = #"C:\Users\da3ds\Pictures\image.jpg";
var modelLocation = #"C:\Users\da3ds\Downloads\deepdanbooru-v3-20211112-sgd-e28\model-resnet-custom_v3.onnx";
MLContext mlContext = new MLContext();
Console.WriteLine("Read model");
Console.WriteLine($"Model location: {modelLocation}");
Console.WriteLine(
$"Default parameters: image size=({InputModel.imageWidth},{InputModel.imageHeight})");
Console.WriteLine($"Images location: {imageLocation}");
Console.WriteLine("");
Console.WriteLine("=====Identify the objects in the images=====");
Console.WriteLine("");
// Create IDataView from empty list to obtain input data schema
var data = new InputModel { ImagePath = imageLocation };
// Define scoring pipeline
var predictionEngine = GetPredictionEngine(mlContext, modelLocation);
var outputs = predictionEngine.Predict(data);
var outputMapped = tags.Zip(outputs.Scores).Select(t => new { Tag = t.First, f = t.Second })
.ToDictionary(a => a.Tag, a => a.f);
var outputTags = outputMapped.Where(a => Math.Abs(a.Value - 1) < 0.00001f).Select(a => a.Key).OrderBy(a => a)
.ToList();
}
private static PredictionEngine<InputModel, OutputModel> GetPredictionEngine(MLContext mlContext, string modelLocation)
{
var estimator = mlContext.Transforms.LoadImages(InputModel.ModelInput, "", nameof(InputModel.ImagePath))
.Append(mlContext.Transforms.ResizeImages(InputModel.ModelInput, InputModel.imageWidth,
InputModel.imageHeight, InputModel.ModelInput, ImageResizingEstimator.ResizingKind.IsoPad))
.Append(mlContext.Transforms.ExtractPixels(InputModel.ModelInput, InputModel.ModelInput))
.Append(mlContext.Transforms.ApplyOnnxModel(OutputModel.ModelOutput, InputModel.ModelInput,
modelLocation));
var transformer = estimator.Fit(mlContext.Data.LoadFromEnumerable(Array.Empty<InputModel>()));
// Fit scoring pipeline
var predictionEngine = mlContext.Model.CreatePredictionEngine<InputModel, OutputModel>(transformer);
return predictionEngine;
}
class InputModel
{
public const int imageHeight = 512;
public const int imageWidth = 512;
// input tensor name
public const string ModelInput = "input_1:0";
public string ImagePath { get; set; }
[ColumnName(ModelInput)]
[ImageType(imageHeight, imageWidth)]
public Bitmap Image { get; set; }
}
class OutputModel
{
// output tensor name
public const string ModelOutput = "Identity:0";
[ColumnName(ModelOutput)]
public float[] Scores { get; set; }
}
}
I wrote up a very simple test program to try to get an output that matches a python project, only in C# so I could efficiently use it in an ASP.Net api (also just prefer C#). The original Python works, even after I modified it to use onnxruntime instead of keras, which is where the model originated. It gives a float[9176] of scores 0-1, which matches a list of tags in tags.txt, for whether that tag should apply to a given image.
It's a multi-classification problem with TensorFlow. I used the object detection sample to get here, and it returns a result, and the result is...correct, but not. It's rounding for whatever reason.
I'm new to ML and ML.Net has very little out there, so I figured I'd use my first question in a long time and hoping someone can shed some light on this for me.

Ok, new day. I traced the code path of the python project and made an MVP. In doing so, I have very few things to look at the difference of.
import os
import onnxruntime
import skimage.transform
import tensorflow as tf
def main():
# disable CUDA acceleration for simplicity in running the test
# you need drivers, an nvidia gpu, etc. for that
os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
image_path = 'C:\\Users\\da3ds\\Pictures\\image.jpg'
model_path = 'C:\\Users\\da3ds\\Downloads\\deepdanbooru-v3-20211112-sgd-e28\\model-resnet-custom_v3.onnx'
# load tags
tags_path = 'C:\\Users\\da3ds\\Downloads\\deepdanbooru-v3-20211112-sgd-e28\\tags.txt'
with open(tags_path, 'r') as tags_stream:
tags = [tag for tag in (tag.strip() for tag in tags_stream) if tag]
# create inference session
model = onnxruntime.InferenceSession(model_path, providers=['CPUExecutionProvider'])
width = model.get_inputs()[0].shape[1] # 512
height = model.get_inputs()[0].shape[2] # 512
image_raw = tf.io.read_file(image_path)
image = tf.io.decode_png(image_raw, channels=3)
image = tf.image.resize(image, size=(width, height), method=tf.image.ResizeMethod.AREA, preserve_aspect_ratio=True)
image = image.numpy() # EagerTensor to np.array
image_width = image.shape[0]
image_height = image.shape[1]
t = skimage.transform.AffineTransform(translation=(-image_width * 0.5, -image_height * 0.5))
t += skimage.transform.AffineTransform(translation=(width * 0.5, height * 0.5))
image = skimage.transform.warp(image, t.inverse, output_shape=(width, height), order=1, mode='edge')
# at this point all widths and heights are probably 512
# normalize the image
image = image / 255.0
image_shape = image.shape
# build the input shape of Vector<1, 512, 512, 3>
image = image.reshape((1, image_shape[0], image_shape[1], image_shape[2]))
onnx_result = model.run(None, {'input_1:0': image})
# onnx_result is 2 arrays deep for reason
# 1 would make sense, as it can handle batches
onnx_result = onnx_result[0][0]
# print a nice result
for i, tag in enumerate(tags):
print(f'({onnx_result[i]:05.3f}) {tag}')
if __name__ == '__main__':
main()
Conveniently, in doing so, I made a mistake in a default value that yielded the same result as the ML.Net results: (not) Normalizing the Image. I couldn't figure out how to do that in the ML.Net pipeline, so I made the array with Magick.Net and fed it to ML.Net directly.
Here's the final code:
using ImageMagick;
using Microsoft.ML;
using Microsoft.ML.Data;
namespace OnnxTest;
public static class Program
{
public static void Main(string[] args)
{
var tags = File.ReadLines(#"C:\Users\da3ds\Downloads\deepdanbooru-v3-20211112-sgd-e28\tags.txt");
var imageLocation = #"C:\Users\da3ds\Pictures\image.jpg";
var modelLocation = #"C:\Users\da3ds\Downloads\deepdanbooru-v3-20211112-sgd-e28\model-resnet-custom_v3.onnx";
MLContext mlContext = new MLContext(seed: 0);
Console.WriteLine("Read model");
Console.WriteLine($"Model location: {modelLocation}");
Console.WriteLine(
$"Default parameters: image size=({InputModel.Width},{InputModel.Height})");
Console.WriteLine($"Images location: {imageLocation}");
Console.WriteLine("");
Console.WriteLine("=====Identify the objects in the images=====");
Console.WriteLine("");
// Create IDataView from empty list to obtain input data schema
var data = new InputModel { Data = GetImage(imageLocation) };
// Define scoring pipeline
var predictionEngine = GetPredictionEngine(mlContext, modelLocation);
var output = predictionEngine.Predict(data);
var outputMapped = tags.Zip(output.Scores).Select(t => new { Tag = t.First, f = t.Second })
.ToDictionary(a => a.Tag, a => a.f);
var outputTags = outputMapped.Where(a => a.Value > 0.80f).Select(a => (Tag: a.Key, Score: a.Value))
.ToList();
foreach (var tag in outputTags)
{
Console.WriteLine($"({tag.Score:P1}) {tag.Tag}");
}
}
private static PredictionEngine<InputModel, OutputModel> GetPredictionEngine(MLContext mlContext, string modelLocation)
{
var transformer = GetBasicTransformer(mlContext, modelLocation);
// Fit scoring pipeline
var predictionEngine = mlContext.Model.CreatePredictionEngine<InputModel, OutputModel>(transformer);
return predictionEngine;
}
private static ITransformer GetBasicTransformer(MLContext mlContext, string modelLocation)
{
var estimator = mlContext.Transforms.ApplyOnnxModel(OutputModel.ModelOutput, InputModel.ModelInput,
modelLocation);
var transformer = estimator.Fit(mlContext.Data.LoadFromEnumerable(Array.Empty<InputModel>()));
return transformer;
}
public static float[] GetImage(string imagePath)
{
using var mImage = new MagickImage(imagePath);
mImage.Quality = 100;
mImage.BackgroundColor = new MagickColor(0, 0, 0);
mImage.HasAlpha = false;
mImage.Resize(new MagickGeometry($"{InputModel.Width}>x{InputModel.Height}>"));
mImage.Extent(InputModel.Width, InputModel.Height, Gravity.Center, new MagickColor(0,0,0));
var pixels = mImage.GetPixels();
var array = pixels.ToArray();
var data = new float[InputModel.Width * InputModel.Height * InputModel.Channels];
for (var index = 0; index < array.Length; index++)
{
data[index] = array[index] / 255.0f;
}
return data;
}
class InputModel
{
public const int Width = 512;
public const int Height = 512;
public const int Channels = 3;
public const string ModelInput = "input_1:0";
[ColumnName(ModelInput)]
[VectorType(1, Width, Height, Channels)]
public float[] Data { get; set; }
}
class OutputModel
{
// output tensor name
public const string ModelOutput = "Identity:0";
[ColumnName(ModelOutput)]
public float[] Scores { get; set; }
}
}
Obviously, the final...final code will be less of an MVP, but this was a test. I leave this as a trail of my efforts in case someone else hits a similar issue. At the very least, it gives my debugging steps and some sample code. Thanks for being my rubber ducks.

Related

I'm trying to create a simple neural network using ML.NET with regression which returns the specified number, but it returns a different number

I learn ML.NET library. In general, I use regression trainers in my projects.
I have this code which must return number, which given to model:
class Program
{
static Input[] trainData;
static void CreateData(int count = 100)
{
trainData = new Input[count];
for (int i = 0; i < count; i++)
{
trainData[i] = new Input { Column1 = i, Result = i };
}
}
static void Main(string[] args)
{
CreateData();
var mlContext = new MLContext();
var dataView = mlContext.Data.LoadFromEnumerable(trainData);
var pipeline = mlContext.Transforms.CopyColumns("Label", "Result")
.Append(mlContext.Transforms.Concatenate("Features", "Column1"))
.Append(mlContext.Regression.Trainers.Sdca());
var model = pipeline.Fit(dataView);
var predictor = mlContext.Model.CreatePredictionEngine<Input, Output>(model);
var prediction = predictor.Predict(new Input { Column1 = 178, Result = 0});
Console.WriteLine(prediction.Result);
Console.Beep(500, 100);
Console.Read();
}
}
class Input
{
public float Column1;
public float Result;
}
class Output
{
[ColumnName("Score")]
public float Result;
}
That's all, but it returns 173, and with FastTree() trainer my predictor return 94.
What's wrong?

Check if an stl file may contain two models

An stl file may contain 2 3D models. Is there any way I can detect if there are 2 or more models stored in one stl file?
In my current code, it can detect that there are 2 models in the example, but there are instances that it detects a lot of model even though it only has one.
The Triangle class structure has Vertices that contains 3 points (x, y, z)..
Sample STL File:
EDIT: Using #Gebb's answer this is how I implemented it:
private int GetNumberOfModels(List<TopoVertex> vertices)
{
Vertex[][] triangles = new Vertex[vertices.Count() / 3][];
int vertIdx = 0;
for(int i = 0; i < vertices.Count() / 3; i++)
{
Vertex v1 = new Vertex(vertices[vertIdx].pos.x, vertices[vertIdx].pos.y, vertices[vertIdx].pos.z);
Vertex v2 = new Vertex(vertices[vertIdx + 1].pos.x, vertices[vertIdx + 1].pos.y, vertices[vertIdx + 1].pos.z);
Vertex v3 = new Vertex(vertices[vertIdx + 2].pos.x, vertices[vertIdx + 2].pos.y, vertices[vertIdx + 2].pos.z);
triangles[i] = new Vertex[] { v1, v2, v3 };
vertIdx += 3;
}
var uniqueVertices = new HashSet<Vertex>(triangles.SelectMany(t => t));
int vertexCount = uniqueVertices.Count;
// The DisjointUnionSets class works with integers, so we need a map from vertex
// to integer (its id).
Dictionary<Vertex, int> indexedVertices = uniqueVertices
.Zip(
Enumerable.Range(0, vertexCount),
(v, i) => new { v, i })
.ToDictionary(vi => vi.v, vi => vi.i);
int[][] indexedTriangles =
triangles
.Select(t => t.Select(v => indexedVertices[v]).ToArray())
.ToArray();
var du = new XYZ.view.wpf.DisjointUnionSets(vertexCount);
// Iterate over the "triangles" consisting of vertex ids.
foreach (int[] triangle in indexedTriangles)
{
int vertex0 = triangle[0];
// Mark 0-th vertexes connected component as connected to those of all other vertices.
foreach (int v in triangle.Skip(1))
{
du.Union(vertex0, v);
}
}
var connectedComponents =
new HashSet<int>(Enumerable.Range(0, vertexCount).Select(x => du.Find(x)));
return connectedComponents.Count;
}
In some cases, it produces the correct output, but for the example image above, it outputs 3 instead of 2. I am now trying to optimize the snippet #Gebb gave to use float values since I believe that the floating points are necessary to the comparisons. Does anyone have a way to do that as well? Maybe I need another perspective.
You could do this by representing vertices and connections between them as a graph and finding the number of connected components of the graph with the help of the Disjoint-set data structure.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Vertex = System.ValueTuple<double,double,double>;
namespace UnionFindSample
{
internal class DisjointUnionSets
{
private readonly int _n;
private readonly int[] _rank;
private readonly int[] _parent;
public DisjointUnionSets(int n)
{
_rank = new int[n];
_parent = new int[n];
_n = n;
MakeSet();
}
// Creates n sets with single item in each
public void MakeSet()
{
for (var i = 0; i < _n; i++)
// Initially, all elements are in
// their own set.
_parent[i] = i;
}
// Finds the representative of the set
// that x is an element of.
public int Find(int x)
{
if (_parent[x] != x)
{
// if x is not the parent of itself, then x is not the representative of
// his set.
// We do the path compression by moving x’s node directly under the representative
// of this set.
_parent[x] = Find(_parent[x]);
}
return _parent[x];
}
// Unites the set that includes x and
// the set that includes x
public void Union(int x, int y)
{
// Find representatives of two sets.
int xRoot = Find(x), yRoot = Find(y);
// Elements are in the same set, no need to unite anything.
if (xRoot == yRoot)
{
return;
}
if (_rank[xRoot] < _rank[yRoot])
{
// Then move x under y so that depth of tree remains equal to _rank[yRoot].
_parent[xRoot] = yRoot;
}
else if (_rank[yRoot] < _rank[xRoot])
{
// Then move y under x so that depth of tree remains equal to _rank[xRoot].
_parent[yRoot] = xRoot;
}
else
{
// if ranks are the same
// then move y under x (doesn't matter which one goes where).
_parent[yRoot] = xRoot;
// And increment the result tree's
// rank by 1
_rank[xRoot] = _rank[xRoot] + 1;
}
}
}
internal class Program
{
private static void Main(string[] args)
{
string file = args[0];
Vertex[][] triangles = ParseStl(file);
var uniqueVertices = new HashSet<Vertex>(triangles.SelectMany(t => t));
int vertexCount = uniqueVertices.Count;
// The DisjointUnionSets class works with integers, so we need a map from vertex
// to integer (its id).
Dictionary<Vertex, int> indexedVertices = uniqueVertices
.Zip(
Enumerable.Range(0, vertexCount),
(v, i) => new {v, i})
.ToDictionary(vi => vi.v, vi => vi.i);
int[][] indexedTriangles =
triangles
.Select(t => t.Select(v => indexedVertices[v]).ToArray())
.ToArray();
var du = new DisjointUnionSets(vertexCount);
// Iterate over the "triangles" consisting of vertex ids.
foreach (int[] triangle in indexedTriangles)
{
int vertex0 = triangle[0];
// Mark 0-th vertexes connected component as connected to those of all other vertices.
foreach (int v in triangle.Skip(1))
{
du.Union(vertex0, v);
}
}
var connectedComponents =
new HashSet<int>(Enumerable.Range(0, vertexCount).Select(x => du.Find(x)));
int count = connectedComponents.Count;
Console.WriteLine($"Number of connected components: {count}.");
var groups = triangles.GroupBy(t => du.Find(indexedVertices[t[0]]));
foreach (IGrouping<int, Vertex[]> g in groups)
{
Console.WriteLine($"Group id={g.Key}:");
foreach (Vertex[] triangle in g)
{
string tr = string.Join(' ', triangle);
Console.WriteLine($"\t{tr}");
}
}
}
private static Regex _triangleStart = new Regex(#"^\s+outer loop");
private static Regex _triangleEnd = new Regex(#"^\s+endloop");
private static Regex _vertex = new Regex(#"^\s+vertex\s+(\S+)\s+(\S+)\s+(\S+)");
private static Vertex[][] ParseStl(string file)
{
double ParseCoordinate(GroupCollection gs, int i) =>
double.Parse(gs[i].Captures[0].Value, CultureInfo.InvariantCulture);
var triangles = new List<Vertex[]>();
bool isInsideTriangle = false;
List<Vertex> triangle = new List<Vertex>();
foreach (string line in File.ReadAllLines(file))
{
if (isInsideTriangle)
{
if (_triangleEnd.IsMatch(line))
{
isInsideTriangle = false;
triangles.Add(triangle.ToArray());
triangle = new List<Vertex>();
continue;
}
Match vMatch = _vertex.Match(line);
if (vMatch.Success)
{
double x1 = ParseCoordinate(vMatch.Groups, 1);
double x2 = ParseCoordinate(vMatch.Groups, 2);
double x3 = ParseCoordinate(vMatch.Groups, 3);
triangle.Add((x1, x2, x3));
}
}
else
{
if (_triangleStart.IsMatch(line))
{
isInsideTriangle = true;
}
}
}
return triangles.ToArray();
}
}
}
I'm also using the fact that System.ValueTuple implements Equals and GetHashCode in an appropriate way, so we can easily compare vertices (this is used implicitly by HashSet) and use them as keys in a dictionary.

C# Detects the maximum and lists all the elements that acquire

Please can somebody help me with C# issue?
Create a program that detects the maximum and lists all the elements that acquire this value. The name of the pupil and the height of the pupil will be entered at the entrance with the precision per cm. At the output, the program lists the maximum altitude of all pupils and the next line of the names of all pupils who reach this maximum height (in the same order as the names were entered).
Inputs:
Martin Novacek 171
Bohumil Betak 177
Ladislav Zlatohlavek 150
Hana Drbohlavova 177
Result:
177
Bohumil Betak
Hana Drbohlavova
My code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace ulozka5
{
class Program
{
static void Main(string[] args)
{
string input;
int max = 0;
string[] array = new string[9999];
for (int i = 0; i < array.Length; i++) // inputs
{
input = Console.ReadLine();
if (input == "" || input == null)
{
Array.Resize(ref array, i);
break;
}
else
{
array[i] = input;
}
}
for (int i = 0; i < array.Length; i++) //max value
{
var number = int.Parse(new string(array[i].Where(char.IsDigit).ToArray()));
if (max < number)
{
max = number;
}
}
Console.WriteLine(max);
for (int i = 0; i < array.Length; i++) //compare with max
{
var cislo = int.Parse(new string(array[i].Where(char.IsDigit).ToArray()));
if (cislo == max)
{
var name = Regex.Replace(array[i], #"[\d-]", string.Empty);
Console.WriteLine(name);
}
}
}
}
}
Everything works fine, but scholar system gave me Score 20 for this :( ... Thanks for any idea..
There are lot of ways in which you can improve the code. Here are some suggestions.
Input Part
Instead of defining a huge array upfront, you can create a List and wait for User to enter a special terminating character (in this case, Programs waits for User to enter "q" to end input). Also, you can use regex to parse the Name/Altitude parts and store in a object.
string input = string.Empty;
var list = new List<Pupils>();
var regex = new Regex(#"(?<name>[a-zA-Z\s]+)\s+(?<altitude>[0-9]+)",RegexOptions.Compiled);
while((input = Console.ReadLine()) != "q")
{
var matches = regex.Match(input.Trim());
list.Add(new Pupils
{
Name = matches.Groups["name"].Value,
Altitude = int.Parse(matches.Groups["altitude"].Value)
});
}
Where Pupils is defined as
public class Pupils
{
public string Name {get;set;}
public int Altitude {get;set;}
}
Querying Max and related Users
Then you can calculate Max Altitude can be found using Linq
var maxAltitude = list.Max(x=>x.Altitude);
With maxAltitude in hand, you can now parse the Users with max value.
var result = list.Where(x=>x.Altitude.Equals(maxAltitude)).Select(x=>x.Name);
Complete code:
void Main()
{
string input = string.Empty;
var list = new List<Pupils>();
var regex = new Regex(#"(?<name>[a-zA-Z\s]+)\s+(?<altitude>[0-9]+)",RegexOptions.Compiled);
while((input = Console.ReadLine()) != "q")
{
var matches = regex.Match(input.Trim());
list.Add(new Pupils
{
Name = matches.Groups["name"].Value,
Altitude = int.Parse(matches.Groups["altitude"].Value)
});
}
var maxAltitude = list.Max(x=>x.Altitude);
var result = list.Where(x=>x.Altitude.Equals(maxAltitude)).Select(x=>x.Name);
Console.WriteLine($"Max Altitude :{maxAltitude}");
foreach(var item in result)
{
Console.WriteLine(item);
}
}
public class Pupils
{
public string Name {get;set;}
public int Altitude {get;set;}
}
Just for fun.
Your code can be little bid simpler if you will use separator character between name and height.
// Get pupils and calculate max height
var pupils =
Enumerable.Range(0, int.MaxValue)
.Select(i => Console.ReadLine())
.TakeWhile(input => string.IsNullOrWhiteSpace(input) == false)
.Select(input => input.Split(':'))
.Select(values =>
{
var name = values.First();
var validHeight = int.TryParse(values.Last(), out int height);
return (Name: name, Height: height, Valid: validHeight);
})
.Where(pupil => pupil.Valid)
.ToList();
var maxHeight = pupils.Max(pupil => pupil.Height);
// Build output string
var output =
pupils.Where(pupil => pupil.Height == maxHeight)
.Aggregate(new StringBuilder().AppendLine(maxHeight.ToString()),
(builder, pupil) => builder.AppendLine(pupil.Name));
Console.WriteLine(output);

Exact copy of text using OCR

I have an image. I read the text content using ironocr. The following code used to read text.
var Ocr = new AutoOcr();
var Result = Ocr.Read(bmpCrop);
string text = Result.Text;
return text;
But the text trims the space and I couldn't get the exact copy of the text as in the image. Is there way or any other ocr libraries that reads text as an exact copy from the image. Please find the image attached, that I have used to read using ocr.
I have tried the following methode specified in the below url also, This also not working for me.
How to preserve document structure in tesseract
I have found that the latest IronOCR has a detailed document object model of pages, blocks, paragraphs, lines, words and characters
https://ironsoftware.com/csharp/ocr/examples/results-objects/
using IronOcr;
using System.Drawing; //for image export
// We can delve deep into OCR results as an object model of
// Pages, Barcodes, Paragraphs, Lines, Words and Characters
// This allows us to explore, export and draw OCR content using other APIs/
var Ocr = new IronTesseract();
Ocr.Configuration.EngineMode = TesseractEngineMode.TesseractAndLstm;
Ocr.Configuration.ReadBarCodes = true;
using (var Input = new OcrInput(#"example.tiff"))
{
OcrResult Result = Ocr.Read(Input);
foreach (var Page in Result.Pages)
{
// Page object
int PageNumber = Page.PageNumber;
string PageText = Page.Text;
int PageWordCount = Page.WordCount;
// null if we dont set Ocr.Configuration.ReadBarCodes = true;
OcrResult.Barcode[] Barcodes = Page.Barcodes;
System.Drawing.Bitmap PageImage = Page.ToBitmap(Input);
int PageWidth = Page.Width;
int PageHeight = Page.Height;
foreach (var Paragraph in Page.Paragraphs)
{
// Pages -> Paragraphs
int ParagraphNumber = Paragraph.ParagraphNumber;
String ParagraphText = Paragraph.Text;
System.Drawing.Bitmap ParagraphImage = Paragraph.ToBitmap(Input);
int ParagraphX_location = Paragraph.X;
int ParagraphY_location = Paragraph.Y;
int ParagraphWidth = Paragraph.Width;
int ParagraphHeight = Paragraph.Height;
double ParagraphOcrAccuracy = Paragraph.Confidence;
OcrResult.TextFlow paragrapthText_direction = Paragraph.TextDirection;
foreach (var Line in Paragraph.Lines)
{
// Pages -> Paragraphs -> Lines
int LineNumber = Line.LineNumber;
String LineText = Line.Text;
System.Drawing.Bitmap LineImage = Line.ToBitmap(Input); ;
int LineX_location = Line.X;
int LineY_location = Line.Y;
int LineWidth = Line.Width;
int LineHeight = Line.Height;
double LineOcrAccuracy = Line.Confidence;
double LineSkew = Line.BaselineAngle;
double LineOffset = Line.BaselineOffset;
foreach (var Word in Line.Words)
{
// Pages -> Paragraphs -> Lines -> Words
int WordNumber = Word.WordNumber;
String WordText = Word.Text;
System.Drawing.Image WordImage = Word.ToBitmap(Input);
int WordX_location = Word.X;
int WordY_location = Word.Y;
int WordWidth = Word.Width;
int WordHeight = Word.Height;
double WordOcrAccuracy = Word.Confidence;
if (Word.Font != null)
{
// Word.Font is only set when using Tesseract Engine Modes rather than LTSM
String FontName = Word.Font.FontName;
double FontSize = Word.Font.FontSize;
bool IsBold = Word.Font.IsBold;
bool IsFixedWidth = Word.Font.IsFixedWidth;
bool IsItalic = Word.Font.IsItalic;
bool IsSerif = Word.Font.IsSerif;
bool IsUnderLined = Word.Font.IsUnderlined;
bool IsFancy = Word.Font.IsCaligraphic;
}
foreach (var Character in Word.Characters)
{
// Pages -> Paragraphs -> Lines -> Words -> Characters
int CharacterNumber = Character.CharacterNumber;
String CharacterText = Character.Text;
System.Drawing.Bitmap CharacterImage = Character.ToBitmap(Input);
int CharacterX_location = Character.X;
int CharacterY_location = Character.Y;
int CharacterWidth = Character.Width;
int CharacterHeight = Character.Height;
double CharacterOcrAccuracy = Character.Confidence;
// Output alternative symbols choices and their probability.
// Very useful for spellchecking
OcrResult.Choice[] Choices = Character.Choices;
}
}
}
}
}
}

change string in C# [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I am new to C# and not so expert in string manipulation. I have a string which represents the position of an object in diagram. The string contains integer values which I want to change for each object.
Example
String Position = "l=50; r=190; t=-430; b=-480";
I want to modify this string to
String Position = "l=50; r=190; t=-505; b=-555";
So if you notice t = -430 is changed to t = -505 and b = -480 to b = -555 which means an increment of -75 in both top and bottom
How can I do this ?
Thanks
If you want to easily populate or modify the values in your input string, you can use string.Format(), like this:
Position = string.Format("l={0}; r={1}; t={2}; b={3}", 50, 190, -430, -480);
You can extract the values of an existing input string using regular expressions, like this:
String Position = "l=50; r=190; t=-430; b=-480";
string pattern = #"^l=(\-{0,1}\d+); r=(\-{0,1}\d+); t=(\-{0,1}\d+); b=(\-{0,1}\d+)$";
var captGroups = Regex.Match(Position, pattern).Groups;
var l = captGroups[1];
var r = captGroups[2];
var t = captGroups[3];
var b = captGroups[4];
You need to parse your expression. Here is an example, in which the string is first splitted for ; and then each part is splitted by =:
var position = "l=50; r=190; t=-430; b=-480";
var parts = position.Split(';'); // split string into 4 parts
var assignments = new Dictionary<string, int>();
foreach (var part in parts)
{
var trimmedPart = part.Trim();
var assignmentParts = trimmedPart.Split('='); // split each part into variable and value part
var value = Int32.Parse(assignmentParts[1]); // convert string to integer value
assignments.Add(assignmentParts[0], value);
}
// change values
assignments["t"] = -505;
assignments["b"] = -555;
// build new string
var newPosition = String.Join("; ", assignments.Select(p => p.Key + "=" + p.Value));
Console.WriteLine(">> " + newPosition);
String Position = "l=50; r=190; t=-430; b=-480";
public void modifyPosition(int l, int r, int t, int b)
{
string[] parts = Position.Split(';');
int oldL = int.Parse(parts[0].Replace("l=","").Trim());
int oldR = int.Parse(parts[1].Replace("r=","").Trim());
int oldT = int.Parse(parts[2].Replace("t=","").Trim());
int oldB = int.Parse(parts[3].Replace("b=","").Trim());
Position = "l="+(oldL+l).ToString()+"; r="+(oldR+r).ToString()+
"; t="+(oldT+t).ToString()+"; b="+(oldB+b).ToString()+";";
}
There are a lot of ways to generalize and optimize that -- I'll leave those up to you...
To provide a further explanation to Jeroen van Langen's comment
You should parse the string into an object, change the values and reformat a string.
You can achieve this with a similar setup to this:
public class Position
{
public int l { get; set; }
public int r { get; set; }
public int t { get; set; }
public int b { get; set; }
public override string ToString()
{
return $"l = {l}, r = {r}, t = {t}, b = {b}";
}
}
(Change the access modifiers to suit your needs)
And work with the object (simply) as below:
public void UpdatePostion()
{
// Create new position
Position pos = new Position
{
l = 50,
r = 190,
t = -430,
b = -480
};
Console.WriteLine($"Pos before change: {pos.ToString()}");
// change top and bottom value to accommodate increment
pos.t += -75;
pos.b += -75;
// Prove that they've been updated
Console.WriteLine($"Pos after change: {pos.ToString()}");
}
You don't usually want to manipulate data in string form unless the data is actually a string. In this case, you've really got a structure which is being represented as a string, so you should turn it into a structure first.
Based on the string you provided, this turns any string of a similar form into a dictionary.
var source = "l=50; r=190; t=-505; b=-555";
var components = source.Split(';').Select(x => x.Trim());
// components is now an enumerable of strings "l=50", "r=190" etc.
var pairs = components.Select(x => x.Split('=').ToArray());
// pairs is now an enumerable of arrays of strings ["l", "50"], ["r", "190"] etc;
var dictionary = pairs.ToDictionary(x => x[0], x => x[1]);
// dictionary now contains r => 190, l => 50 etc.
Of course, you want to play with actual numbers, so you want to convert the values to integers really, so swap that last line out:
var dictionary = pairs.ToDictionary(x => x[0], x => Convert.ToInt32(x[1]));
Now you have a Dictionary which you can manipulate:
dictionary["t"] = dictionary["t"] - 75;
For example. Or you could use this as the basis of populating an object with appropriate properties and manipulate those.
struct Position {
public int L { get; set; }
public int R { get; set; }
public int T { get; set; }
public int B { get; set; }
}
Of course you might want to change it back into a string when you're done manipulating it:
var newString = string.Join("; ", dictionary.Select(x => $"{x.Key}={x.Value}"));
Although if you're planning to manipulate it a lot you should keep the parsed version around for as long as possible, as the parse/render cycle could be a performance issue if you do a lot of it.
Always store your data in appropriate types. Write new classes, write new structs, they don't have to be complicated but the more you can give names and shapes to your data the easier it is to manipulate it and to ensure that you manipulated it correctly.
This is all very simplistic of course - there's no error handling, such as if the value part isn't a valid Int32 (Convert.ToInt32 will throw an exception) or if there are duplicate keys (ToDictionary will throw an exception) etc. But hopefully it gives you some idea where to go.
It's also not the only way you could parse this kind of string, but it's fairly straightforward to follow and shouldn't be too slow, although it's not going to be very memory-efficient on a large input string.
Check this fiddle here.
public class MyObject
{
public int r {get;set;}
public int l {get;set;}
public int t {get;set;}
public int b {get;set;}
public MyObject()
{
}
public void ParseFromString(string val)
{
string[] splitVal = val.Split(';');
int intVal ;
if(!int.TryParse(splitVal[0].Replace("l=","").Trim(), out intVal))
intVal = 0;
this.l = intVal;
if(!int.TryParse(splitVal[1].Replace("r=","").Trim(), out intVal))
intVal = 0;
this.r = intVal;
if(!int.TryParse(splitVal[2].Replace("t=","").Trim(), out intVal))
intVal = 0;
this.t = intVal;
if(!int.TryParse(splitVal[3].Replace("b=","").Trim(), out intVal))
intVal = 0;
this.b = intVal;
}
public string ParseToString()
{
return "l=" + this.l + "; r=" + this.r + "; t=" + this.t + "; b=" + this.b + "";
}
}
So, building on one of the comments suggestions about parsing into an object - you could represent your Position object as follows:
public class Position
{
public int Left { get; set; }
public int Right { get; set; }
public int Top { get; set; }
public int Bottom { get; set; }
public Position(string str)
{
int[] values = str.Split(new[] { "; " }, StringSplitOptions.None)
.Select(s => Convert.ToInt32(s.Substring(s.IndexOf('=') +1))).ToArray();
Left = values[0];
Right = values[1];
Top = values[2];
Bottom = values[3];
}
public override string ToString()
{
return string.Join("; ", Left, Right, Top, Bottom);
}
}
You could then use this object to serialize/deserialize your string to and from an object as follows:
string intputString = "l=50; r=190; t=-430; b=-480";
Position p = new Position(intputString);
p.Top -= 75;
p.Bottom -= 75;
string outputString = p.ToString();`
The only thing of any real complexity is turning your string into the object values. To do this we can delimit the string into each individual value by splitting the string on "; ". Each value can then be turned into an integer by taking the substring of each element after the equals character ("l=50" becomes "50") and converting it into an integer.
I think the best approach would be to use a regex in this case:
var position = "l=50; r=190; t=-430; b=-480";
var match = Regex.Match(position, #"l=(-?\d+); r=(-?\d+); t=(-?\d+); b=(-?\d+)");
if (!match.Success)
throw new Exception("invalid data");
var l = Convert.ToInt32(match.Groups[1].Value);
var r = Convert.ToInt32(match.Groups[2].Value);
var t = Convert.ToInt32(match.Groups[3].Value);
var b = Convert.ToInt32(match.Groups[4].Value);
// modify the values as you like, for example: t -= 75; b -=75;
var newPosition = $"l={l}; r={r}; t={t}; b={b}";
I think a simple method can do the trick:
public static string ModifyPositions(string positionsInput, int displacement)
{
string input = "l=50; r=190; t=-430; b=-480";
var pattern = #"(t|b)=(-?\d+)";
var regex = new Regex(pattern);
var matches = regex.Matches(input);
foreach (Match match in matches)
{
input = input.Replace(match.Groups[2].Value, (int.Parse(match.Groups[2].Value) + displacement).ToString());
}
return input;
}
In your case displacement is -75, positionsInput is "l=50; r=190; t=-430; b=-480"
You can create a Position structure, like this:
public struct Position
{
public int Left { get; private set; }
public int Top { get; private set; }
public int Right { get; private set; }
public int Bottom { get; private set; }
public Position(int left, int top, int right, int bottom)
{
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
public Position(string positionText)
{
string pattern = #"(?=l=(?<left>[\d\-]+))|(?=t=(?<top>[\d\-]+))|(?=r=(?<right>[\d\-]+))|(?=b=(?<bottom>[\d\-]+))";
Match match = Regex.Match(positionText, pattern);
int left = Convert.ToInt32(match.Groups["left"].Value);
int top = Convert.ToInt32(match.Groups["top"].Value);
int right = Convert.ToInt32(match.Groups["right"].Value);
int bottom = Convert.ToInt32(match.Groups["bottom"].Value);
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
public Position Move(int leftDelta = 0, int topDelta=0, int rightDelta=0, int bottomDelta = 0)
{
return new Position(Left + leftDelta, Top + topDelta, Right + rightDelta, Bottom + bottomDelta);
}
public override string ToString()
{
return string.Format("l={0}; r={1}; t={2}; b={3}", Left, Right, Top, Bottom);
}
}
Then you can use it like this:
Position oldPosition = new Position("l=50; r=190; t=-430; b=-480");
Position newPosition = oldPosition.Move(topDelta: -75, bottomDelta: -75);
string newPositionText = newPosition.ToString();

Categories

Resources