Bitmap region locked / bitmap in use elsewhere error - c#

I have a program where I perform OCR on selected text lines in an image, I used a for loop and it works well. Only problem is that in for loop each line is processed after the previous one is done which adds up the processing time.
So I tried using a parallel.for statement so that all lines will be processed together(max 5 lines). When there is only 1 line it works and I get no error, but when there are multiple lines, I get the following errors :
System.InvalidOperationException: 'Bitmap region is already locked.'
OR Bitmap is in use elsewhere
Im using EMGU and tesseract class for processing.
This is my code :
private void ocrmethod()
{
this.Invoke((MethodInvoker)delegate
{
Image<Gray, byte> temp = frame.Convert<Gray, byte>().Clone();
// for (int i = 0; i < imgcount; i++)
Parallel.For(0, imgcount, i =>
{
temp.ROI = LineRect[i]; // Selected text line region in the image
OCR_Class doocr = new OCR_Class();
Image<Gray, byte> foundareafilter = temp.Copy();
bitmap2 = new Bitmap(foundareafilter.ToBitmap<Gray, byte>());
//OCR SECTION
doocr.trainingdatapath(#"./OCRtraindata", "eng");
doocr.ProcessOCR(bitmap2, 1);
string result = doocr.result().Replace(" ", "").Replace(" ", "");
// }
});
});
}
I cant figure out how to make the bitmaps available to all process simultaneously. Please help.

Changing external variables in a parallel loop leads to their locking.
bitmap2 = new Bitmap(foundareafilter.ToBitmap<Gray, byte>());
You need to make thread-safe code
Patterns for Parallel Programming: Understanding and Applying
Parallel Patterns with the .NET Framework 4 - Stephen Toub

Related

Fo-Dicom Window Change is slow

I am using the following render code. This is a slightly modified code because, in the actual code, I am getting all the files from the FTP server. CurrentWindowCenter and CurrentWindowWidth hold the current value of WL and WW. This value can be changed from the UI. I am showing the rendered WritableBitmap directly to the user using an image component inside a canvas.
But the rendering of the image is very slow. Especially for images with large file sizes such as X-Ray. So, the WW and WL change is also very slow since it also uses the render function.
I am not very knowledgeable about this. But is there a way to make the rendering or WW/WL change faster? Is there a way to skip the rendering of the image every time a WW/WL change happens?
Any advice in the right direction is appreciated.
Thanks in advance.
// assume filePath holds an actual file location.
var filePath = "";
var dicomFile = new DicomFile.Open(filePath);
var dicomImage = new DicomImage(dicomFile.DataSet);
if (CurrentWindowCenter.HasValue && CurrentWindowWidth.HasValue)
{
dicomImage.WindowCenter = Convert.ToDouble(CurrentWindowCenter.Value);
dicomImage.WindowWidth = Convert.ToDouble(CurrentWindowWidth.Value);
}
dicomImage.RenderImage().AsWriteableBitmap();
Environment
Fo-Dicom (4.0.8)
.NET Framework 4.8
WPF
My guess is that fo-dicom is not really intended for high performance and more for compatibility. For best performance you should probably use the GPU via DirectX, OpenCL or similar. Second best should be some tightly optimized SIMD code, probably using c++.
But there might be some improvements to be had using just c#. From the looks of it fo-dicom creates a new image, copies pixels to this image, and then creates a writeableBitmap and does another copy. These step will take some extra time.
My code for copying pixels and applying a lut/transfer function look like this:
public static unsafe void Mono16ToMono8(
byte* source,
byte* target,
int sourceStride,
int targetStride,
int width,
int height,
byte[] lut)
{
Parallel.For(0, height, y =>
{
var ySource = source + y * sourceStride;
var yTarget = target + y * targetStride;
var srcUshort = (ushort*)ySource;
for (int x = 0; x < width; x++)
{
var sample = srcUshort[x];
yTarget[x] = lut[sample];
}
});
}
And the code to do the actual update of the writeable bitmap:
public static unsafe void Update(
this WriteableBitmap self,
IImage source,
byte[] lut)
{
self.Lock();
try
{
var targetPtr = (byte*)self.BackBuffer;
fixed (byte* sourcePtr = source.Data)
{
if (source.PixelFormat == PixelType.Mono16)
{
Mono16ToMono8(
sourcePtr,
targetPtr,
source.Stride,
self.BackBufferStride,
source.Width,
source.Height,
lut);
}
}
self.AddDirtyRect(new Int32Rect(0, 0, (int)self.Width, (int)self.Height));
}
finally
{
self.Unlock();
}
}
This uses an internal IImage format, where source.Data is a ReadOnlySpan<byte>, but could just as well be a byte[]. I hope most of the other properties are self-explanatory. I would expect this code to be a bit faster since it avoids both allocations and some copying steps.
All of this assumes the image is in 16-bit unsigned format, that is common for dicom, but not the only format. It also assumes you can actually get a hold of a pointer to the actual pixel-buffer, and an array of the lut that maps each possible pixelvalue to a byte. It also assumes a writeablebitmap of the correct size and color space.
And as previously mentioned, if you want both high performance, and handle all possible image formats, you might need to invest time to build your own image rendering pipeline.

C# Emgu CV Trouble Predicting Face EigenFaceRecognizer

I'm trying to save face images and predict them using Emgu CV 4.4 with EigenFaceRecognizer using .Net5.
Basically I am getting a single image, resizing it (I read somewhere that smaller images have better accuracy),adding it to the List<Mat>and saving the trained data to see what is there out of curiosity.After that I'm using the same image and attempt to predict what it is (label).
Unfortunately it fails producing this error.
Emgu.CV.Util.CvException: 'OpenCV: Wrong shapes for given matrices. Was size(src) = (1,57600), size(W) = (19200,1).'
I also tried imageForTraining.Mat within Predict however the same error occurred.
// Get The GrayImage And Resize It.
Image<Bgr, byte> imageForTraining = grayImage.Resize(128, 150, Emgu.CV.CvEnum.Inter.Cubic);
// Create New cv::Mat Lists.
List<Mat> mats = new List<Mat>()
{
imageForTraining.Mat
};
// Dirty Solution For Label Testing.
List<int> labels = new List<int>()
{
0
};
// Create Face Recognizer And Tell It There Is Only 1 Item.
FaceRecognizer faceRecognizer = new EigenFaceRecognizer(1);
// Write Data To File To See If Something Is There.
faceRecognizer.Write("trainingData");
// Attempt To Train.
faceRecognizer.Train(new VectorOfMat(mats.ToArray()), new VectorOfInt(labels.ToArray()));
// Test Image That Was Just Trained.
FaceRecognizer.PredictionResult predictionResult = faceRecognizer.Predict(imageForTraining);

Fastest way to scale an SKImage (SkiaSharp)

I'm looking for the fastest way to resize an SKImage. Unfortunately I found a few examples.
I ask this because if I perform these functions in parallel (in my case about 60 threads) the execution time of each single scaling function increases up to twenty times as much.
I tried with the following methods and the performance looks very similar, is there anything better?
Method 1:
SKImage src = (...);
SKImageInfo info = new SKImageInfo(width, height, SKColorType.Bgra8888);
SKImage output = SKImage.Create(info);
src.ScalePixels(output.PeekPixels(), SKFilterQuality.None);
Method 2:
SKImage src = (...);
SKImage output;
float resizeFactorX = (float)width / (float)Src.Width;
float resizeFactorY = (float)height / (float)Src.Height;
using (SKSurface surface = SKSurface.Create((int)(Src.Width *
resizeFactorX), (int)(Src.Height * resizeFactorY),
SKColorType.Bgra8888, SKAlphaType.Opaque))
{
surface.Canvas.SetMatrix(SKMatrix.MakeScale(resizeFactorX, resizeFactorY));
surface.Canvas.DrawImage(Src, 0, 0);
surface.Canvas.Flush();
output = surface.Snapshot();
}
This is the code I use. An additional thought is to be sure to wrap your SKImage objects in using, to ensure they're disposed of quickly. I'm not sure if that could be causing the slowdown with each iteration.
using (var surface = SKSurface.Create(resizedWidth, resizedHeight,
SKImageInfo.PlatformColorType, SKAlphaType.Premul))
using (var paint = new SKPaint())
{
// high quality with antialiasing
paint.IsAntialias = true;
paint.FilterQuality = SKFilterQuality.High;
// draw the bitmap to fill the surface
surface.Canvas.DrawImage(srcImg, new SKRectI(0, 0, resizedWidth, resizedHeight),
paint);
surface.Canvas.Flush();
using (var newImg = surface.Snapshot())
{
// do something with newImg
}
}

Tesseract empty page

I use tesseract for detecting characters on image.
try
{
using (var engine = new TesseractEngine(#"C:\Users\ea\Documents\Visual Studio 2015\Projects\ocrtTest", "eng", EngineMode.Default))
{
using (var img = Pix.LoadFromFile(testImagePath))
{
Bitmap src = (Bitmap)Image.FromFile(testImagePath);
using (var page = engine.Process(img))
{
var text = page.GetHOCRText(1);
File.WriteAllText("test.html", text);
//Console.WriteLine("Text: {0}", text);
//Console.WriteLine("Mean confidence: {0}", page.GetMeanConfidence());
int p = 0;
int l = 0;
int w = 0;
int s = 0;
int counter = 0;
using (var iter = page.GetIterator())
{
iter.Begin();
do
{
do
{
do
{
do
{
do
{
//if (iter.IsAtBeginningOf(PageIteratorLevel.Block))
//{
// logger.Log("New block");
//}
if (iter.IsAtBeginningOf(PageIteratorLevel.Para))
{
p++;//counts paragraph
//logger.Log("New paragraph");
}
if (iter.IsAtBeginningOf(PageIteratorLevel.TextLine))
{
l++;//count lines
//logger.Log("New line");
}
if (iter.IsAtBeginningOf(PageIteratorLevel.Word))
{
w++;//count words
//logger.Log("New word");
}
s++;//count symbols
//logger.Log(iter.GetText(PageIteratorLevel.Symbol));
// get bounding box for symbol
Rect symbolBounds;
if (iter.TryGetBoundingBox(PageIteratorLevel.Symbol, out symbolBounds))
{
Rectangle dueDateRectangle = new Rectangle(symbolBounds.X1, symbolBounds.Y1, symbolBounds.X2 - symbolBounds.X1, symbolBounds.Y2 - symbolBounds.Y1);
rect = dueDateRectangle;
PixelFormat format = src.PixelFormat;
Bitmap cloneBitmap = src.Clone(dueDateRectangle, format);
MemoryStream ms = new MemoryStream();
cloneBitmap.Save(ms, ImageFormat.Png);
ms.Position = 0;
Image i = Image.FromStream(ms);
//i.Save(ms,System.Drawing.Imaging.ImageFormat.Png);
i.Save("character" + counter + ".bmp", ImageFormat.Png);
counter++;
}
} while (iter.Next(PageIteratorLevel.Word, PageIteratorLevel.Symbol));
// DO any word post processing here (e.g. group symbols by word)
} while (iter.Next(PageIteratorLevel.TextLine, PageIteratorLevel.Word));
} while (iter.Next(PageIteratorLevel.Para, PageIteratorLevel.TextLine));
} while (iter.Next(PageIteratorLevel.Block, PageIteratorLevel.Para));
} while (iter.Next(PageIteratorLevel.Block));
}
Console.WriteLine("Pragraphs = " + p);
Console.WriteLine("Lines = " + l);
Console.WriteLine("Words = " + w);
Console.WriteLine("Symbols = " + s);
}
And it works when I have an image with a lot of text, but when I have an image with only one letter it does not.
It found a symbol, I see it in input. Symbols = 1. But it cant get BoundingBox. Why?
The same whem I use alphabet image
You may need to test the OCR with different page segmentation mode and OCR Engine mode to get the best result. Below is the usage information available in Tesseract 4.0.
Page segmentation modes:
0 Orientation and script detection (OSD) only.
1 Automatic page segmentation with OSD.
2 Automatic page segmentation, but no OSD, or OCR.
3 Fully automatic page segmentation, but no OSD. (Default)
4 Assume a single column of text of variable sizes.
5 Assume a single uniform block of vertically aligned text.
6 Assume a single uniform block of text.
7 Treat the image as a single text line.
8 Treat the image as a single word.
9 Treat the image as a single word in a circle.
10 Treat the image as a single character.
11 Sparse text. Find as much text as possible in no particular order.
12 Sparse text with OSD.
13 Raw line. Treat the image as a single text line,
bypassing hacks that are Tesseract-specific.<br>
OCR Engine modes:
0 Original Tesseract only.
1 Neural nets LSTM only.
2 Tesseract + LSTM.
3 Default, based on what is available.
For example,
psm 8 would give the best result for OCR a single word
psm 6 may give the best result of a block of text
In your code, it showed you have used the default engine mode and not specified segmentation mode. You may do some more tests to find out which modes give the correct result.
For images as bellow, use --psm 9 in your tesseract command

Are there any good examples of how to take a screenshot in selenium webdriver C#, then crop and save the image?

I've been searching for some good examples of how to take a screenshot using ITakesScreenshot in Selenium Webdriver in C#, then to crop the image using the elements dimensions and then saving this new image. With no such luck have I found any in C#.
I have this method at the moment but every so often I get a Out of Memory exception when used in a test. It fails on the line where it tries to crop.
public void TakeScreenShotOfElement(IWebDriver _driver, string rootpath, string imgName, string element2)
{
string element3 = element2;
var element = driver.FindElement(By.XPath(element3));
Byte[] ba = ((ITakesScreenshot)driver).GetScreenshot().AsByteArray;
var ss = new Bitmap(new MemoryStream(ba));
var crop = new Rectangle(element.Location.X, element.Location.Y, element.Size.Width, element.Size.Height);
//create a new image by cropping the original screenshot
Bitmap image2 = ss.Clone(crop, ss.PixelFormat);
if (!Directory.Exists(rootpath))
{
Directory.CreateDirectory(rootpath);
}
image2.Save(String.Format("{0}\\{1}.png", rootpath, imgName), System.Drawing.Imaging.ImageFormat.Png);
}
Any help is much appreciated, thanks in advance!!!
Some of the objects you're creating are IDisposable. You need to make sure Dispose() gets called on them. As it stands they're not releasing the memory they've claimed, which is why you get the exceptions.
The easiest way to make sure these items get disposed is to wrap each of them in a using block.

Categories

Resources