Bitmap Image Repetition with Memory Stream - c#

Bitmap Image Multiplication
I Found this Memory stream option to merge the files with given no of repeats.
All the things are working file but in the End, the generated BMP file is not as per enter repetition.
Need help.
Commented lines in code is another way which I tried.
enter code here
public partial class Form1 : Form
{
FileStream bitmapfileload;
Bitmap bmp;
int NoOfRepeats;
public Form1()
{
InitializeComponent();
}
private void btnOpenFile_Click(object sender, EventArgs e)
{
DoubleBuffered = true;
OpenFileDialog openFileDialog1 = new OpenFileDialog
{
InitialDirectory = #"D:\",
Title = "Browse Design Files",
CheckFileExists = true,
CheckPathExists = true,
DefaultExt = "tif",
Filter = "Images (*.BMP;*.TIF)|*.BMP;*.TIF|" + "All files (*.*)|*.*",
FilterIndex = 1,
RestoreDirectory = true,
Multiselect = false,
ReadOnlyChecked = false,
ShowReadOnly = true
};
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
byte[] ba = null;
foreach (String file in openFileDialog1.FileNames)
{
Image temp = new Bitmap(file);
LblSourceWidth.Text = Convert.ToString(temp.Width);
LblSourHeight.Text = Convert.ToString(temp.Height);
temp.Dispose();
bitmapfileload = new FileStream(file,FileMode.Open,FileAccess.Read,FileShare.ReadWrite);
ba = streamToByteArray(bitmapfileload);
}
NoOfRepeats = int.Parse(ltbRepeat.Text); // Text box will give no of repeat for writing proceedure
// List <byte[]> ListBa = new List<byte[]>();
MemoryStream MergeFile = new MemoryStream();
using (MemoryStream allFrameStream = new MemoryStream())
{
for (int i = 0; i < NoOfRepeats; i++)
{
allFrameStream.Write(ba, 0, ba.Length);
allFrameStream.Position = ba.Length * i;
//ListBa.Add(allFrameStream.ToArray());
//ListBa.Add(ba);
}
MergeFile = allFrameStream;
bmp = new Bitmap(MergeFile);
}
//byte[] finaleba = Combine(ListBa.ToArray());
//byte[] finaleba = ListBa.Cast<byte[]>().SelectMany(a => a).ToArray();
//LblListCount.Text = Convert.ToString(finaleba.Length); // List Count = no of repeats for FOR-LOOP
//Stream Finalfile = new MemoryStream(MergeFile);
//richTextBox1.Text = Convert.ToString(Finalfile);
LblDestiWidth.Text = Convert.ToString(bmp.Width); // no change here
LblDestiHeight.Text = Convert.ToString(bmp.Height); // no change here
pictureBox1.Image = bmp;
pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
}
}
private static byte[] Combine(byte[][] arrays)
{
byte[] bytes = new byte[arrays.Sum(a => a.Length)];
int offset = 0;
foreach (byte[] array in arrays)
{
Buffer.BlockCopy(array, 0, bytes, offset, array.Length);
offset += array.Length;
}
return bytes;
}
public static byte[] streamToByteArray(Stream input)
{
MemoryStream ms = new MemoryStream();
input.CopyTo(ms);
return ms.ToArray();
}

The example is not very obvious. I would assume the end goal is to produce a larger image, with multiple copies of the source image stacked on top of each other.
It looks like you are trying to do this by concatenating the streams for an image. This will not work since images have headers. This will cause only the first image to be decoded.
The correct approach would be to create a new, larger, bitmap, and copy all the pixel-values to this bitmap. For example:
public static unsafe Bitmap Repeat(Bitmap source, int count)
{
var targetHeight = source.Height * count;
var target = new Bitmap(source.Width, targetHeight, source.PixelFormat);
var sourceData = source.LockBits(new Rectangle(0,0, source.Width, source.Height), ImageLockMode.ReadOnly, source.PixelFormat);
var targetData = target.LockBits(new Rectangle(0, 0, target.Width, target.Height), ImageLockMode.ReadOnly,
target.PixelFormat);
try
{
var frameLength = sourceData.Stride * sourceData.Height;
for (int i = 0; i < count; i++)
{
var targetPtr = targetData.Scan0 + i * frameLength;
Buffer.MemoryCopy(sourceData.Scan0.ToPointer(), targetPtr.ToPointer(), frameLength * count, frameLength);
}
}
finally
{
source.UnlockBits(sourceData);
target.UnlockBits(targetData);
}
return target;
}
This could be adopted to merge images of different size, but it would require copying the image row by row instead, and some more math regarding height, indices etc. Note that this code is untested, use at own risk.
Another approach would be to use the Graphics api with FromImage and DrawImageUnscaled. This would perhaps be easier if you have multiple images.

Related

Cannot convert between a TensorFlowLite buffer with 150528 bytes and a ByteBuffer with 602112 bytes

Im using xamarin form to create Image Classification which i got the documentation in xamarin blog.
but i got an error when passing image to bytebuffer, once the tensorflow-lite interpreter run the output i got an error saying "Cannot convert between a TensorFlowLite buffer with 150528 bytes and a ByteBuffer with 602112 bytes."
Hope someone help me in this error. im little frustrated, thank you in advance. below is my code
public class TensorflowClassifier : IClassify
{
//FloatSize is a constant with the value of 4 because a float value is 4 bytes
const int FloatSize = 4;
//PixelSize is a constant with the value of 3 because a pixel has three color channels: Red Green and Blue
const int PixelSize = 3;
public List<ImageClassificationModel> Classify(byte[] image)
{
var mappedByteBuffer = GetModelAsMappedByteBuffer();
var interpreter = new Xamarin.TensorFlow.Lite.Interpreter(mappedByteBuffer);
//To resize the image, we first need to get its required width and height
var tensor = interpreter.GetInputTensor(0);
var shape = tensor.Shape();
var width = shape[1];
var height = shape[2];
var byteBuffer = GetPhotoAsByteBuffer(image, width, height);
//use StreamReader to import the labels from labels.txt
var streamReader = new StreamReader(Application.Context.Assets.Open("labels.txt"));
//Transform labels.txt into List<string>
var labels = streamReader.ReadToEnd().Split('\n').Select(s => s.Trim()).Where(s => !string.IsNullOrEmpty(s)).ToList();
//Convert our two-dimensional array into a Java.Lang.Object, the required input for Xamarin.TensorFlow.List.Interpreter
var outputLocations = new float[1][] { new float[labels.Count] };
var outputs = Java.Lang.Object.FromArray(outputLocations);
interpreter.Run(byteBuffer, outputs);
var classificationResult = outputs.ToArray<float[]>();
//Map the classificationResult to the labels and sort the result to find which label has the highest probability
var classificationModelList = new List<ImageClassificationModel>();
for (var i = 0; i < labels.Count; i++)
{
var label = labels[i]; classificationModelList.Add(new ImageClassificationModel(label, classificationResult[0][i]));
}
return classificationModelList;
}
//Convert model.tflite to Java.Nio.MappedByteBuffer , the require type for Xamarin.TensorFlow.Lite.Interpreter
private MappedByteBuffer GetModelAsMappedByteBuffer()
{
var assetDescriptor = Application.Context.Assets.OpenFd("model.tflite");
var inputStream = new FileInputStream(assetDescriptor.FileDescriptor);
var mappedByteBuffer = inputStream.Channel.Map(FileChannel.MapMode.ReadOnly, assetDescriptor.StartOffset, assetDescriptor.DeclaredLength);
return mappedByteBuffer;
}
//Resize the image for the TensorFlow interpreter
private ByteBuffer GetPhotoAsByteBuffer(byte[] image, int width, int height)
{
var bitmap = BitmapFactory.DecodeByteArray(image, 0, image.Length);
var resizedBitmap = Bitmap.CreateScaledBitmap(bitmap, width, height, true);
var modelInputSize = FloatSize * height * width * PixelSize;
var byteBuffer = ByteBuffer.AllocateDirect(modelInputSize);
byteBuffer.Order(ByteOrder.NativeOrder());
var pixels = new int[width * height];
resizedBitmap.GetPixels(pixels, 0, resizedBitmap.Width, 0, 0, resizedBitmap.Width, resizedBitmap.Height);
var pixel = 0;
//Loop through each pixels to create a Java.Nio.ByteBuffer
for (var i = 0; i < width; i++)
{
for (var j = 0; j < height; j++)
{
var pixelVal = pixels[pixel++];
byteBuffer.PutFloat(pixelVal >> 16 & 0xFF / 4);
byteBuffer.PutFloat(pixelVal >> 8 & 0xFF / 4);
byteBuffer.PutFloat(pixelVal & 0xFF / 4);
}
}
bitmap.Recycle();
return byteBuffer;
}
}
On my project i passed the byte array to the classifier
private async void SaveImagePad(object sender, EventArgs e)
{
Stream image = await PadView.GetImageStreamAsync(SignatureImageFormat.Jpeg);
//byte[] bytes = ReadFully(image);
byte[] bytes = new byte[image.Length];
image.Read(bytes, 0, bytes.Length);
List<ImageClassificationModel> classifyImage = DependencyService.Get<IClassify>().Classify(bytes);
}

C# loading lots of images to picturebox but it takes too much memory

This is my code to load 100 images from MySQL database to a Panel,
and each of the image is about 28 KB.
However, the resource monitor shows that it takes more than 2 GB of memory,
and sometimes it leads to out of memory exception.
for (int x = 0; x < Topic4Number; x++)
{
ReaderKeyword.Read();
pbTopic4[x] = new PictureBox();
if (x == 0)
{
pbTopic4[x].Location = new Point(45, 75);
}
else
{
if (pbTopic4[x - 1].Right < 1300)
{
pbTopic4[x].Location = new Point(pbTopic4[x - 1].Right + 35, pbTopic4[x - 1].Top);
}
else
{
pbTopic4[x].Location = new Point(45, pbTopic4[x - 1].Top + 445);
}
}
pbTopic4[x].Size = new Size(Topic4pb_width, Topic4pb_height);
pbTopic4[x].SizeMode = PictureBoxSizeMode.Zoom;
if (ReaderKeyword.HasRows)
{
long len = ReaderKeyword.GetBytes(0, 0, null, 0, 0);
byte[] buffer = new byte[len];
len = ReaderKeyword.GetBytes(0, 0, buffer, 0, (int)len);
MemoryStream ms = new MemoryStream(buffer);
Bitmap img = new Bitmap(ms);
pbTopic4[x].Image = img;
}
}
PanelAll.Controls.AddRange(pbTopic4);
I did use .Dispose() to free the memory once I don't need those PictureBox.
if (pbTopic4 != null)
{
for (int x = 0; x < Topic4Number; x++)
{
pbTopic4[x].Dispose();
pbTopic4[x] = null;
GC.Collect();
}
}
Here are my questions:
Is there any way to reduce the memory used?
Is there any way to improve my code so that it can work faster?
Why it takes that much memory to show the PictureBox?
I made some modification about creating thumbnail of the images, the program works faster and takes much lesser memory.
public bool ThumbnailCallback()
{
return false;
}
if (ReaderTopic4.HasRows)
{
long len = ReaderTopic4.GetBytes(0, 0, null, 0, 0);
byte[] buffer = null;
buffer = new byte[len];
len = ReaderTopic4.GetBytes(0, 0, buffer, 0, (int)len);
MemoryStream ms = new MemoryStream(buffer);
Image.GetThumbnailImageAbort myCallback = new Image.GetThumbnailImageAbort(ThumbnailCallback);
Bitmap img = new Bitmap(ms);
Image myThumbnail = img.GetThumbnailImage(353, 250, myCallback, IntPtr.Zero);
pbTopic4[x].Image = myThumbnail;
}
Reference:
https://learn.microsoft.com/zh-tw/dotnet/api/system.drawing.image.getthumbnailimage?view=dotnet-plat-ext-6.0

when i try read renderd content from the texture of swapchain into byte[] on some pc's the returned array size bigger then expected

so the situation is like this
I use sharp dx to create a swap chain that linked to the XAML element SwapchainPanel now
I have something rendered over it and then I try to grab rendered data into byte[] to place into an image buffer
for that, I use this function ReadBuffer
private D3D11.Texture2D OffscreenstagingTexture;
private D3D11.Texture2D GetTexture(D3D11.Device _Device,
D3D11.DeviceContext _Context,
D3D11.Texture2DDescription _Desc)
{
if (OffscreenstagingTexture == null ||
OffscreenstagingTexture.Description.Width != _Desc.Width ||
OffscreenstagingTexture.Description.Height != _Desc.Height)
{
RemoveAndDispose(ref OffscreenstagingTexture);
D3D11.Texture2DDescription _2DDesc = new D3D11.Texture2DDescription
{
Format = DXGI.Format.B8G8R8A8_UNorm,
Height = _Desc.Height,
Width = _Desc.Width,
SampleDescription = new DXGI.SampleDescription(1, 0),
ArraySize = _Desc.ArraySize,
BindFlags = D3D11.BindFlags.None,
CpuAccessFlags = D3D11.CpuAccessFlags.Read | D3D11.CpuAccessFlags.Write,
Usage = D3D11.ResourceUsage.Staging,
MipLevels = _Desc.MipLevels
};
OffscreenstagingTexture = ToDispose(new D3D11.Texture2D(_Device, _2DDesc));
}
return OffscreenstagingTexture;
}
public byte[] ReadBuffer(D3D11.Texture2D _2DTexture)
{
byte[] data = null;
D3D11.Texture2DDescription _Desc = _2DTexture.Description;
D3D11.Device _Device = _2DTexture.Device;
D3D11.DeviceContext _Context = _Device.ImmediateContext;
D3D11.Texture2D _2DMappedTexture = GetTexture(_Device, _Context, _Desc);
try
{
_Context.CopyResource(_2DTexture, _2DMappedTexture);
int size = _Desc.Width * _Desc.Height * 4;
DataBox box = _Context.MapSubresource(_2DMappedTexture, 0, 0, D3D11.MapMode.Read, D3D11.MapFlags.None, out DataStream stream);
data = ToByteArray(stream);
stream.Dispose();
}
catch (Exception)
{
}
return data;
}
private byte[] ToByteArray(Stream inputStream)
{
using (MemoryStream ms = new MemoryStream())
{
inputStream.CopyTo(ms);
return ms.ToArray();
}
}
now, on most of the pc, it works fine but there are some pc where SwapChainPanle is returning bigger byte[] than expected
if I have 100 * 100 SwapChainPanel then the expected byte array size is 100 * 100 * 4 but it returns a bigger size array then that and because of that on some pc my app getting crashes
and I have checked this same crash at my dell Vostro 3558 also
So I found the fix, I have to update the Readbuffer function
public byte[] ReadBuffer(D3D11.Texture2D _2DTexture)
{
D3D11.Texture2DDescription _Desc = _2DTexture.Description;
D3D11.Device _Device = _2DTexture.Device;
D3D11.DeviceContext _Context = _Device.ImmediateContext;
D3D11.Texture2D _2DMappedTexture = GetTexture(_Device, _Context, _Desc);
_Context.CopyResource(_2DTexture, _2DMappedTexture);
DataBox box = _Context.MapSubresource(_2DMappedTexture, 0, 0, D3D11.MapMode.Read, D3D11.MapFlags.None, out DataStream stream);
var dataRectangle = new DataRectangle
{
DataPointer = stream.DataPointer,
Pitch = box.RowPitch
};
var strid = _Desc.Width * 4;
int size = _Desc.Width * _Desc.Height * 4;
using (var bitmap = new Bitmap(manager.WICFactory,
_2DMappedTexture.Description.Width,
_2DMappedTexture.Description.Height,
PixelFormat.Format32bppBGRA,
dataRectangle))
{
var data = new byte[size];
bitmap.CopyPixels(data, strid);
_Context.UnmapSubresource(_2DMappedTexture, 0);
return data;
}
}
and it works like a charm

FileLabels are not visible

I try to capture desktop screenshot using SharpDX. My application is able to capture screenshot but without labels in Windows Explorer.
I tryed 2 solutions but without change. I tried find in documentation any information, but without change.
Here is mi code:
public void SCR()
{
uint numAdapter = 0; // # of graphics card adapter
uint numOutput = 0; // # of output device (i.e. monitor)
// create device and factory
var device = new SharpDX.Direct3D11.Device(SharpDX.Direct3D.DriverType.Hardware);
var factory = new Factory1();
// creating CPU-accessible texture resource
var texdes = new SharpDX.Direct3D11.Texture2DDescription
{
CpuAccessFlags = SharpDX.Direct3D11.CpuAccessFlags.Read,
BindFlags = SharpDX.Direct3D11.BindFlags.None,
Format = Format.B8G8R8A8_UNorm,
Height = factory.Adapters1[numAdapter].Outputs[numOutput].Description.DesktopBounds.Height,
Width = factory.Adapters1[numAdapter].Outputs[numOutput].Description.DesktopBounds.Width,
OptionFlags = SharpDX.Direct3D11.ResourceOptionFlags.None,
MipLevels = 1,
ArraySize = 1
};
texdes.SampleDescription.Count = 1;
texdes.SampleDescription.Quality = 0;
texdes.Usage = SharpDX.Direct3D11.ResourceUsage.Staging;
var screenTexture = new SharpDX.Direct3D11.Texture2D(device, texdes);
// duplicate output stuff
var output = new Output1(factory.Adapters1[numAdapter].Outputs[numOutput].NativePointer);
var duplicatedOutput = output.DuplicateOutput(device);
SharpDX.DXGI.Resource screenResource = null;
SharpDX.DataStream dataStream;
Surface screenSurface;
var i = 0;
var miliseconds = 2500000;
while (true)
{
i++;
// try to get duplicated frame within given time
try
{
SharpDX.DXGI.OutputDuplicateFrameInformation duplicateFrameInformation;
duplicatedOutput.AcquireNextFrame(miliseconds, out duplicateFrameInformation, out screenResource);
}
catch (SharpDX.SharpDXException e)
{
if (e.ResultCode.Code == SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code)
{
// this has not been a successful capture
// thanks #Randy
// keep retrying
continue;
}
else
{
throw e;
}
}
device.ImmediateContext.CopyResource(screenResource.QueryInterface<SharpDX.Direct3D11.Resource>(), screenTexture);
screenSurface = screenTexture.QueryInterface<Surface>();
// screenSurface.Map(SharpDX.DXGI.MapFlags.Read, out dataStream);
int width = output.Description.DesktopBounds.Width;
int height = output.Description.DesktopBounds.Height;
var boundsRect = new System.Drawing.Rectangle(0, 0, width, height);
var mapSource = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None);
using (var bitmap = new System.Drawing.Bitmap(width, height, PixelFormat.Format32bppArgb))
{
// Copy pixels from screen capture Texture to GDI bitmap
var bitmapData = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat);
var sourcePtr = mapSource.DataPointer;
var destinationPtr = bitmapData.Scan0;
for (int y = 0; y < height; y++)
{
// Copy a single line
Utilities.CopyMemory(destinationPtr, sourcePtr, width * 4);
// Advance pointers
sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch);
destinationPtr = IntPtr.Add(destinationPtr, bitmapData.Stride);
}
// Release source and dest locks
bitmap.UnlockBits(bitmapData);
device.ImmediateContext.UnmapSubresource(screenTexture, 0);
bitmap.Save(string.Format(#"d:\scr\{0}.png", i));
}
// var image = FromByte(ToByte(dataStream));
//var image = getImageFromDXStream(1920, 1200, dataStream);
//image.Save(string.Format(#"d:\scr\{0}.png", i));
// dataStream.Close();
//screenSurface.Unmap();
screenSurface.Dispose();
screenResource.Dispose();
duplicatedOutput.ReleaseFrame();
}
}
After few hours of research and googling i found working solution:
From:
PixelFormat.Format32bppArgb
To:
PixelFormat.Format32bppRgb

Marshal.Copy/UnlockBits hangs

A user selects a portion of an image for a cut and paste operation. I create a new bitmap, paste the selected portion in the new image, wipe the source array and paste it back into the old image. Works but at least half the time it hangs with Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Any thoughts or help?
public BitmapSource CutToNew(double left, double top, double width, double height, double pageWidth, double pageHeight)
{
var destBmp = new Bitmap((int)pageWidth, (int)pageHeight);
var g = Graphics.FromImage(destBmp);
g.FillRectangle(new SolidBrush(Color.White), 0, 0,
(int)pageHeight, (int)pageWidth);
g.Dispose();
var croppedArea = new Rectangle((int)left, (int)top, (int)width, (int)height);
BitmapData croppedSource = _bitmapImage.LockBits(croppedArea,
ImageLockMode.ReadWrite, BitmapImage.PixelFormat);
var croppedDestArea = new Rectangle((int)left, (int)top, (int)width, (int)height);
BitmapData croppedDest = destBmp.LockBits(croppedDestArea,
ImageLockMode.WriteOnly, BitmapImage.PixelFormat);
// Create data array to hold bmpSource pixel data
int stride = croppedSource.Stride;
int numBytes = stride * (int)height;
var srcData = new byte[numBytes];
var destData = new byte[numBytes];
Marshal.Copy(croppedSource.Scan0, srcData, 0, numBytes);
//Tried creating a separate array in case that helped.
Array.Copy(srcData, destData, srcData.Length);
//Often hangs here with Attempted to read or write protected memory.
Marshal.Copy(destData, 0, croppedDest.Scan0, numBytes);
destBmp.UnlockBits(croppedDest);
var retVal = new DocAppImage {BitmapImage = destBmp};
destBmp.Dispose();
//Blank the source area
for (int y = 0; y < srcData.Length; y++)
srcData[y] = 0xFF;
Marshal.Copy(srcData, 0, croppedSource.Scan0, numBytes);
_bitmapImage.UnlockBits(croppedSource);
return retVal.bmpSource;
}
private Bitmap _bitmapImage;
public Bitmap BitmapImage
{
get
{
if (_bitmapImage != null)
return _bitmapImage;
if (FileImage != null)
{
var stream = new MemoryStream(FileImage); //Fileimage=TIFF read from file.
_bitmapImage = new Bitmap(stream);
return _bitmapImage;
}
return null;
}
set
{
if (value != null)
{
ImageCodecInfo codecInfo = GetImageCodecInfo("TIFF");
... implementation to set the bitmap image.
You may want to try specifying your PixelFormat when you create the new object.
For example:
var destBmp = new Bitmap((int)pageWidth, (int)pageHeight, PixelFormat.Format24bppRgb);

Categories

Resources