Newbie question on using ImageSharp #1807
-
I hope I'm in the right place to ask newbie questions (my first post), please redirect me to the right place if not. //============================================================
//=============================================================
Thanks in advance for any help |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 11 replies
-
I think you want from the code above to create a grey scale image where you have a source I believe this is just the So I think this can all end up as a one liner of var greyImg = Image.LoadPixelData<L8>(imageDataBytes, width, height); // don't forget to dispose of this when your finished. if you plan to then post process the image more and need a full color space you can then use var rgbImage = greyImg.CloneAs<Rgba32>(); // now both greyImage and rgbImage are completely separate and both need disposing which will create a new image expanded to the full |
Beta Was this translation helpful? Give feedback.
-
@jkowalski0058 since Why do you need to copy the pixels to a new array? How is the array being consume? Is it passed to some other library? (If yes: in form of
|
Beta Was this translation helpful? Give feedback.
-
I truly appreciate the support. I have legacy code currently maintained and built with VS2019 version 16.11.5, C# targeting .NET Framework 4.5. The images are captured by a monochrome camera C4410 20 MP CMOS Camera (imperx.com)<https://www.imperx.com/cmos-cameras/C4410/>. We take one image and search through the pixels locating objects that look like our target pattern. We take a second image to illuminate the fluorescence tags and then for each object location we evaluate the relative intensity to determine if the target is activated with fluorescence or not.
I have created a new project with .NET 5.0 VS2019 C# which I understand no longer supports the Microsoft imaging tools so I can't compile to load images to process the pixels as bytes. In my limited search I found your library which seems popular and am trying to get it to work and evaluate. I've attached one legacy file which does most of the image file loading and converting to byte arrays. Once I have a byte array in the same format as my legacy app, I can pass the byte array to lower-level functions that will process through the pixels, locate objects, and evaluate intensities.
My graphics expertise is very limited and am on a steep learning curve, appreciate any help.
From: Anton Firszov ***@***.***>
Sent: Thursday, November 4, 2021 6:45 AM
To: SixLabors/ImageSharp ***@***.***>
Cc: Jerry Kowalski - Applied BioCode ***@***.***>; Mention ***@***.***>
Subject: Re: [External] [SixLabors/ImageSharp] Newbie question on using ImageSharp (Discussion #1807)
@jkowalski0058<https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fjkowalski0058&data=04%7C01%7Cjkowalski%40apbiocode.com%7C32238962a8224a0411fe08d99f995601%7C5f35050700e043d7b4cca15d32b7e6b6%7C0%7C0%7C637716303199953188%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=xJ7Au5rbuNwfm9ogOQzZnMl2ylXvjqJ71awzb7tuGGM%3D&reserved=0> since TryGetSinglePixelSpan is about to go in ImageSharp 2.0 (#1739<https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2FSixLabors%2FImageSharp%2Fissues%2F1739&data=04%7C01%7Cjkowalski%40apbiocode.com%7C32238962a8224a0411fe08d99f995601%7C5f35050700e043d7b4cca15d32b7e6b6%7C0%7C0%7C637716303199963181%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=KQzAS%2B2X2KWqhSNSA3JVlv63VVBLVsN85iWxtQflZA4%3D&reserved=0>), I would like to understand your use case better, in order to design the best replacement API/s, so I have a few questions:
Why do you need to copy the pixels to a new array? How is the array consume? Can't you just instead do either of the following in your app:
* Pass around Span<L8> or Span<byte>
* Pass around the Image<L8> object, and process it row-by-row, when needed
-
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub<https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2FSixLabors%2FImageSharp%2Fdiscussions%2F1807%23discussioncomment-1588746&data=04%7C01%7Cjkowalski%40apbiocode.com%7C32238962a8224a0411fe08d99f995601%7C5f35050700e043d7b4cca15d32b7e6b6%7C0%7C0%7C637716303199963181%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=9fbIJe%2Fsg%2B8rYzYaqHCMw4tLH1HJWE4koNaZKdPAxbY%3D&reserved=0>, or unsubscribe<https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FASPIH642IFDKVEHPTGCEEX3UKKE6TANCNFSM5HIQ3DHQ&data=04%7C01%7Cjkowalski%40apbiocode.com%7C32238962a8224a0411fe08d99f995601%7C5f35050700e043d7b4cca15d32b7e6b6%7C0%7C0%7C637716303199973174%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=43PJRXDUyEGzrJp0Gtu6btet%2BzRqtcgJK%2BR5Sy%2BlS7Y%3D&reserved=0>.
Triage notifications on the go with GitHub Mobile for iOS<https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fapps.apple.com%2Fapp%2Fapple-store%2Fid1477376905%3Fct%3Dnotification-email%26mt%3D8%26pt%3D524675&data=04%7C01%7Cjkowalski%40apbiocode.com%7C32238962a8224a0411fe08d99f995601%7C5f35050700e043d7b4cca15d32b7e6b6%7C0%7C0%7C637716303199973174%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=qGUG960caPndQ6bWbB6T%2FEH3lBR4shvLvctLIhCMjJ0%3D&reserved=0> or Android<https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dcom.github.android%26referrer%3Dutm_campaign%253Dnotification-email%2526utm_medium%253Demail%2526utm_source%253Dgithub&data=04%7C01%7Cjkowalski%40apbiocode.com%7C32238962a8224a0411fe08d99f995601%7C5f35050700e043d7b4cca15d32b7e6b6%7C0%7C0%7C637716303199983168%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=Lc%2FJWgySFcvJHSLx4uqo55D4782OW1oFFuOV4m1dPh8%3D&reserved=0>.
///*********************************************************************************************************************
/// File:
/// ImageUtilities.cs
///
/// Purpose:
/// Provides methods for accessing application images.
///
///*********************************************************************************************************************
#define BitmapConverts8bppToArgbOnRead
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace ApplicationUtilities
{
///---------------------------------------------------------------------------------------------------------------------
/// <summary>
/// Provides static routines for accessing generic application images.
/// </summary>
///---------------------------------------------------------------------------------------------------------------------
public class ImageUtilities
{
#region Fields (private)
///-------------------------------------------------------------------------------------------------------------
/// Fields
///-------------------------------------------------------------------------------------------------------------
private static readonly ColorPalette GrayLut;
#endregion Fields (private)
#region Construction (Static)
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// Constructor (static)
/// </summary>
///-------------------------------------------------------------------------------------------------------------
static ImageUtilities()
{
// temporary bitmap
var bmp = new Bitmap( 64, 64, System.Drawing.Imaging.PixelFormat.Format8bppIndexed );
GrayLut = bmp.Palette;
for ( int i = 0; i < 256; i++ )
{
GrayLut.Entries[i] = System.Drawing.Color.FromArgb( 255, i, i, i );
}
}
#endregion Construction (Static)
#region Methods (8-bit)
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static void SaveDataAs8BitPng( string filename, byte[] imageDataBytes, System.Drawing.Size imageSize )
{
SaveDataAs8BitPng( filename, imageDataBytes, imageSize.Width, imageSize.Height );
}
public static void SaveDataAs8BitPng( string filename, byte[] imageDataBytes, int width, int height )
{
if ( File.Exists( filename ))
{
File.Delete( filename );
}
string tempFilename = filename + ".tmp";
Bitmap bitmap = Create8BitBitmap( imageDataBytes, width, height );
bitmap.Save( tempFilename, ImageFormat.Png );
File.Move( tempFilename, filename );
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static byte[] Convert12BitArrayTo8BitUsingMax( ushort[] imageDataShorts )
{
int count = imageDataShorts.Length;
byte[] imageDataBytes = new byte[ count ];
for ( int i = 0; i < count; i++ )
{
ushort shortValue = imageDataShorts[i];
imageDataBytes[i] = ( shortValue > byte.MaxValue ) ? byte.MaxValue : (byte) shortValue;
}
return imageDataBytes;
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static byte[] Convert12BitArrayTo8BitUsingShift( ushort[] imageDataShorts )
{
int nShorts = imageDataShorts.Length;
byte[] imageDataBytes = new byte[ nShorts ];
for ( int i = 0; i < nShorts; i++ )
{
imageDataBytes[ i ] = (byte) ( ( imageDataShorts[i] >> 4 ) & 0xff );
}
return imageDataBytes;
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static void SaveDataAsTxtFile( string filename, byte[] imageDataBytes, int width, int height )
{
if ( File.Exists( filename ))
{
File.Delete( filename );
}
using ( StreamWriter writer = new StreamWriter( filename )
)
{
writer.WriteLine( width.ToString() );
writer.WriteLine( height.ToString() );
for ( int i = 0; i < imageDataBytes.Length; i++ )
{
writer.WriteLine( imageDataBytes[i].ToString() );
}
}
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static byte[] GetDataBytes( Bitmap bitmap )
{
System.Drawing.Imaging.PixelFormat pixelFormat = System.Drawing.Imaging.PixelFormat.Format8bppIndexed;
int width = bitmap.Width;
int height = bitmap.Height;
int nBytes = width * height;
byte[] imageDataBytes = new byte[ nBytes];
var boundsRect = new Rectangle(0, 0, width, height );
BitmapData bmpData = bitmap.LockBits( boundsRect, ImageLockMode.ReadOnly, pixelFormat );
Marshal.Copy( (IntPtr) bmpData.Scan0, imageDataBytes, 0, nBytes ); // // bmpData.Stride * b.Height;
bitmap.UnlockBits(bmpData);
return imageDataBytes;
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static Bitmap CreateThumbnail(string filename, int width, int height)
{
try
{
Bitmap bitmap = new Bitmap(filename);
return CreateThumbnailFromBitmap( bitmap, width, height );
}
catch
{
return null;
}
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static Bitmap CreateThumbnailFromBitmap( Bitmap bitmap, int width, int height )
{
Bitmap bmpOut = null;
try
{
ImageFormat bitmapFormat = bitmap.RawFormat;
double ratio;
int newWidth = 0;
int newHeight = 0;
//*** If the image is smaller than a thumbnail just return it
if (bitmap.Width < width && bitmap.Height < height)
{
return bitmap;
}
// calc new height / width (keep scale)
if (bitmap.Width > bitmap.Height)
{
ratio = (double)width / bitmap.Width;
newWidth = width;
newHeight = (int) ( bitmap.Height * ratio );
}
else
{
ratio = (double) height / bitmap.Height;
newHeight = height;
newWidth = (int) ( bitmap.Width * ratio );
}
// create new bitmap
bmpOut = new Bitmap(newWidth, newHeight);
// get a graphics context to draw into
using ( Graphics g = Graphics.FromImage(bmpOut))
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
// Fill with white
g.FillRectangle( System.Drawing.Brushes.White, 0, 0, newWidth, newHeight );
// draw bitmpa into new graphics (interolation handles most of this)
g.DrawImage( bitmap, 0, 0, newWidth, newHeight );
}
// dispose of original bitmap
bitmap.Dispose();
}
catch
{
return null;
}
return bmpOut;
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static Bitmap Create8BitBitmap( byte[] imageDataBytes, int width, int height )
{
System.Drawing.Imaging.PixelFormat pixelFormat = System.Drawing.Imaging.PixelFormat.Format8bppIndexed;
var b = new Bitmap( width, height, pixelFormat );
b.Palette = GrayLut;
var boundsRect = new Rectangle( 0, 0, width, height );
BitmapData bmpData = b.LockBits( boundsRect, ImageLockMode.WriteOnly, pixelFormat );
Marshal.Copy( imageDataBytes, 0, (IntPtr) bmpData.Scan0, imageDataBytes.Length );
b.UnlockBits(bmpData);
return b;
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// converts 12-bit values to 8-bit by using max algorithm. I.E. anything > 255 is set to 255
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static BitmapImage Create8BitBitmapImageFromPacked12BitDataUsingMax( byte[] packedImageDataBytes, int width, int height )
{
ushort[] imageDataShorts = ImageUtilities.Unpack12BitImageDataTo16Bit( packedImageDataBytes );
byte[] imageDataBytes = ImageUtilities.Convert12BitArrayTo8BitUsingMax( imageDataShorts );
return ConvertBitmapToBitmapImage( Create8BitBitmap( imageDataBytes, width, height ));
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// Converts 12-bit values to 8-bit by using max algorithm. I.e. anything > 255 is set to 255
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static BitmapImage Create8BitBitmapImageFromUnpacked12BitDataUsingMax( ushort[] imageDataShorts, int width, int height )
{
byte[] imageDataBytes = ImageUtilities.Convert12BitArrayTo8BitUsingMax( imageDataShorts );
return ConvertBitmapToBitmapImage( Create8BitBitmap( imageDataBytes, width, height ));
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static BitmapImage Create8BitBitmapImageFrom8BitData( byte[] imageDataBytes, int width, int height )
{
return ConvertBitmapToBitmapImage( Create8BitBitmap( imageDataBytes, width, height ));
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static BitmapImage ConvertBitmapToBitmapImage( Bitmap bitmap )
{
BitmapImage bitmapImage = new BitmapImage();
using(MemoryStream memory = new MemoryStream())
{
bitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
}
return bitmapImage;
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static Bitmap Roi( Bitmap image, Rectangle rc )
{
var bmpdata = image.LockBits(rc, ImageLockMode.ReadOnly, image.PixelFormat);
Bitmap roi_bmp = new Bitmap(bmpdata.Width, bmpdata.Height, bmpdata.Stride, bmpdata.PixelFormat, bmpdata.Scan0);
image.UnlockBits(bmpdata);
if (roi_bmp.PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
{
roi_bmp.Palette = GrayLut;
}
return roi_bmp;
}
#endregion Methods (8-bit)
#region Methods (12-bit)
///---------------------------------------------------------------------------------------------------------
/// <summary>
/// Converts a 12-bit image in "packed" format (three bytes => two pixels) into an array of ushort data
/// </summary>
///---------------------------------------------------------------------------------------------------------
public static ushort[] Unpack12BitImageDataTo16Bit( byte[] data12 )
{
int nBytes = data12.Length;
int nShorts = ( nBytes * 2 + 2 ) / 3; // 2/3 times (i.e. for each 3 bytes of data we get 2 short
ushort[] data16 = new ushort[ nShorts ];
// Mono12Packed format: every three bytes contain two 12-bit pixels, organized as
// byte0 byte1 byte2
// 11..4 3..0|3..0 11..4
// px1 px1 px2 px2
unsafe
{
fixed( byte* src = data12 )
{
fixed( ushort* dest = data16 )
{
// need to use copy since the original is fixed
byte* srcx = src;
ushort* destx = dest;
for ( int i = 0; i < nBytes; i += 3 )
{
byte b0 = *srcx++;
byte b1 = *srcx++;
byte b2 = *srcx++;
*destx++ = (ushort)( ( b0 << 4) | ( b1 & 0x0f ) );
*destx++ = (ushort)( ( b2 << 4) | ( b1 >> 4 ) );
}
}
}
}
return data16;
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static byte[] Unpack12BitImageDataTo8Bit( byte[] data12 )
{
int nBytes12 = data12.Length;
int nBytes8 = ( nBytes12 * 2 + 2 ) / 3; // 2/3 times (i.e. for each 3 bytes of data we get 2 bytes
byte[] data8 = new byte[ nBytes8 ];
// Mono12Packed format: every three bytes contain two 12-bit pixels, organized as
// byte0 byte1 byte2
// 11..4 3..0|3..0 11..4
// px1 px1 px2 px2
unsafe
{
fixed( byte* src = data12 )
{
fixed( byte* dest = data8 )
{
// need to use copy since the original is fixed
byte* srcx = src;
byte* destx = dest;
for ( int i = 0; i < nBytes12; i += 3 )
{
byte b0 = *srcx++;
byte b1 = *srcx++;
byte b2 = *srcx++;
*destx++ = b0;
*destx++ = b2;
}
}
}
}
return data8;
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static byte[] Pack16BitImageDataInto12Bit(ushort[] data16)
{
int nShorts = data16.Length;
int nBytes = (nShorts * 3 + 1) / 2; // add 2 just to make sure we have enough
byte[] data12 = new byte[nBytes];
// Mono12Packed format: every three bytes contain two 12-bit pixels, organized as
// byte0 byte1 byte2
// 11..4 3..0|3..0 11..4
// px1 px1 px2 px2
unsafe
{
fixed (ushort* src = data16)
{
fixed (byte* dest = data12)
{
// need to use copy since the original is fixed
ushort* srcx = src;
byte* destx = dest;
for (int i = 0; i < nShorts; i += 2)
{
ushort s0 = *srcx++;
ushort s1 = *srcx++;
*destx++ = (byte)(s0 >> 4);
*destx++ = (byte)(((s1 & 0x0F) << 4) | (s0 & 0x0F));
*destx++ = (byte)(s1 >> 4);
}
}
}
}
return data12;
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static byte[] Pack8BitImageDataInto12Bit( byte[] data8 )
{
int nBytesSrc = data8.Length;
int nBytesDest = (nBytesSrc * 3 + 1) / 2; // add 2 just to make sure we have enough
byte[] data12 = new byte[nBytesDest];
// Mono12Packed format: every three bytes contain two 12-bit pixels, organized as
// byte0 byte1 byte2
// 11..4 3..0|3..0 11..4
// px1 px1 px2 px2
unsafe
{
fixed ( byte* src = data8)
{
fixed (byte* dest = data12)
{
// need to use copy since the original is fixed
byte* srcx = src;
byte* destx = dest;
for (int i = 0; i < nBytesSrc; i += 2)
{
byte s0 = *srcx++;
byte s1 = *srcx++;
*destx++ = s0;
*destx++ = 0;
*destx++ = s1;
}
}
}
}
return data12;
}
#endregion Methods (12-bit)
#region Methods (16-bit)
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static ushort[] GetDataShorts( Bitmap bitmap )
{
System.Drawing.Imaging.PixelFormat pixelFormat = System.Drawing.Imaging.PixelFormat.Format16bppGrayScale;
int width = bitmap.Width;
int height = bitmap.Height;
int nShorts = width * height;
Rectangle rect = new Rectangle( 0, 0, width, height );
ushort[] imageDataShorts = new ushort[ nShorts ];
BitmapData bmpData = bitmap.LockBits( rect, ImageLockMode.ReadOnly, pixelFormat );
Marshal.Copy( (IntPtr)bmpData.Scan0, (short[])(Array)imageDataShorts, 0, nShorts ); // could multiply by 2
bitmap.UnlockBits(bmpData);
return imageDataShorts;
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static void Histogram( string filename, ushort[] array, ushort maxValue )
{
ushort[] counts = new ushort[ maxValue+1 ];
for ( int i = 0; i < counts.Length; i++ )
{
counts[i] = 0;
}
for ( int i = 0; i < array.Length; i++ )
{
ushort value = array[i];
if ( value > maxValue )
{
value = (ushort) maxValue;
}
counts[ value ]++;
}
using ( StreamWriter writer = new StreamWriter( filename ) )
{
for ( int i = 0; i <= maxValue; i++ )
{
if ( counts[i] > 0 )
{
writer.WriteLine( string.Format( "{0},{1}", i, counts[i] ) );
}
}
}
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static void Histogram( string filename, byte[] array )
{
int maxValue = 255;
ushort[] counts = new ushort[ maxValue+1 ];
for ( int i = 0; i < counts.Length; i++ )
{
counts[i] = 0;
}
for ( int i = 0; i < array.Length; i++ )
{
ushort value = array[i];
counts[ value ]++;
}
using ( StreamWriter writer = new StreamWriter( filename ) )
{
for ( int i = 0; i <= maxValue; i++ )
{
if ( counts[i] > 0 )
{
writer.WriteLine( string.Format( "{0},{1}", i, counts[i] ) );
}
}
}
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static Bitmap Create16BitBitmap( ushort[] data, int width, int height )
{
System.Drawing.Imaging.PixelFormat pixelFormat = System.Drawing.Imaging.PixelFormat.Format16bppGrayScale;
var b = new Bitmap( width, height, pixelFormat );
var boundsRect = new Rectangle(0, 0, width, height );
BitmapData bmpData = b.LockBits( boundsRect, ImageLockMode.WriteOnly, pixelFormat );
Marshal.Copy( (short[])(Array)data, 0, (IntPtr) bmpData.Scan0, data.Length );
b.UnlockBits( bmpData );
return b;
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static void SaveDataAs16BitPng( string filename, ushort[] imageDataShorts, System.Drawing.Size imageSize )
{
SaveDataAs16BitPng( filename, imageDataShorts, imageSize.Width, imageSize.Height );
}
public static void SaveDataAs16BitPng( string filename, ushort[] imageDataShorts, int width, int height )
{
if ( File.Exists( filename ))
{
File.Delete( filename );
}
string tempFilename = filename + ".tmp";
var bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Gray16, null);
bitmap.WritePixels( new System.Windows.Int32Rect( 0, 0, width, height ), imageDataShorts, width * 2, 0 );
var encoder = new PngBitmapEncoder();
encoder.Frames.Add( BitmapFrame.Create(bitmap ) );
using (var stream = System.IO.File.Create( tempFilename ))
{
encoder.Save(stream);
}
File.Move( tempFilename, filename );
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static void SaveDataAsTxtFile( string filename, ushort[] imageDataShorts, int width, int height )
{
if ( File.Exists( filename ))
{
File.Delete( filename );
}
using ( StreamWriter writer = new StreamWriter( filename ))
{
writer.WriteLine( width.ToString() );
writer.WriteLine( height.ToString() );
for ( int i = 0; i < imageDataShorts.Length; i++ )
{
writer.WriteLine( imageDataShorts[i].ToString() );
}
}
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static Bitmap LoadPng(string filename)
{
try
{
using( Stream strm = new FileStream( filename, FileMode.Open, FileAccess.Read, FileShare.Read ) )
{
var decoder = new PngBitmapDecoder(strm, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
var frame = decoder.Frames[0];
// Parameters for Bitmap creatioin & construct
System.Drawing.Imaging.PixelFormat format = System.Drawing.Imaging.PixelFormat.Undefined;
int bpp = 0; // bytes per pixel
// Convert the Windows.Media.PixelFormat into one that works with bitmap creation
//## This is imperfect. For one thing, the WM pixelformat supports many more formats than System.Drawing.Bitmap.
//## In particular, image files might be BGR or CMY instead of RGB
int nchannels = frame.Format.Masks.Count;
switch ( frame.Format.BitsPerPixel )
{
case 8:
{
bpp = nchannels;
if ( nchannels == 1 )
{
format = System.Drawing.Imaging.PixelFormat.Format8bppIndexed;
}
else
if ( nchannels == 3 )
{
format = System.Drawing.Imaging.PixelFormat.Format24bppRgb;
}
else
if ( nchannels == 4 )
{
format = System.Drawing.Imaging.PixelFormat.Format32bppArgb;
}
break;
}
case 16:
{
bpp = 2 * nchannels;
if ( nchannels == 1 )
{
format = System.Drawing.Imaging.PixelFormat.Format16bppGrayScale;
}
else
if ( nchannels == 3 )
{
format = System.Drawing.Imaging.PixelFormat.Format48bppRgb;
}
else
if ( nchannels== 4 )
{
format = System.Drawing.Imaging.PixelFormat.Format64bppArgb;
}
break;
}
default:
{
break;
}
}
if ( format == System.Drawing.Imaging.PixelFormat.Undefined )
{
throw new ArgumentException( "ImageUtilities.LoadPng(): Unrecognized pixel format" );
}
// Create an actual Bitmap with the proscribed format and dimensions
Bitmap bmp = new Bitmap( frame.PixelWidth, frame.PixelHeight, format );
if ( format == System.Drawing.Imaging.PixelFormat.Format8bppIndexed )
{
bmp.Palette = GrayLut;
}
// Lock it
BitmapData bmpdata = bmp.LockBits( new Rectangle( System.Drawing.Point.Empty, bmp.Size ), ImageLockMode.WriteOnly, format );
// Copy the frame's data into that space
frame.CopyPixels( Int32Rect.Empty, bmpdata.Scan0, bmpdata.Stride * bmpdata.Height * bpp, bmpdata.Stride );
// release
bmp.UnlockBits(bmpdata);
// return it
return bmp;
}
}
catch
{
}
return null;
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public static BitmapImage LoadBitmapImage( string filename, int decodePixelWidth )
{
try
{
BitmapImage bitmapImage = new BitmapImage();
// BitmapImage.UriSource must be in a BeginInit/EndInit block
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri( filename );
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
// To save significant application memory, set the DecodePixelWidth or
// DecodePixelHeight of the BitmapImage value of the image source to the desired
// height or width of the rendered image. If you don't do this, the application will
// cache the image as though it were rendered as its normal size rather then just
// the size that is displayed.
// Note: In order to preserve aspect ratio, set DecodePixelWidth
// or DecodePixelHeight but not both.
bitmapImage.DecodePixelWidth = decodePixelWidth;
bitmapImage.EndInit();
return bitmapImage;
}
catch
{
return null;
}
}
#endregion Methods (16-bit)
#region Methods (Image Quality)
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
/// <remarks>
/// These routines are the same (except one routine inputs an array of bytes and the other
/// uses an array of ushorts)
/// <remarks>
///-------------------------------------------------------------------------------------------------------------
public static double CalculateImageQuality( byte[] pixels, System.Drawing.Size imageSize, out double averagePixel )
{
return CalculateImageQuality( pixels, imageSize.Width, imageSize.Height, out averagePixel );
}
public static double CalculateImageQuality( byte[] pixels, int width, int height, out double averagePixel )
{
long sumOfDiffsSquared = 0;
long sum = 0;
int count = 0;
for ( int row = 0; row < height-1; row++ )
{
for (int col = 0; col < width - 1; col ++ )
{
count++;
int i = row * width + col;
int pixel1 = pixels[i]; // pixel at [row,col]
int pixel2 = pixels[i+1]; // pixel at [row,col+1] (to the right)
int pixel3 = pixels[i+width]; // pixel at [row+1,col] (below)
int diff1 = pixel1 - pixel2;
int diff2 = pixel1 - pixel3;
sum += pixel1;
sumOfDiffsSquared += diff1 * diff1 + diff2 * diff2;
}
}
averagePixel = (double) sum / count;
// return average diff
return (double)sumOfDiffsSquared / count;
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
/// <remarks>
/// These routines are the same (except one routine inputs an array of bytes and the other uses
/// an array of ushorts)
/// <remarks>
///-------------------------------------------------------------------------------------------------------------
public static double CalculateImageQuality( ushort[] pixels, System.Drawing.Size imageSize, out double averagePixel )
{
return CalculateImageQuality( pixels, imageSize.Width, imageSize.Height, out averagePixel );
}
public static double CalculateImageQuality( ushort[] pixels, int width, int height, out double averagePixel )
{
long sumOfDiffsSquared = 0;
long sum = 0;
int count = 0;
for ( int row = 0; row < height-1; row++ )
{
for (int col = 0; col < width - 1; col ++ )
{
count++;
int i = row * width + col;
int pixel1 = pixels[i]; // pixel at [row,col]
int pixel2 = pixels[i + 1]; // pixel at [row,col+1] (to the right)
int pixel3 = pixels[i + width]; // pixel at [row+1,col] (below)
int diff1 = pixel1 - pixel2;
int diff2 = pixel1 - pixel3;
sum += pixel1;
sumOfDiffsSquared += diff1 * diff1 + diff2 * diff2;
}
}
averagePixel = (double) sum / count;
// return average diff
return (double)sumOfDiffsSquared / count;
}
#endregion Methods (Image Quality)
#region Methods (Test)
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public void Test8()
{
int width = 256;
int height = 256;
var imageDataBytes = new byte[width * height];
int x = 0;
for ( uint i = 0; i < height; i++ )
{
for ( uint j = 0; j < width; j++ )
{
imageDataBytes[ x++ ] = (byte) j;
}
}
string filename = "Test8.png";
SaveDataAs8BitPng( filename, imageDataBytes, width, height );
Bitmap bitmap2 = LoadPng( filename );
if ( bitmap2 == null )
{
return;
}
if (bitmap2.PixelFormat != System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
{
throw new InvalidOperationException("ImageUtilities.Test8 FAILED: loaded file has wrong pixel format");
}
byte[] imageDataBytes2 = GetDataBytes( bitmap2 );
Trace.WriteLine( string.Format( "orig length = {0}, new length = {1}", imageDataBytes.Length, imageDataBytes2.Length ));
if ( imageDataBytes.Length == imageDataBytes2.Length )
{
int count = 0;
for ( int i = 0; i < imageDataBytes.Length; i++ )
{
if ( imageDataBytes[i] != imageDataBytes2[i] )
{
count++;
}
}
Trace.WriteLine( string.Format( "number of different data bytes = {0}", count ));
}
}
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// ...
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public void Test16()
{
int width = 256;
int height = 256;
var pixels = new ushort[width * height];
for ( var y = 0; y < height; ++y )
{
for (var x = 0; x < width; ++x)
{
var v = (0x10000*2 * x/width + 0x10000 * 3 * y / height);
var isMirror = (v / 0x10000) % 2 == 1;
v = v % 0xFFFF;
if (isMirror)
{
v = 0xFFFF - v;
}
pixels[y * width + x] = (ushort)v;
}
}
SaveDataAs16BitPng( "gray16.png", pixels, width, height );
Bitmap bitmap2 = LoadPng("gray16.png");
if ( bitmap2 == null )
{
return;
}
ushort[] pixels2 = GetDataShorts( bitmap2 );
if ( pixels.Length != pixels2.Length )
{
Trace.WriteLine( "Lengths do not match" );
}
for ( int i = 0; i < pixels.Length; i++ )
{
if ( pixels[i] != pixels2[i] )
{
Trace.WriteLine( string.Format( "Pixel {0} does not match: {1} {2}", i, pixels[i], pixels2[i] ));
}
}
}
#endregion Methods (Test)
}
}
|
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
sorry my bad, I assumed the previous code example I copied from was 100% correct rather than have a typo in it. If you use intellisense to look at what type imageDataBytes = MemoryMarshal.AsBytes(pixelSpan()).ToArray(); should actually be imageDataBytes = MemoryMarshal.AsBytes(pixelSpan).ToArray(); // no brackets after pixelSpan its a type not |
Beta Was this translation helpful? Give feedback.
I think you want from the code above to create a grey scale image where you have a source
byte[]
made up of pixel data where each byte represents a single grey scale pixel.I believe this is just the
L8
pixel format (no need for palette stuff)So I think this can all end up as a one liner of
if you plan to then post process the image more and need a full color space you can then use
which will create a new image expanded to the full
Rgba32
col…