C# Emgu CV Trouble Predicting Face EigenFaceRecognizer - c#

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);

Related

Bitmap region locked / bitmap in use elsewhere error

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

Align two images from a set of four points each

I'm playing around in accord.net in C# and am trying to align two images. I'm new to computer vision and wondered if accord.net can do this or if I will have to write something myself.
I have two images and for each image I have four points per image. The points are assigned anti clockwise around the image starting with top left so (TL, TR, BR, BL)
Example:
Image 1
Point 1(221, 156)
Point 2(4740, 156)
Point 3(4740, 3347)
Point 4(221, 3347)
Image 2
Point 1(157, 213)
Point 2(4572, 32)
Point 3(4697, 3221)
Point 4(282, 3402)
In the two images image 1's point 1 correlates to image 2's point 1 same with the remaining points.
So what I would like to do is align the two images so the scale, rotation and alignment matches between the two images and I end up with two images when overlayed should look like this image:
So far I have this, which works for rotation and alignment but doesn't scale the image. from my understanding RANSAC seems to be over kill for this job as I'm already correlating the points? Plus I'd like the images output separately for further image processing.
// Images
var img1Path = Path.Combine(filePath, "image1.jpg");
var image1 = new[] { new PointF(221, 156), new PointF(4740, 156), new PointF(4740, 3347), new PointF(221, 3347) };
var img2Path = Path.Combine(filePath, "image2.jpg");
var image2 = new[] { new PointF(157, 213), new PointF(4572, 32), new PointF(4697, 3221), new PointF(282, 3402) };
// Create Bitmaps
var img1 = new Bitmap(img1Path);
var img2 = new Bitmap(img2Path);
var ransac = new RansacHomographyEstimator(0.001, 0.99);
var homographyMatrix = ransac.Estimate(image1, image2);
var blend = new Blend(homographyMatrix, img1) { Gradient = false };
var result = blend.Apply(img2);
result.Save("out.jpg", ImageFormat.Jpeg);
Thanks!
The answer turns out to be a couple of the points were wrong causing the non-scaling of the image content, a stupid but thankfully simple mistake.
and secondly to have accord.net save the output as separate images you use this code:
var filter = new Rectification(homographyMatrix);
var result = filter.Apply(image);
result.Save("out.jpg", ImageFormat.Jpeg);
Thanks to ezfn I also got this working in emguCV but the forum post isn't complete, I worked out this missing code and here it is completed:
var srcImagePath = Path.Combine(FilePath, "image2.jpg");
var dstImagePath = Path.Combine(FilePath, "image1.jpg");
var srcImage = new Image<Bgr, int>(srcImagePath);
var dstImage = new Image<Bgr, int>(dstImagePath);
float[,] srcPoints = { { 221, 156 }, { 4740, 156 }, { 4740, 3347 }, { 221, 3347 } };
float[,] dstPoints = { { 371, 356 }, { 4478, 191 }, { 4595, 3092 }, { 487, 3257 } };
var srcMat = new Matrix<float>(srcPoints);
var dstMat = new Matrix<float>(dstPoints);
var homogMat = new Matrix<float>(3, 3);
var invertHomogMat = new Matrix<float>(3, 3);
var maskMat = new IntPtr();
var s = new MCvScalar(0, 0, 0);
// .Ptr?
CvInvoke.cvFindHomography(srcMat, dstMat, homogMat, HOMOGRAPHY_METHOD.DEFAULT, 3.0, maskMat);
CvInvoke.cvInvert(homogMat, invertHomogMat, SOLVE_METHOD.CV_LU);
CvInvoke.cvWarpPerspective(srcImage, dstImage, invertHomogMat, (int)INTER.CV_INTER_NN, s);
dstImage.Save("2.jpg");
Thanks to everyone for the help!
Well,
Assuming you have the 4 points accurately corresponding - the solution is very simple. You have to calculate the homographic perspective transformation (In short - homography) between the images using the 4 correspondences. You can then use the calculated homography in order warp the second image so that is sits exactly on the first image.
You can do it easily using EmguCV. You have a great example of how to calculate the homography and apply it on an image:
http://www.emgu.com/forum/viewtopic.php?f=7&t=4122
Where you can treat image 2 as sourceImage, and image 1 as dest image.

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.

How to use the Nokia Imaging SDK's BlendFilter on WP8?

I'm staring with the Nokia Imaging SDK to play a little with it. Now, I'm facing the problem where I have an Image which already exists (in a folder in my visual studio solution) and I want to convert this image in order to use it in the BlendFilter class of the Nokia Imaging SDK. However I don't know how to use it.
I was trying to convert the existing image in a stream and then pass it as a parameter to the BlendFilter constructor. But not luck. The compiler says that the best overload method match ... has some invalid arguments.
This is the way I'm trying to load the existing image to a stream:
Image image = new Image();
image.Source = new BitmapImage(new Uri("/Images/Template3.2.png", UriKind.Relative));
BitmapImage bitImage = new BitmapImage(new Uri("/Images/Template3.2.png", UriKind.Relative));
WriteableBitmap Bitmap = new WriteableBitmap(bitImage);
And then:
var BlendFilter = new BlendFilter(bitImage, BlendFunction.Add); --> the compiler error is here
Does anyone know how to use the BlendFilter class? any example would be very helpful.
Regards!
Blend filter takes an IImageProvider as input. That means you can use any of the X-ImageSource classes as input and it will do all the work internally.
If you have a stream of the image I suggest you create an StreamImageSource and pass that to BlendFilter.
The list of different image sources is quite long, I suggest you look into the documentation and chose the one that is most appropriate to you.
Here is an example that takes a stream of an image as input, and blends a new image on top of it. For simplicity the other image is just an image filled with one color (ColorImageSource), but you can set any IImageProvider as source: chose the most convenient one.
using (var backgroundSource = new StreamImageSource(stream))
using (var filterEffect = new FilterEffect(backgroundSource))
{
using (BlendFilter blendFilter = new BlendFilter())
{
var size = new Windows.Foundation.Size(400, 400);
var color = Windows.UI.Color.FromArgb(250, 128, 255, 200);
blendFilter.ForegroundSource = new ColorImageSource(size, color);
blendFilter.BlendFunction = BlendFunction.Add;
filterEffect.Filters = new[] { blendFilter };
var result = await new JpegRenderer(filterEffect).RenderAsync();
}
}

How to achieve good sharpness with twain/emgu/open cv?

I am using an Epson Perfection V700 scanner and selecting the following options when scanning using their tool:
ICM Color correction (source: EPSON-Standard and target: sRGB)
Unsharp Mask (medium)
That produces this image:
Now my problem is this - I actually need to interact with this scanner using TWAIN .Net and when I do so, the image I get back is this:
Aside: I unselected the aforementioned two options and scanned again with the Epson and got a very similar image to what I get through TWAIN.
So I figure that perhaps these are post processing steps that I can do myself on the image (maybe they are done in the hardware somehow though, I don't know).
I am using EmguCV so first of all I created an extension method that applies the ICM (I struggled to find any documentation for this, so it is a bit of a guess and maybe I am wrong straight away but I got the information from here: The bitmap transform class and it seems to make a difference to the image):
public static Image<Bgr, TDepth> ApplyIcm<TDepth>(
this Image<Bgr, TDepth> source,
string sourceIcm,
string targetIcm)
where TDepth : new()
{
var target = source.CopyBlank();
using (source)
{
using (var b = source.Bitmap)
{
using (var memory = new MemoryStream())
{
b.Save(memory, ImageFormat.Bmp);
memory.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
var ccb = new ColorConvertedBitmap();
ccb.BeginInit();
ccb.Source = bitmapImage;
ccb.SourceColorContext =
new ColorContext(new Uri(sourceIcm));
ccb.DestinationColorContext =
new ColorContext(new Uri(targetIcm));
ccb.EndInit();
var encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(ccb));
using (var ms = new MemoryStream())
{
encoder.Save(ms);
target.Bitmap = new Bitmap(ms);
}
}
}
}
return target;
}
Then I looked at that unsharpen thing and came across this question: How to sharpen an image in OpenCV? which says:
You use a gaussian smoothing filter and subtract the smoothed version from the original image
(I also checked this question to find out what the equivalent emgucv call is Why might EmguCV Gaussian blur not return identical results as OpenCV Gaussian blur?) and came up with this additional extension method:
public static Image<Bgr, TDepth> UnsharpMask<TDepth>(
this Image<Bgr, TDepth> source,
Size kernelSize,
int kernelHoritonalStandardDeviation,
int kernelVerticalStandardDeviation,
double alpha,
double beta,
double gamma)
where TDepth : new()
{
Image<Bgr, TDepth> ret = source.CopyBlank();
CvInvoke.cvSmooth(source,
ret,
SMOOTH_TYPE.CV_GAUSSIAN,
kernelSize.Width,
kernelSize.Height,
kernelHoritonalStandardDeviation,
kernelVerticalStandardDeviation);
CvInvoke.cvAddWeighted(source, alpha, ret, beta, gamma, ret);
return ret;
}
Now I call it like so:
string sourceIcm = #"C:\Windows\System32\spool\drivers\color\ewrgb18.icm";
string targetIcm = #"C:\Windows\System32\spool\drivers\color\ewsrgb.icm";
using(var im = new Image<Bgr, byte>("out.bmp"))
{
using (var icmmed = im.ApplyIcm(sourceIcm, targetIcm))
{
using (var ret = icmmed.UnsharpMask(new Size(0, 0), 5, 5, 2.4, -1.5, 0))
{
ret.Save("ret.bmp");
}
}
}
and this is the result:
Not very good! :-(
I have fiddled with the parameters endlessly but I just cannot work out how (or even if) I can achieve the same result as the Epson tool.
So, my question is:
Does anyone know if it is possible to achieve a result using opencv/emgucv (or even TWAIN - I had a look through the documentation for that and tried adjusting some of the capability parameters but I just made the image worse) that is similar in sharpness to the original image above or is there another technique I should try (could it be that I would need to know some details about the hardware itself in order to achieve correct sharpening)?
I think you should know how using WIA (Windows Image Acquisition) in your project, you may don't need to get access to hardware using opencv. WIA is used for integrating with webcams and scanners. Or, you can use TWAIN as you mentioned
have a look at these examples they could by helpful for your project:
using WIA
and
using TWAIN
Concerning the sharpening, you can use opencv functionality at software level, as another choice to solve your problem

Categories

Resources