Finding specific pixel colors of a BitmapImage - wpf

I have a WPF BitmapImage which I loaded from a .JPG file, as follows:
this.m_image1.Source = new BitmapImage(new Uri(path));
I want to query as to what the colour is at specific points. For example, what is the RGB value at pixel (65,32)?
How do I go about this? I was taking this approach:
ImageSource ims = m_image1.Source;
BitmapImage bitmapImage = (BitmapImage)ims;
int height = bitmapImage.PixelHeight;
int width = bitmapImage.PixelWidth;
int nStride = (bitmapImage.PixelWidth * bitmapImage.Format.BitsPerPixel + 7) / 8;
byte[] pixelByteArray = new byte[bitmapImage.PixelHeight * nStride];
bitmapImage.CopyPixels(pixelByteArray, nStride, 0);
Though I will confess there's a bit of monkey-see, monkey do going on with this code.
Anyway, is there a straightforward way to process this array of bytes to convert to RGB values?

Here is how I would manipulate pixels in C# using multidimensional arrays:
[StructLayout(LayoutKind.Sequential)]
public struct PixelColor
{
public byte Blue;
public byte Green;
public byte Red;
public byte Alpha;
}
public PixelColor[,] GetPixels(BitmapSource source)
{
if(source.Format!=PixelFormats.Bgra32)
source = new FormatConvertedBitmap(source, PixelFormats.Bgra32, null, 0);
int width = source.PixelWidth;
int height = source.PixelHeight;
PixelColor[,] result = new PixelColor[width, height];
source.CopyPixels(result, width * 4, 0);
return result;
}
usage:
var pixels = GetPixels(image);
if(pixels[7, 3].Red > 4)
{
...
}
If you want to update pixels, very similar code works except you will create a WriteableBitmap, and use this:
public void PutPixels(WriteableBitmap bitmap, PixelColor[,] pixels, int x, int y)
{
int width = pixels.GetLength(0);
int height = pixels.GetLength(1);
bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, width*4, x, y);
}
thusly:
var pixels = new PixelColor[4, 3];
pixels[2,2] = new PixelColor { Red=128, Blue=0, Green=255, Alpha=255 };
PutPixels(bitmap, pixels, 7, 7);
Note that this code converts bitmaps to Bgra32 if they arrive in a different format. This is generally fast, but in some cases may be a performance bottleneck, in which case this technique would be modified to match the underlying input format more closely.
Update
Since BitmapSource.CopyPixels doesn't accept a two-dimensional array it is necessary to convert the array between one-dimensional and two-dimensional. The following extension method should do the trick:
public static class BitmapSourceHelper
{
#if UNSAFE
public unsafe static void CopyPixels(this BitmapSource source, PixelColor[,] pixels, int stride, int offset)
{
fixed(PixelColor* buffer = &pixels[0, 0])
source.CopyPixels(
new Int32Rect(0, 0, source.PixelWidth, source.PixelHeight),
(IntPtr)(buffer + offset),
pixels.GetLength(0) * pixels.GetLength(1) * sizeof(PixelColor),
stride);
}
#else
public static void CopyPixels(this BitmapSource source, PixelColor[,] pixels, int stride, int offset)
{
var height = source.PixelHeight;
var width = source.PixelWidth;
var pixelBytes = new byte[height * width * 4];
source.CopyPixels(pixelBytes, stride, 0);
int y0 = offset / width;
int x0 = offset - width * y0;
for(int y=0; y<height; y++)
for(int x=0; x<width; x++)
pixels[x+x0, y+y0] = new PixelColor
{
Blue = pixelBytes[(y*width + x) * 4 + 0],
Green = pixelBytes[(y*width + x) * 4 + 1],
Red = pixelBytes[(y*width + x) * 4 + 2],
Alpha = pixelBytes[(y*width + x) * 4 + 3],
};
}
#endif
}
There are two implementations here: The first one is fast but uses unsafe code to get an IntPtr to an array (must compile with /unsafe option). The second one is slower but does not require unsafe code. I use the unsafe version in my code.
WritePixels accepts two-dimensional arrays, so no extension method is required.
Edit: As Jerry pointed out in the comments, because of the memory layout, the two-dimensional array has the vertical coordinate first, in other words it must be dimensioned as Pixels[Height,Width] not Pixels[Width,Height] and addressed as Pixels[y,x].

I'd like to add to Ray´s answer that you can also declare PixelColor struct as a union:
[StructLayout(LayoutKind.Explicit)]
public struct PixelColor
{
// 32 bit BGRA
[FieldOffset(0)] public UInt32 ColorBGRA;
// 8 bit components
[FieldOffset(0)] public byte Blue;
[FieldOffset(1)] public byte Green;
[FieldOffset(2)] public byte Red;
[FieldOffset(3)] public byte Alpha;
}
And that way you'll also have access to the UInit32 BGRA (for fast pixel access or copy), besides the individual byte components.

The interpretation of the resulting byte array is dependent upon the pixel format of the source bitmap, but in the simplest case of a 32 bit, ARGB image, each pixel will be composed of four bytes in the byte array. The first pixel would be interpreted thusly:
alpha = pixelByteArray[0];
red = pixelByteArray[1];
green = pixelByteArray[2];
blue = pixelByteArray[3];
To process each pixel in the image, you would probably want to create nested loops to walk the rows and the columns, incrementing an index variable by the number of bytes in each pixel.
Some bitmap types combine multiple pixels into a single byte. For instance, a monochrome image packs eight pixels into each byte. If you need to deal with images other than 24/32 bit per pixels (the simple ones), then I would suggest finding a good book that covers the underlying binary structure of bitmaps.

I'd like to improve upon Ray's answer - not enough rep to comment. >:( This version has the best of both safe/managed, and the efficiency of the unsafe version. Also, I've done away with passing in the stride as the .Net documentation for CopyPixels says it's the stride of the bitmap, not of the buffer. It's misleading, and can be computed inside the function anyway. Since the PixelColor array must be the same stride as the bitmap (to be able to do it as a single copy call), it makes sense to just make a new array in the function as well. Easy as pie.
public static PixelColor[,] CopyPixels(this BitmapSource source)
{
if (source.Format != PixelFormats.Bgra32)
source = new FormatConvertedBitmap(source, PixelFormats.Bgra32, null, 0);
PixelColor[,] pixels = new PixelColor[source.PixelWidth, source.PixelHeight];
int stride = source.PixelWidth * ((source.Format.BitsPerPixel + 7) / 8);
GCHandle pinnedPixels = GCHandle.Alloc(pixels, GCHandleType.Pinned);
source.CopyPixels(
new Int32Rect(0, 0, source.PixelWidth, source.PixelHeight),
pinnedPixels.AddrOfPinnedObject(),
pixels.GetLength(0) * pixels.GetLength(1) * 4,
stride);
pinnedPixels.Free();
return pixels;
}

I took all examples and created a slightly better one - tested it too
(the only flaw was that magic 96 as DPI which really bugged me)
I also compared this WPF tactic versus:
GDI by using Graphics (system.drawing)
Interop by directly invoking GetPixel from GDI32.Dll
To my supprise,
This works x10 faster than GDI, and around x15 times faster then Interop.
So if you're using WPF - much better to work with this to get your pixel color.
public static class GraphicsHelpers
{
public static readonly float DpiX;
public static readonly float DpiY;
static GraphicsHelpers()
{
using (var g = Graphics.FromHwnd(IntPtr.Zero))
{
DpiX = g.DpiX;
DpiY = g.DpiY;
}
}
public static Color WpfGetPixel(double x, double y, FrameworkElement AssociatedObject)
{
var renderTargetBitmap = new RenderTargetBitmap(
(int)AssociatedObject.ActualWidth,
(int)AssociatedObject.ActualHeight,
DpiX, DpiY, PixelFormats.Default);
renderTargetBitmap.Render(AssociatedObject);
if (x <= renderTargetBitmap.PixelWidth && y <= renderTargetBitmap.PixelHeight)
{
var croppedBitmap = new CroppedBitmap(
renderTargetBitmap, new Int32Rect((int)x, (int)y, 1, 1));
var pixels = new byte[4];
croppedBitmap.CopyPixels(pixels, 4, 0);
return Color.FromArgb(pixels[3], pixels[2], pixels[1], pixels[0]);
}
return Colors.Transparent;
}
}

A little remark:
If you are trying to use this code (Edit: provided by Ray Burns), but get the error about the array's rank, try to edit the extension methods as follows:
public static void CopyPixels(this BitmapSource source, PixelColor[,] pixels, int stride, int offset, bool dummy)
and then call the CopyPixels method like this:
source.CopyPixels(result, width * 4, 0, false);
The problem is, that when the extension method doesn't differ from the original, the original one is called. I guess this is because PixelColor[,] matches Array as well.
I hope this helps you if you got the same problem.

If you want just one Pixel color:
using System.Windows.Media;
using System.Windows.Media.Imaging;
...
public static Color GetPixelColor(BitmapSource source, int x, int y)
{
Color c = Colors.White;
if (source != null)
{
try
{
CroppedBitmap cb = new CroppedBitmap(source, new Int32Rect(x, y, 1, 1));
var pixels = new byte[4];
cb.CopyPixels(pixels, 4, 0);
c = Color.FromRgb(pixels[2], pixels[1], pixels[0]);
}
catch (Exception) { }
}
return c;
}

Much simpler. There's no need to copy the data around, you can get it directly. But this comes at a price: pointers and unsafe. In a specific situation, decide whether it's worth the speed and ease for you (but you can simply put the image manipulation into its own separate unsafe class and the rest of the program won't be affected).
var bitmap = new WriteableBitmap(image);
data = (Pixel*)bitmap.BackBuffer;
stride = bitmap.BackBufferStride / 4;
bitmap.Lock();
// getting a pixel value
Pixel pixel = (*(data + y * stride + x));
bitmap.Unlock();
where
[StructLayout(LayoutKind.Explicit)]
protected struct Pixel {
[FieldOffset(0)]
public byte B;
[FieldOffset(1)]
public byte G;
[FieldOffset(2)]
public byte R;
[FieldOffset(3)]
public byte A;
}
The error checking (whether the format is indeed BGRA and handling the case if not) will be left to the reader.

You can get color components in a byte array. First copy the pixels in 32 bit to an array and convert that to 8-bit array with 4 times larger size
int[] pixelArray = new int[stride * source.PixelHeight];
source.CopyPixels(pixelArray, stride, 0);
// byte[] colorArray = new byte[pixelArray.Length];
// EDIT:
byte[] colorArray = new byte[pixelArray.Length * 4];
for (int i = 0; i < colorArray.Length; i += 4)
{
int pixel = pixelArray[i / 4];
colorArray[i] = (byte)(pixel >> 24); // alpha
colorArray[i + 1] = (byte)(pixel >> 16); // red
colorArray[i + 2] = (byte)(pixel >> 8); // green
colorArray[i + 3] = (byte)(pixel); // blue
}
// colorArray is an array of length 4 times more than the actual number of pixels
// in the order of [(ALPHA, RED, GREEN, BLUE), (ALPHA, RED...]

Related

GlyphRun and the exact position of the cursor (WPF)

I would like to draw a text exactly in the place of the mouse cursor.
Because I need very high performance, I would like to use GlyphRun.
Everything works almost well, but unfortunately my text is slightly below the cursor.
Can someone help me modify this method to eliminate this vertical shift?
Now it looks like this
My expectation (the text touches the cursor)
My code:
void MyDrawer_MouseMove(object sender, MouseEventArgs e)
{
Test1();
}
void Test1()
{
MyDrawer.DeleteVisual(Dv);
MyDrawer.Cursor = Cursors.Cross;
string text = "Hello Word";
double size = 40;
Dv = new DrawingVisual();
using (var dc = Dv.RenderOpen())
{
Typeface typeface = new Typeface("Arial");
if (typeface.TryGetGlyphTypeface(out GlyphTypeface glyphTypeface))
{
ushort[] glyphIndexes = new ushort[text.Length];
double[] advanceWidths = new double[text.Length];
for (int i = 0; i < text.Length; i++)
{
ushort glyphIndex = glyphTypeface.CharacterToGlyphMap[text[i]];
glyphIndexes[i] = glyphIndex;
double width = glyphTypeface.AdvanceWidths[glyphIndex] * size;
advanceWidths[i] = width;
}
Point origin = Mouse.GetPosition(MyDrawer);
//Move text belowe the cursor
origin = new Point { X = origin.X, Y = origin.Y + (glyphTypeface.Baseline * size) };
GlyphRun glyphRun = new GlyphRun(glyphTypeface, 0, false, size,
glyphIndexes, origin, advanceWidths, null, null, null, null,
null, null);
dc.DrawGlyphRun(Brushes.Red, glyphRun);
MyDrawer.AddVisual(Dv);
}
}
}
Of course, this is only a test, in practice it will not affect the cursor, but the indicated point and text will be much more than in this example.
I bumped into this question in search for the same issue.
Glyph content (GlyphRun for that matter) is wrapped in a black-box.
The black-box contain a bit of padding on the top.
Like so : Rought skecth of the glyph run
// To get the box size
var blackBoxHeight = glyphTypeface.Height * fontSize;
// To get the actual character height
var characterHeight = glyphTypeface.Baseline * fontSize;
// To get the padding inside the black box
var blackBoxPadding = blackBoxHeight - characterHeight;
// set the point to draw a little bit up (skip the padding)
origin.Y -= blackBoxPadding;
// Create the glyph
var run = new GlyphRun
(
glyphTypeface: glyphTypeface,
bidiLevel: 0,
isSideways: false,
renderingEmSize: fontSize,
pixelsPerDip: 1.0f,
glyphIndices: indices,
baselineOrigin: origin, /* The point containing the padding offset */
advanceWidths: widths,
glyphOffsets: null,
characters: null,
deviceFontName: null,
clusterMap: null,
caretStops: null,
language: null
);
The other answer lacks of some details.
There is actually no magic or unreasoned paddings. The picture shows relations of glyphTypeface height parameters to the displayed GlyphRun.
The code that calculates corresponding line vertical positions based on the GlyphRun origin Y is shown below:
var baseLine = origin.Y;
var boxTop = baseLine - glyphTypeface.Baseline * FontSize;
var boxBottom = boxTop + glyphTypeface.Height * FontSize;
var capsHeight = baseLine - glyphTypeface.CapsHeight * FontSize;
var lowHeight = baseLine - glyphTypeface.XHeight * FontSize;

Precisely locating glyph text in WPF

I am writing a chemical molecule editor for Windows. As it has to be used in a Word Add-In I am restricted to using WPF for rendering structures. This is working quite well, apart from one tiny niggling point.
I use GlyphRuns for rendering atom labels and they are always displaced slightly to the right. If you look on the screenshot you can see there is a leading whitespace, especially with the H2N, and Hg atom labels. Why? The white background is what you get when you get the outline geometry of the glyph run.
The GlyphRun class is so badly documented that I cannot see which of the properties to amend to precisely locate the text where I want it. So any suggestions to try would be welcome.
UPDATE: I've been asked to provide a sample. The code is complex, but not gratuitously so, so I'm cutting it down to focus on the essentials:
public void MeasureAtCenter(Point center)
{
GlyphInfo = GlyphUtils.GetGlyphsAndInfo(Text, PixelsPerDip, out GlyphRun groupGlyphRun, center, _glyphTypeface, TypeSize);
//compensate the main offset vector for any descenders
Vector mainOffset = GlyphUtils.GetOffsetVector(groupGlyphRun, AtomShape.SymbolSize) + new Vector(0.0, -MaxBaselineOffset) + new Vector(-FirstBearing(groupGlyphRun), 0.0);
TextRun = groupGlyphRun;
TextMetrics = new AtomTextMetrics
{
BoundingBox = groupGlyphRun.GetBoundingBox(center + mainOffset),
Geocenter = center,
TotalBoundingBox = groupGlyphRun.GetBoundingBox(center + mainOffset),
OffsetVector = mainOffset
};
}
public static GlyphInfo GetGlyphs(string symbolText, GlyphTypeface glyphTypeFace, double size)
{
ushort[] glyphIndexes = new ushort[symbolText.Length];
double[] advanceWidths = new double[symbolText.Length];
double[] uprightBaselineOffsets = new double[symbolText.Length];
double totalWidth = 0;
for (int n = 0; n < symbolText.Length; n++)
{
ushort glyphIndex = glyphTypeFace.CharacterToGlyphMap[symbolText[n]];
glyphIndexes[n] = glyphIndex;
double width = glyphTypeFace.AdvanceWidths[glyphIndex] * size;
advanceWidths[n] = width;
double ubo = glyphTypeFace.DistancesFromHorizontalBaselineToBlackBoxBottom[glyphIndex] * size;
uprightBaselineOffsets[n] = ubo;
totalWidth += width;
}
return new GlyphInfo { AdvanceWidths = advanceWidths, Indexes = glyphIndexes, Width = totalWidth, UprightBaselineOffsets = uprightBaselineOffsets };
}
public static GlyphUtils.GlyphInfo GetGlyphsAndInfo(string symbolText, float pixelsPerDip, out GlyphRun hydrogenGlyphRun, Point point, GlyphTypeface glyphTypeFace, double symbolSize)
{
//measure the H atom first
var glyphInfo = GlyphUtils.GetGlyphs(symbolText, glyphTypeFace, symbolSize);
hydrogenGlyphRun = GlyphUtils.GetGlyphRun(glyphInfo, glyphTypeFace,
symbolSize, pixelsPerDip, point);
//work out exactly how much we should offset from the center to get to the bottom left
return glyphInfo;
}
public static Vector GetOffsetVector(GlyphRun glyphRun, double symbolSize)
{
Rect rect = glyphRun.ComputeInkBoundingBox();
//Vector offset = (rect.BottomLeft - rect.TopRight) / 2;
Vector offset = new Vector(-rect.Width / 2, glyphRun.GlyphTypeface.CapsHeight * symbolSize / 2);
return offset;
}
Indeed the GlyphRun class is a lot of work to use. I would suggest working with FormattedText objects instead. If there are performance issues, you can consider converting the FormattedText to Geometry once and reusing that. The MSDN docs provide a comparison of the different approaches.

How make a per-pixel output on WPF/XNA?

I want to draw like in the old qbasik, where you can into 5 lines and PSET (x, y) derive any graph, or Lissajous figures.
Question: what better way to go for WPF? and way for XNA?
Any samples?
For WPF and Silverlight
WriteableBitmap
http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap.aspx
WriteableBitmapEx library. Tries to compensate that with extensions methods that are easy to use like built in methods and offer GDI+ like functionality:
http://writeablebitmapex.codeplex.com/
In XNA this isn't the most efficient thing in general, but I think your best bet is to probably create a texture and set each pixel using SetData, and render it to the screen with SpriteBatch.
SpriteBatch spriteBatch;
Texture2D t;
Color[] blankScreen;
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
//initialize texture
t = new Texture2D(GraphicsDevice, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, false, SurfaceFormat.Color);
//clear screen initially
blankScreen = new Color[GraphicsDevice.Viewport.Width * GraphicsDevice.Viewport.Height];
for (int i = 0; i < blankScreen.Length; i++)
{
blankScreen[i] = Color.Black;
}
ClearScreen();
}
private void Set(int x, int y, Color c)
{
Color[] cArray = { c };
//unset texture from device
GraphicsDevice.Textures[0] = null;
t.SetData<Color>(0, new Rectangle(x, y, 1, 1), cArray, 0, 1);
//reset
GraphicsDevice.Textures[0] = t;
}
private void ClearScreen()
{
//unset texture from device
GraphicsDevice.Textures[0] = null;
t.SetData<Color>(blankScreen);
//reset
GraphicsDevice.Textures[0] = t;
}
protected override void Draw(GameTime gameTime)
{
spriteBatch.Begin();
spriteBatch.Draw(t, Vector2.Zero, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
With this you can call either Set or ClearScreen at will in your Update or Draw. You may have to play with the texture index (I just used 0 for this example, might not be it for you), and also you only need to unset / reset one time per frame, so you can optimize that depending on how you use them.

How would I map a camera image to create a live funhouse mirror using opencv?

Using Opencv and Linux I would like to create a fun-house mirror effect, short and squat, tall and thin effect using a live webcamera. My daughter loves those things and I would like to create one using a camera. I am not quite sure about the transforms necessary for these effects. Any help would be appreciated. I have much of the framework running, live video playing and such, just not the transforms.
thanx
I think that you need to use 'radial' transforms and 'pin cushion' which is inverse radial.
In order to braker the symmetry of the transforms you can strech the image before and after:
Suppose your image is 300x300
pixels.
Strech it to 300x600 or
600x300 using cvResize()
Apply transform: radial, pincushion or
sinusoidal
Strech back to 300x300
I never used radial or sinusoidal transforms in openCV so I dont have a piece of code to attach. But you can use cvUndistort2() and see if it is OK.
Create window with trackbars with range 0..100. Each trackbar controls parameter of distortion:
static IplImage* srcImage;
static IplImage* dstImage;
static double _camera[9];
static double _dist4Coeff[4]; // This is the transformation matrix
static int _r = 50; // Radial transform. 50 in range 0..100
static int _tX = 50; // Tangetial coef in X directio
static int _tY = 50; // Tangetial coef in Y directio
static int allRange = 50;
// Open windows
cvNamedWindow(winName, 1);
// Add track bars.
cvShowImage(winName, srcImage );
cvCreateTrackbar("Radial", winName, &_r , 2*allRange, callBackFun);
cvCreateTrackbar("Tang X", winName, &_tX , 2*allRange, callBackFun);
cvCreateTrackbar("Tang Y", winName, &_tY , 2*allRange, callBackFun);
callBackFun(0);
// The distortion call back
void callBackFun(int arg){
CvMat intrCamParamsMat = cvMat( 3, 3, CV_64F, _camera );
CvMat dist4Coeff = cvMat( 1, 4, CV_64F, _dist4Coeff );
// Build distortion coefficients matrix.
dist4Coeff.data.db[0] = (_r-allRange*1.0)/allRange*1.0;
dist4Coeff.data.db[1] = (_r-allRange*1.0)/allRange*1.0;
dist4Coeff.data.db[2] = (_tY-allRange*1.0)/allRange*1.0;
dist4Coeff.data.db[3] = (_tX-allRange*1.0)/allRange*1.0;
// Build intrinsic camera parameters matrix.
intrCamParamsMat.data.db[0] = 587.1769751432448200/2.0;
intrCamParamsMat.data.db[1] = 0.;
intrCamParamsMat.data.db[2] = 319.5000000000000000/2.0+0;
intrCamParamsMat.data.db[3] = 0.;
intrCamParamsMat.data.db[4] = 591.3189722549362800/2.0;
intrCamParamsMat.data.db[5] = 239.5000000000000000/2.0+0;
intrCamParamsMat.data.db[6] = 0.;
intrCamParamsMat.data.db[7] = 0.;
intrCamParamsMat.data.db[8] = 1.;
// Apply transformation
cvUndistort2( srcImage, dstImage, &intrCamParamsMat, &dist4Coeff );
cvShowImage( winName, dstImage );
}

Using FJCore to encode Silverlight WriteableBitmap

I am trying to find out how to use FJCore to encode a WriteableBitmap to a jpeg. I understand that WriteableBitmap provides the raw pixels but I am not sure how to convert it to the format that FJCore expects for its JpegEncoder method. JpegEncoder has two overloads, one takes a FluxJpeg.Core.Image and the other takes in a DecodedJpeg.
I was trying to create a FluxJpeg.Core.Image but it expects a byte[][,] for the image data. byte[n][x,y] where x is width and y is height but I don't know what n should be.
I thought that n should be 4 since that would correspond to the argb info encoded in each pixel but when I tried that FJCore throws an argument out of range exception. Here is what I tried. Raster is my byte[4][x,y] array.
raster[0][x, y] = (byte)((pixel >> 24) & 0xFF);
raster[1][x, y] = (byte)((pixel >> 16) & 0xFF);
raster[2][x, y] = (byte)((pixel >> 8) & 0xFF);
raster[3][x, y] = (byte)(pixel & 0xFF);
Figured it out! I downloaded FJCore from code.google.com and went through the image class. It only expects the RGB bytes. Here is the function that I wrote. I need the base64 version of the image so that's what my function returns.
private static string GetBase64Jpg(WriteableBitmap bitmap)
{
int width = bitmap.PixelWidth;
int height = bitmap.PixelHeight;
int bands = 3;
byte[][,] raster = new byte[bands][,];
for (int i = 0; i < bands; i++)
{
raster[i] = new byte[width, height];
}
for (int row = 0; row < height; row++)
{
for (int column = 0; column < width; column++)
{
int pixel = bitmap.Pixels[width * row + column];
raster[0][column, row] = (byte)(pixel >> 16);
raster[1][column, row] = (byte)(pixel >> 8);
raster[2][column, row] = (byte)pixel;
}
}
ColorModel model = new ColorModel { colorspace = ColorSpace.RGB };
FluxJpeg.Core.Image img = new FluxJpeg.Core.Image(model, raster);
MemoryStream stream = new MemoryStream();
JpegEncoder encoder = new JpegEncoder(img, 90, stream);
encoder.Encode();
stream.Seek(0, SeekOrigin.Begin);
byte[] binaryData = new Byte[stream.Length];
long bytesRead = stream.Read(binaryData, 0, (int)stream.Length);
string base64String =
System.Convert.ToBase64String(binaryData,
0,
binaryData.Length);
return base64String;
}
This code is fine and it should work. I am using same code to send image stream to server via web service and than regenerate image using these bytes...you can save these bytes to Db also
[WebMethod]
public string SaveImage(string data, string fileName)
{
byte[] imageBytes = System.Convert.FromBase64String(data);
MemoryStream mem = new MemoryStream();
mem.Write(imageBytes, 0, imageBytes.Length);
System.Drawing.Image img = System.Drawing.Image.FromStream(mem);
img.Save("D:\\FinalTest.jpg");
return "Saved !";
}
Sounds like [n] should be the byte-array of the image, I have been looking into encoding WriteableBitmap into a JPEG and found the same library but have not looked into it in detail, but assume this would be the case, will add more later to this answer to see if this works, as I have not have the chance to try it yet. There will be some method to get the bytes of a WritableBitmap in Silverlight I guess as it is possible to save to other types.

Resources