I use this code in C# to decode (not detect) a QRCode and it works:
LuminanceSource ls = new RGBLuminanceSource(image, image.Width, image.Height);
Result result = new QRCodeReader().decode(new BinaryBitmap(new HybridBinarizer(ls)));
Now I would like to detect a QRCode in a more complex image with a lot of other stuffs such images and text. I'm not able to understand how to accomplish this because I cannot find any sample and transforming Bitmap (C#) to Bitmatrix for Detector (zxing) is not so direct.
Does anyone have a piece of code to give me?
thanks a lot
UPDATE
I try this code but I get a ReaderException:
The code:
LuminanceSource ls = new RGBLuminanceSource(bitmap, bitmap.Width, bitmap.Height);
QRCodeMultiReader multiReader = new QRCodeMultiReader();
Result[] rs = multiReader.decodeMultiple(new BinaryBitmap(new HybridBinarizer(ls)), hints);
return rs[0].Text;
The exception
com.google.zxing.ReaderException:
in com.google.zxing.qrcode.detector.FinderPatternFinder.selectBestPatterns()
in com.google.zxing.qrcode.detector.FinderPatternFinder.find(Hashtable hints)
in com.google.zxing.qrcode.detector.Detector.detect(Hashtable hints)
in com.google.zxing.qrcode.QRCodeReader.decode(BinaryBitmap image, Hashtable hints)
in com.google.zxing.qrcode.QRCodeReader.decode(BinaryBitmap image)
in ...Logic.BarCodeManager.QRCodeReader(Bitmap bitmap) in
UPDATE 02/12/2011
I have just tried to scan the printed QRCode (with the piece of code on the top of the post) with an App on my iPhone and it works well! So the problem is surely in the detection/decode phase.
QR Codes always have the three squares in the top left, top right, bottom left corners. Knowing this you should be able to search for that square pattern within the pixel data of the image you are parsing, to figure out the top left, width and height of the qr code with a bit of simple logic parsing.
Though it's old. I still want to post it in case someone needs it.
The noise of images makes them difficult for zxing to detect qrcodes. The results are much better if the images are noise free. I use a simple method to reduce noise of scanned images. It can be done by shrinking the image. The shrink factor may vary by the noise of images. I found the factor 3 works fine in my case.
private string Qrreader(Bitmap x)
{
BarcodeReader reader = new BarcodeReader { AutoRotate = true, TryHarder = true };
Result result = reader.Decode(x);
string decoded = result.ToString().Trim();
return decoded;
}
works for me! TryHarder makes it search in the whole picture
Related
TL;DR mode:
Is there a way to get a BitmapSource from a DrawingVisual without using RenderTargetBitmap, whilst still having the 'crop' utility a RenderTargetBitmap affords you.
Full Question:
My issue is an awkward one, I'm using a number of image editing processes to effectively crop, resize, rotate and perform all that usual stuff expected of even basic image editing in an automated manner, and everything is working just fine except for one incredibly annoying caveat: Images profiled as CMYK can't be processed then saved again as CMYK without doing a little dance with RenderTargetBitmap (which breaks and refuses to create an instance of itself with any PixelFormat other than "PixelFormats.Default" or "PixelFormats.Pbgra32") and the FormatConvertedBitmap class to even get the image to go back to being a CMYK
So where you might think, 'oh this is easy, just...'
var newCanvas = new RenderTargetBitmap(
width, height, // Dimensions
96, 96, // DPI
PixelFormats.Cmyk32); // <---- Like so
The class just breaks and gives the error:
System.ArgumentException: ''Cmyk32' PixelFormat is not supported for this operation. Arg_ParamName_Name'
So instead I'm having to do a little trick with the full-code involved being as such:
string myUri = "" // Some CMYK Image
int newWidth = 600; // Just for this example, real values come in elsewhere
int newHeight = 400; // As above
// Load the image
BitmapDecoder decoder = BitmapDecoder.Create(new Uri(FullURL), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
//Define the new size
Rect rect = new Rect(0, 0, newWidth, newHeight) // Set new size of img.
DrawingGroup group = new DrawingGroup(); // Create a group for img layers
group.Children.Add(new ImageDrawing(image, decoder.Frames[0])); // Add loaded image
// (sometimes more children will be added, so it must be a DrawingGroup)
// Prepare the group to be rendered.
var drawingVisual = new DrawingVisual();
using (var drawing = drawingVisual.RenderOpen()) {
// Additional things such as shapes may be added here later
drawing.DrawDrawing(group);
}
// Create what is effectively the canvas the image goes into
var resizedImage = new RenderTargetBitmap(
newWidth, newHeight,
96, 96,
PixelFormats.Default); // <---- I'm forced to do this
// And then render it
resizedImage.Render(drawingVisual);
// THEN after that, convert it back too CMYK32
var fcb = new FormatConvertedBitmap(resizedImage, PixelFormats.Cmyk32, null, 0);
This "FormatConvertedBitmap fcb" is then saved as a TIFF, and a CMYK TIFF at that. Success, right?
Well no, during this little dance between "PixelFormats.Default" and "PixelFormats.Cmyk32" done with RenderTargetBitmap -> FormatConvertedBitmap, it seems the Default conversion is enough to upset all of the colours inside the file, not a lot, but enough that it's no longer the true image that was input at the start.
For example, what might have been a block of CMYK(0,0,0,100) pixels, so pure jet-black, might now be CMYK(2,3,2,96). So it's dropped the blac(K) down 4%, and pushed the (C)yan, (M)agenta, and (Y)ellow up 2-3%, creating a foggier image that looks washed-out.
My working theory, although i have no way yet of proving it 100% true, is that this problem is being caused by the RenderTargetBitmap effectively making it an RGB image first.
Is there any way at all to truly preserve CMYK images whilst editing them in the layered and 'croppable' way I showed, or some way to effectively save the image without needing to use RenderTargetBitmap? It seems insane that Microsoft even has this class that has the option to set PixelFormat, except the only format that works is what amounts to "Default". Why even give the option in the function? (I realize I'm ranting now).
Does anyone know of a way to solve this weird issue?
How can I extract an object (Signature) from a white background image (A4 Paper) taken using a mobile camera in C#/.NET and crop it if possible?
I am trying ImageMagick library, but the out put is not 100% correct, I tried to manipulate the values without luck:
string GetSignature(string signature) {
string withoutBackground = "signature_no_bg.png";
using (var image = new MagickImage(signature))
{
image.Transparent(MagickColors.White);
// -alpha set
image.Alpha(AlphaOption.Set);
// -channel RGBA (don't think you need this)
// -fuzz 50%
image.ColorFuzz = new Percentage(40);
// -fill none
image.Settings.FillColor = MagickColors.None;
// -floodfill +0+0 white
image.FloodFill(MagickColors.White, 0, 0);
image.Write(Server.MapPath(withoutBackground));
}
return withoutBackground;
}
Using the above code, the following image:
was converted to:
Another option was to https://www.remove.bg/tools-api , it worked perfectly but its a bit expensive.
Any suggestions to enhance my ImageMagick code or to use another kind of libraries ?
I'm not experienced with ImageMagick, but I would try the following steps as a simple(st) alternative to filling:
Convert to greyscale
Threshold the image
Use the thresholded image as mask to extract the signature from the original RGB Image.
You will have to try how well it performs on your own images though. You can also try to include some blurring on the greyscale image before thresholding against noise.
I would like to be able to recognize the position (center) and the angle of some small components with openCV with C#. To achieve that, I am grabbing pictures from a webcam and try to process them with the Canny algorithm. Unfortunately, the results are not that good as expected. Sometimes it is ok sometimes it is not.
I have attached an example image from the cam and the corresponding output of OpenCV.
I hope that someone could give me hints or maybe some code snippets, how to achieve my desired results. Is this something that is usually done with AI?
Example images:
Input:
Output 1:
Output 2:
Expected:
Thanks.
Actual code:
Mat src;
src = BitmapConverter.ToMat(lastFrame);
Mat dst = new Mat();
Mat dst2 = new Mat();
Cv2.Canny(src, dst, hScrollBar1.Value, hScrollBar2.Value);
// Find contours
OpenCvSharp.Point[][] contours; //vector<vector<Point>> contours;
HierarchyIndex[] hierarchyIndexes; //vector<Vec4i> hierarchy;
Cv2.FindContours(dst, out contours, out hierarchyIndexes, RetrievalModes.External, ContourApproximationModes.ApproxTC89L1);
foreach (OpenCvSharp.Point[] element in contours)
{
var biggestContourRect = Cv2.BoundingRect(element);
Cv2.Rectangle(dst,
new OpenCvSharp.Point(biggestContourRect.X, biggestContourRect.Y),
new OpenCvSharp.Point(biggestContourRect.X + biggestContourRect.Width, biggestContourRect.Y + biggestContourRect.Height),
new Scalar(255, 0, 0), 3);
}
using (new Window("dst image", dst)) ;
using (new Window("src image", src)) ;
If you already have a ROI (the box) and you just want to compute the actual orientation of it, you could use the contour inside the right box and compute its moments. A tutorial on how to do this is here (Sorry only C++).
Once you have the moments you can compute the orientation easily. To do this follow the solution here.
If you have trouble figuring out the right box itself, you are actually half way with canny boxes. You could then further try:
Equalize source image:
Posterize next (to 2 levels):
Threshold (255):
Then you can use all the canny boxes you found in the centre and use them as masks to get the right contour in the thresholded image. You can then find the biggest contour here and compute its orientation with image moments. Hope this helps!
My aim is to detect multiple datamatrices on a larger image like this (the four big):
Based on several code samples, I made a small test program:
Bitmap image = getImage();
DataMatrixReader reader = new DataMatrixReader();
GenericMultipleBarcodeReader genericReader = new genericMultipleBarcodeReader(reader);
Dictionary<DecodeHintType, object> hints = new Dictionary<DecodeHintType,object>();
hints.Add(DecodeHintType.TRY_HARDER, true);
BitmapLuminanceSource source = new BitmapLuminanceSource(image);
HybridBinarizer binarizer = new HybridBinarizer(source);
BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);
Result[] results = genericReader.decodeMultiple(binaryBitmap,hints);
show(results);
It could'nt detect any code on the large image.
But it can detect the code, when its cropped like that:
After that I merged two generated data matrices, and it failed too:
Last I ran two more test with slightly cropped images, both failed:
So it seems this library is not robust at all, or maybe I use it wrong.
Any idea how to improve my results? (including other libraries and preprocessing)
It can't be said that the library is not robust but there are two key factors affecting you here:
Zxing's data-matrix detection algorithm assumes that the barcode is centered. Or more precisely, that the center of the image is inside the data-matrix.
Zxing's multiple reader specially fails when barcodes are grid-aligned.
My recommendation is to implement your own MultipleBarcodeReader taking into account what I've mentioned.
A naive approach could be to take sample images centered over a grid of points spaced so every data-matrix (no matter its position within the image) contain at least one of the points inside. You just have to make sure to exclude duplicated codes.
I need to capture an area within my desktop. But I need this area to be very high resolution (like, at least few thousand's pixels horizontal, same goes for vertical). Is it possible to get a screen capture that has high density of pixels? How can I do this? I tried capturing the screen with some AutoIt script, and got some very good results (images that were 350MB big), now I would like to do the same using C#.
Edit:
I am doing my read/write of a .tif file like that, and it already loses most of the data:
using (Bitmap bitmap = (Bitmap)Image.FromFile(#"ScreenShot.tif")) //this file has 350MB
{
using (Bitmap newBitmap = new Bitmap(bitmap))
{
newBitmap.Save("TESTRES.TIF", ImageFormat.Tiff); //now this file has about 60MB, Why?
}
}
I am trying to capture my screen like that, but the best I can get from this is few megabytes (nowhere near 350MB):
using (var bmpScreenCapture = new Bitmap(window[2], window[3], PixelFormat.Format32bppArgb))
{
using (var i = Graphics.FromImage(bmpScreenCapture))
{
i.InterpolationMode = InterpolationMode.High;
i.CopyFromScreen(window[0], window[1], 0, 0, bmpScreenCapture.Size, CopyPixelOperation.SourceCopy);
}
bmpScreenCapture.Save("test2.tif", ImageFormat.Tiff);
}
You can't gather more information than the source has.
This is a basic truth and it does apply here, too.
So you can't capture more the your 1920x1080 pixels at their color depth.
OTOH, since you want to feed the captured image into OCR, there a few more things to consider and in fact to do..
OCR is very happy if you help it by optimizing the image. This should involve
reducing colors and adding contrast
enlarging to the recommended dpi resolution
adding even more contrast
Funnily, this will help OCR although the real information cannot increase above the original source. But a good resizing algorithm will add invented data and these often will be just what the OCR software needs.
You should also take care to use a good i.e. non lossy format when you store the image to a file like png or tif and never jpg.
The best way will have to be adjusted by trial and error until the OCR results are good enough.
Hint: Due to font antialiasing most text on screenshots is surrounded by a halo of colorful pixels. Getting rid of it by the reducing or even removing saturation is one way; maybe you want to turn it off in your display properties? (Check out ClearType!)