Convert Mat to MemoryStream OpenCV for Android - c#

With OpenCVSharp, it is very easy to convert a Mat object to MemoryStream.
private Stream GetStream(Mat mat, string ext)
{
return mat.Clone().ToMemoryStream(ext);
}
How can I do this with Xamarin.OpenCV.Droid?
https://github.com/NAXAM/opencv-android-binding/

You use Imgcodecs.Imencode to populate a MatOfByte instance from a Mat instance and a desired encoding and then you can acquire a byte[] from that to populate a Stream.
Note: OpenCVSharp has a series of helper functions that perform those functions when calling their ToMemoryStream method.
Example (read a png from a file and save it to a jpg):
using (var matFromPng = Imgcodecs.Imread(cachedFileName, Imgcodecs.ImreadColor))
using (var vect = new MatOfByte())
{
if (Imgcodecs.Imencode(".jpg", matFromPng, vect))
{
var stream = new MemoryStream(vect.ToArray());
// Do something with your stream...
cachedFileName = Path.Combine(CacheDir.AbsolutePath, "her.jpg");
using (var cacheFilestreamToJpeg = new FileStream(cachedFileName, FileMode.Create, FileAccess.Write))
{
stream.CopyTo(cacheFilestreamToJpeg);
}
stream.Dispose();
}
}

Related

How use Magick.NET LosslessCompress with Stream and IFormFile

I am trying to compress image(usually around 5-30) quality / size with Magick.NET library, and I cant really understand how can I use ImageOptimizer class and call LosslessCompress() method using stream.
Do I need to use FileStream or MemoryStream?
Do I need to save / create a temp file on server for each image and then proceed with the compression flow? (Performance?)
Anything else?
Simple Code example:
private byte[] ConvertImageToByteArray(IFormFile image)
{
byte[] result = null;
// filestream
using (var fileStream = image.OpenReadStream())
// memory stream
using (var memoryStream = new MemoryStream())
{
var before = fileStream.Length;
ImageOptimizer optimizer = new ImageOptimizer();
optimizer.LosslessCompress(fileStream); // what & how can I pass here stream?
var after = fileStream.Length;
// convert to byte[]
fileStream.CopyTo(memoryStream);
result = memoryStream.ToArray();
}
return result;
}
You cannot use the fileStream because the stream needs to be both readable and writable. If you first copy the data to a memorystream you can then compresses the image in that stream. Your code should be changed to this:
private byte[] ConvertImageToByteArray(IFormFile image)
{
byte[] result = null;
// filestream
using (var fileStream = image.OpenReadStream())
// memory stream
using (var memoryStream = new MemoryStream())
{
fileStream.CopyTo(memoryStream);
memoryStream.Position = 0; // The position needs to be reset.
var before = memoryStream.Length;
ImageOptimizer optimizer = new ImageOptimizer();
optimizer.LosslessCompress(memoryStream);
var after = memoryStream.Length;
// convert to byte[]
result = memoryStream.ToArray();
}
return result;
}

Reading from a ZipArchiveEntry cause exception and MemoryLeak if using a MemoryStream

I have the following code that generate two kinds of errors. First with the current code I get an exception 'NotSupportedException: This stream from ZipArchiveEntry does not support reading.'. How am I supposed to read the data ?
Furthermore if i use a MemoryStream (as the commented code ) then I can read the data and deserialize correctly but the memorystream i created still remains in memory even if the dispose method has been called on it , causing some memory leaks . Any idea what is wrong with this code ?
void Main()
{
List<Product> products;
using (var s = GetDb().Result)
{
products = Utf8Json.JsonSerializer.Deserialize<List<Product>>(s).ToList();
}
}
// Define other methods and classes here
public static Task<Stream> GetDb()
{
var filepath = Path.Combine("c:/users/tom/Downloads", "productdb.zip");
using (var archive = ZipFile.OpenRead(filepath))
{
var data = archive.Entries.Single(e => e.FullName == "productdb.json");
return Task.FromResult(data.Open());
//using (var reader = new StreamReader(data.Open()))
//{
// var ms = new MemoryStream();
// data.Open().CopyTo(ms);
// ms.Seek(0, SeekOrigin.Begin);
// return Task.FromResult((Stream)ms);
//}
}
}
With the commented code you open the stream into a reader, don't use the reader, then open the stream again and copy over to the memory stream without closing the second opened stream.
It is the second opened stream that remains in memory, not the MemoryStream.
Refactor
public static async Task<Stream> GetDb() {
var filepath = Path.Combine("c:/users/tom/Downloads", "productdb.zip");
using (var archive = ZipFile.OpenRead(filepath)) {
var entry = archive.Entries.Single(e => e.FullName == "productdb.json");
using (var stream = entry.Open()) {
var ms = new MemoryStream();
await stream.CopyToAsync(ms);
return ms;
}
}
}

How to use SharpCompress' BZip2Stream to compress a string?

I am trying to compress a string (str) using SharpCompress' BZip2Stream but unable to achieve it. Following is the code I have so far,
public static string Compress(string str)
{
var data = Encoding.UTF8.GetBytes(str);
using (MemoryStream stream = new MemoryStream())
{
using (BZip2Stream zip = new BZip2Stream(stream, SharpCompress.Compressor.CompressionMode.Compress))
{
zip.Write(data, 0, data.Length);
var compressed = Encoding.UTF8.GetString(stream.ToArray());
return compressed;
}
}
}
No matter what string i pass to str it always returns BZh.
Any help is greatly appreciated!
I believe you need to finalize/close/flush the bzip2 stream in order to make sure all compressed data is written to the memory stream prior to reading data from the memory stream. Try:
using (MemoryMemoryStream stream = new MemoryStream())
{
using (BZip2Stream zip = new BZip2Stream(stream, SharpCompress.Compressor.CompressionMode.Compress))
{
zip.Write(data, 0, data.Length);
zip.Close();
}
var compressed = Encoding.UTF8.GetString(stream.ToArray());
return compressed;
}

Why does one method of string compression return an empty string but another one doesn't?

I'm using GZipStream to compress a string, and I've modified two different examples to see what works. The first code snippet, which is a heavily modified version of the example in the documentation, simply returns an empty string.
public static String CompressStringGzip(String uncompressed)
{
String compressedString;
// Convert the uncompressed source string to a stream stored in memory
// and create the MemoryStream that will hold the compressed string
using (MemoryStream inStream = new MemoryStream(Encoding.Unicode.GetBytes(uncompressed)),
outStream = new MemoryStream())
{
using (GZipStream compress = new GZipStream(outStream, CompressionMode.Compress))
{
inStream.CopyTo(compress);
StreamReader reader = new StreamReader(outStream);
compressedString = reader.ReadToEnd();
}
}
return compressedString;
and when I debug it, all I can tell is nothing is read from reader, which is compressedString is empty. However, the second method I wrote, modified from a CodeProject snippet is successful.
public static String CompressStringGzip3(String uncompressed)
{
//Transform string to byte array
String compressedString;
byte[] uncompressedByteArray = Encoding.Unicode.GetBytes(uncompressed);
using (MemoryStream outStream = new MemoryStream())
{
using (GZipStream compress = new GZipStream(outStream, CompressionMode.Compress))
{
compress.Write(uncompressedByteArray, 0, uncompressedByteArray.Length);
compress.Close();
}
byte[] compressedByteArray = outStream.ToArray();
StringBuilder compressedStringBuilder = new StringBuilder(compressedByteArray.Length);
foreach (byte b in compressedByteArray)
compressedStringBuilder.Append((char)b);
compressedString = compressedStringBuilder.ToString();
}
return compressedString;
}
Why is the first code snippet not successful while the other one is? Even though they're slightly different, I don't know why the minor changes in the second snippet allow it to work. The sample string I'm using is SELECT * FROM foods f WHERE f.name = 'chicken';
I ended up using the following code for compression and decompression:
public static String Compress(String decompressed)
{
byte[] data = Encoding.UTF8.GetBytes(decompressed);
using (var input = new MemoryStream(data))
using (var output = new MemoryStream())
{
using (var gzip = new GZipStream(output, CompressionMode.Compress, true))
{
input.CopyTo(gzip);
}
return Convert.ToBase64String(output.ToArray());
}
}
public static String Decompress(String compressed)
{
byte[] data = Convert.FromBase64String(compressed);
using (MemoryStream input = new MemoryStream(data))
using (GZipStream gzip = new GZipStream(input, CompressionMode.Decompress))
using (MemoryStream output = new MemoryStream())
{
gzip.CopyTo(output);
StringBuilder sb = new StringBuilder();
return Encoding.UTF8.GetString(output.ToArray());
}
}
The explanation for a part of the problem comes from this question. Although I fixed the problem by changing the code to what I included in this answer, these lines (in my original code):
foreach (byte b in compressedByteArray)
compressedStringBuilder.Append((char)b);
are problematic, because as dlev aptly phrased it:
You are interpreting each byte as its own character, when in fact that is not the case. Instead, you need the line:
string decoded = Encoding.Unicode.GetString(compressedByteArray);
The basic problem is that you are converting to a byte array based on an encoding, but then ignoring that encoding when you retrieve the bytes.
Therefore, the problem is solved, and the new code I'm using is much more succinct than my original code.
You need to move the code below outside the second using statement:
using (GZipStream compress = new GZipStream(outStream, CompressionMode.Compress))
{
inStream.CopyTo(compress);
outStream.Position = 0;
StreamReader reader = new StreamReader(outStream);
compressedString = reader.ReadToEnd();
}
CopyTo() is not flushing the results to the underlying MemoryStream.
Update
Seems that GZipStream closes and disposes it's underlying stream when it is disposed (not the way I would have designed the class). I've updated the sample above and tested it.

Converting a JPEG image to a byte array - COM exception

Using C#, I'm trying to load a JPEG file from disk and convert it to a byte array. So far, I have this code:
static void Main(string[] args)
{
System.Windows.Media.Imaging.BitmapFrame bitmapFrame;
using (var fs = new System.IO.FileStream(#"C:\Lenna.jpg", FileMode.Open))
{
bitmapFrame = BitmapFrame.Create(fs);
}
System.Windows.Media.Imaging.BitmapEncoder encoder =
new System.Windows.Media.Imaging.JpegBitmapEncoder();
encoder.Frames.Add(bitmapFrame);
byte[] myBytes;
using (var memoryStream = new System.IO.MemoryStream())
{
encoder.Save(memoryStream); // Line ARGH
// mission accomplished if myBytes is populated
myBytes = memoryStream.ToArray();
}
}
However, executing line ARGH gives me the message:
COMException was unhandled. The handle is invalid. (Exception from
HRESULT: 0x80070006 (E_HANDLE))
I don't think there is anything special about the file Lenna.jpg - I downloaded it from http://computervision.wikia.com/wiki/File:Lenna.jpg. Can you tell what is wrong with the above code?
Check the examples from this article: http://www.codeproject.com/KB/recipes/ImageConverter.aspx
Also it's better to use classes from System.Drawing
Image img = Image.FromFile(#"C:\Lenna.jpg");
byte[] arr;
using (MemoryStream ms = new MemoryStream())
{
img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
arr = ms.ToArray();
}
Other suggestion:
byte[] image = System.IO.File.ReadAllBytes ( Server.MapPath ( "noimage.png" ) );
Should be working not only with images.
public byte[] imageToByteArray(System.Drawing.Image imageIn)
{
MemoryStream ms = new MemoryStream();
imageIn.Save(ms,System.Drawing.Imaging.ImageFormat.Gif);
return ms.ToArray();
}
The reason this error happens is because the BitmapFrame.Create() method you are using defaults to an OnDemand load. The BitmapFrame doesn't try to read the stream it's associated with until the call to encoder.Save, by which point the stream is already disposed.
You could either wrap the entire function in the using {} block, or use an alternative BitmapFrame.Create(), such as:
BitmapFrame.Create(fs, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);

Categories

Resources