I'm using Emgu.CV to templateMatch and to save Images.
Unfortunetly I have ran into an issue that I have no been able to solve for a weeks.
Problem is that i serialize byte array and size from original Image to json file, and whenever i try to convert it back sometimes the image is distorted.
I have already tried skipping over serializing procces and it still became distorted.
Here is code of converting procces:
Image<Bgr565, byte> screenCrop = SnipMaker.takeSnip();//method creates screenshot at this point when i display the images they are 100% correct
byte[] data = screenCrop.Bytes;//I would get normaly all this from json file(in this case im skipping over it)
Mat mat = new Mat(screenCrop.Rows, screenCrop.Cols, screenCrop.Mat.Depth, asset.NumberOfChannels);
Marshal.Copy(data, 0, mat.DataPointer, screenCrop.asset.Cols * screenCrop.asset.Rows * asset.NumberOfChannels);
Image<Bgr565, byte> img = mat.ToImage<Bgr565, byte>();//This image is suddenly distorted
Problem is that this results depending on "I'm not sure what" is either prefecly good image or skwed one:
normal result
same code different result
Its almost like its sometimes 1 pixel behind but only thing that is changing is size and dimentions of screen shots.
I have tried dirrect ways like
Image<Bgr, byte> img = new Image<Bgr, byte>(width, height);
img.Bytes = data;//data is byte array that i got from file
This also gives sometimes correct picture but other times it throws an exeption (out of range exception in marshal.cs when trying to copy bytes from data to img)
only thing that i suspect at this point is that im doing something wrong whenever im taking screenshot but im not sure what:
public static Image<Bgr565, byte> Snip()
{
int screenWidth = (int)System.Windows.SystemParameters.PrimaryScreenWidth;
int screenHeight = (int)System.Windows.SystemParameters.PrimaryScreenHeight;
using (Bitmap bmp = new Bitmap(screenWidth, screenHeight))
{
using (Graphics gr = Graphics.FromImage(bmp))
gr.CopyFromScreen(0, 0, 0, 0, bmp.Size);
using (var snipper = new SnippingTool(bmp))
{
if (snipper.ShowDialog() == true)
{
Bitmap bitmapImage = new Bitmap(snipper.Image);
Rectangle rectangle = new Rectangle(0, 0, bitmapImage.Width, bitmapImage.Height);//System.Drawing
BitmapData bmpData = bitmapImage.LockBits(rectangle, ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);//System.Drawing.Imaging
Image<Bgr565, byte> outputImage = new Image<Bgr565, byte>(bitmapImage.Width, bitmapImage.Height, bmpData.Stride, bmpData.Scan0);
bitmapImage.Dispose();
snipper.Close();
return outputImage;
}
}
return null;
}
}
So far I have not been able to solve this and knowing my luck noone will proppably anwser me here. But please could someone help me with this?
Thank you in advance
So thank you to everyones help.
The issue was indeed in the screenshot script. I've used incorrect combination of
pixel formats which resulted in inconsistent bit transfer.
But because the step property in Image<bgr,byte>.Mat was calculated based on the width of the image (Emgucv SC):
step = sizeof(byte) * s.Width * channels;
It caused that some of the images looked normal and other didn't.(speculation based on observation)
Fix:
change all Image<Bgr, byte> to Image<Bgra, byte>
to make it 32bit and then change:
BitmapData bmpData = bitmapImage.LockBits(rectangle, ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
to:
BitmapData bmpData = bitmapImage.LockBits(rectangle, ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
Hope this will help someone in the future. : )
Related
my goal is, to shift an Image an x-amount of pixels to the right. I guess this can be achieved by using WarpAffine. This is at least what my reseach tells me. I used quite a variety of different approaches, like:
CvInvoke.WarpAffine(realImage, transformedImage, transformationMatrix, new Size(realImage.Size);
//or while m is the Mat used to create realImage
transImage.WarpAffine(m,Emgu.CV.CvEnum.Inter.Area,Emgu.CV.CvEnum.Warp.Default,Emgu.CV.CvEnum.BorderType.Default,new Gray());
I get the Exeption:
Exception thrown: 'Emgu.CV.Util.CvException' in Emgu.CV.Platform.NetStandard.dll
[ WARN:0] global E:\bb\cv_x86\build\opencv\modules\videoio\src\cap_msmf.cpp (436) `anonymous-namespace'::SourceReaderCB::~SourceReaderCB terminating async callback
I guess I am using it the wrong way, but there is no suiting example online for me to learn from.
Does anyone has a clean way to explain it to me?
Thank you in advance!
If you are shifting the pixels x amount to the right, I assume that there would be black empty pixels on the left side? If so, you could create an ROI and cut off some pixels on the right, since you are shifting all pixels to the right, and copy the image onto another image.
//The image that you want to shift pixels with
Image<Bgr, byte> inputImage = new Image<Bgr, byte>(1000, 1000);
//The output image
Image<Bgr, byte> image = new Image<Bgr, byte>(990, 1000);
//Create the roi, with 10 pixels cut off from the right side because of the shift
inputImage.ROI = new Rectangle(0, 0, inputImage.Width - 10, inputImage.Height);
inputImage.CopyTo(image);
CvInvoke.ImShow("The Output", image);
CvInvoke.WaitKey(0);
EDIT
Now lets say you want to keep that black stripe on the left side of the image as well. Doing this is very similar to the code above, but only with a few modifications.
//The image that you want to shift pixels with
Image<Bgr, byte> inputImage = new Image<Bgr, byte>(1000, 1000);
//The output image
Image<Bgr, byte> image = new Image<Bgr, byte>(1000, 1000);
//Create the roi, with 10 pixels cut off from the right side because of the shift
inputImage.ROI = new Rectangle(0, 0, inputImage.Width - 10, inputImage.Height);
//The image we want to shift, the same one we created a ROI with,
//has the dimensions 990 X 1000 and the output image has
//dimensions of 1000 x 1000. Unfortunately, in order to paste
//an image onto another image, they need to be the same dimensions.
//How do we do this? We must create an ROI with the output image
//that has the same dimensions as the input image.
image.ROI = new Rectangle(10, 0, image.Width, image.Height);
//Now we can past the image onto the output because the dimensions match
inputImage.CopyTo(image);
//Inorder to make our output seem normal, we must empty the ROI of the output image
image.ROI = Rectangle.Empty;
CvInvoke.ImShow("The Output", image);
CvInvoke.WaitKey(0);
I am trying to convert a grayscale Bitmap (Format16bppGrayScale) to a color Bitmap (Format32bppArgb) like so:
Bitmap color = gray.Clone(new Rectangle(0, 0, gray.Width, gray.Height), PixelFormat.Format32bppArgb);
I keep getting a System.OutOfMemoryException. I have been researching and this error usually occurs when the rectangle provided to Clone is bigger than the actual image that you are trying to clone. This is not the case here since I am using the image dimensions to create the rectangle. Are there known issues with this type of conversions? Are there any other ways to achieve a copy in a different PixelFormat?
Thanks,
I read, people have problem like you. Try this way:
Change PixelFormat, while you try clone, system have problem.
Biggest reason is new definition of Bitmap.
Bitmap clone = new Bitmap(gray.Width, gray.Height, PixelFormat.Format32bppArgb);
using (Graphics gr = Graphics.FromImage(clone)) {
gr.DrawImage(color, new Rectangle(0, 0, clone.Width, clone.Height));
}
Or without DrawImage function:
using (Bitmap color = gray.Clone(new Rectangle(0, 0, gray.Width, gray.Height), PixelFormat.Format32bppArgb))
{
// color is now in the desired format.
}
sorry if this question is silly, i'm just starting with C#
I have an image in disk which I know the path. The image is 20000x10000 aprox in size and around 400MB in size (i know this looking at the image)
I need to load it in the code and resize it since the progam dies if I try to put in a picture box 400m of image, but if I do
Bitmap b0 = new Bitmap(pathImage);
int newWidth = (int)(b0.Width * escala);
int newHeight = (int)(b0.Height * escala);
// Convert other formats (including CMYK) to RGB.
Bitmap newImage = new Bitmap(newWidth, newHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
// Draws the image in the specified size with quality mode set to HighQuality
using (Graphics graphics = Graphics.FromImage(newImage))
{
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.DrawImage(b0, 0, 0, newWidth, newHeight);
}
But when I do Bitmap b0 = new Bitmap(pathImage); it just dies since 20000x10000x32 it's too much for my poor memory.
The problem is that if not specified, c# just uses as pixel format Format32bppArgb but I want Format1bppIndexed, and the only way i've found to change the format is with the constructor
Bitmap(
int width,
int height,
int stride,
PixelFormat format,
IntPtr scan0
)
But for this constructor I need to know the size of the image, which in the code I don't know because it dies before the code can get it and know the size...
I'm lost. Any ideas?
EDIT:
Doing this
int width;
int height;
Image tif;
using (FileStream file = new FileStream(rutaImagenEntrada, FileMode.Open, FileAccess.ReadWrite))
{
using ( tif = Image.FromStream(stream: file,
useEmbeddedColorManagement: false,
validateImageData: false))
{
width = (int)tif.PhysicalDimension.Width;
height = (int)tif.PhysicalDimension.Height;
}
file.Close();
}
I get the size of the image I cannot load, and can create a bitmap with the pixelformat and size that i wanted, BUT everyway I try to upload it doesn't work, it still crashes on me.
I've tried using graphics .draw(tiff...) and tiff.getThumbnail(new size...) and it just dies on me.
It targets x86 and the .Net 3.5 since those are the requirements of the client...
Any ideas?
If FileStream works to open the file, you might want to give some of the other constructors of Bitmap a try, in particular Bitmap(Stream).
If that doesn't help, you might try loading the raw bmp data, creating a Bitmap with the format you want and copying the data into the Bitmap manually (I'm not familiar with C# Bitmaps, but there should be some way of accessing the data).
Hope this helps.
I have a small piece of C# code that uses a Kinect to detect up to 4 glyphs and draws and polygon between them on a canvas, as seen here:
I've tried to follow along to this in order to implement 2D augmented reality and project an image within the created polygon. I've read in a source image and tried to apply the BackwardQuadrilateralTransformation to it but can't seem to display the transformed image. I am probably using the wrong method but I have tried to convert the image and paint it onto a canvas with no luck. I'm not sure if I'm just massively misunderstanding the method and maybe it isn't possible, any help would be greatly appreciated. I can supply more sample code if required.
private void GlyphBackQuad(List<IntPoint> quadpoints)
{
Bitmap srcImage = new Bitmap( // my sample image filepath );
UnmanagedImage sourceImage = UnmanagedImage.FromManagedImage(srcImage);
BackwardQuadrilateralTransformation filter = new BackwardQuadrilateralTransformation(sourceImage, quadpoints);
filter.Apply(sourceImage);
Bitmap bmp = sourceImage.ToManagedImage();
ImageBrush ib = new ImageBrush();
ib.ImageSource = ConvertDrawingImage2MediaImageSource(bmp);
PolyCanvas.Background = ib;
}
After a further play around with the code I think I have developed a partial solution, it still needs some work but hopefully this is more helpful for people to read/debug.
private void GlyphBackQuad(List<IntPoint> quadpoints, Bitmap bmp)
{
// Read in bitmap source image and clone it to the same format as destination
Bitmap srcImage = AForge.Imaging.Image.Clone(new Bitmap( // my sample filepath), System.Drawing.Imaging.PixelFormat.Format24bppRgb);
System.Drawing.Imaging.BitmapData bitmapData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
// Convert to unmanaged image
UnmanagedImage unmanagedImage = new UnmanagedImage( bitmapData );
// Filter
BackwardQuadrilateralTransformation filter = new BackwardQuadrilateralTransformation(srcImage, quadpoints);
filter.ApplyInPlace(unmanagedImage);
// Convert back to managed image and save
Bitmap managedImage = unmanagedImage.ToManagedImage();
managedImage.Save( // my save filepath, System.Drawing.Imaging.ImageFormat.Png);
}
I have been having a tough time creating a thumbnail that is not horrible quality. So far the best code i've come up with is:
Bitmap bmp = new Bitmap(width, height);
Graphics graphic = Graphics.FromImage(bmp);
graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphic.SmoothingMode = SmoothingMode.HighQuality;
graphic.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphic.CompositingQuality = CompositingQuality.HighQuality;
graphic.DrawImage(photo, 0, 0, width, height);
return imageToByteArray(bmp);
Which produces this gem:
If I resize the same image in Paint.NET i get this:
Which is WAY better. Everything I've found on line points me to some variation of the code I have above. I know Paint.NET was open source at one point. Does anyone know what magic they were doing to create such nice resize functionality and if that functionality can be reproduced in C#?
UPDATE:
The original image from this example was a jpg
GIFs
I recalled reading that .NET has issues with palette-based formats, like GIF, so I dug up a few articles.
This article describes how to quantize (pick an optimum palette) to improve quality: http://msdn.microsoft.com/en-us/library/aa479306.aspx, as does this (badly formatted) article.
In brief, I believe GDI+ picks a non-optimum palette when performing the resize.
PNGs
PNGs are palette-based, so they may be prone to the same issues as GIFs. I'm not sure if it matters that the palette can be much larger.
JPEG-friendly example
This code should work fine on JPEGs (but does not render GIFs smoothly). If you try it and it pixelates a JPEG, then there is probably something else going on.
private static byte[] GetScaledImage( byte[] inputBytes, int width, int height ) {
Image img = null;
using( MemoryStream ms = new MemoryStream() ) {
ms.Write( inputBytes, 0, inputBytes.Length );
img = Image.FromStream( ms );
}
using( MemoryStream ms = new MemoryStream() ) {
using( Image newImg = new Bitmap( width, height ) ) {
using( Graphics g = Graphics.FromImage( newImg ) ) {
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage( img, 0, 0, width, height );
newImg.Save( ms, img.RawFormat );
return ms.GetBuffer();
}
}
}
}
since Bitmap(int,int) is effectively Bitmap(int,int,PixelFormat.Format32bppArgb) I think the problem is in source image. Try to create another intermediate copy of the image of the same size as source image if using palette, then use that 32bppArgb image source for your resize function.