0

To start things off I'm fairly new to C#, XAML and windows phone. So far I have managed to get a photo into the app via gallery/camera and turn it into grayscale. But my aim is to get the rgb values from a pixel and display it under the image as text. How would I go about changing the grayscale code to instead modify the image to just display the rgb code? Thanks!

async void GrayScale(Image lpictureBox)
{

    WriteableBitmap modifiedImage = lpictureBox.Source as WriteableBitmap;
    if (modifiedImage == null)
    {
        BitmapSource bs = lpictureBox.Source as BitmapSource;
        modifiedImage = new WriteableBitmap(bs.PixelWidth, bs.PixelHeight);
    }
    int r = 0, a = 0, i = 0;
    int h = modifiedImage.PixelHeight;
    int w = modifiedImage.PixelWidth;
    Stream stream = modifiedImage.PixelBuffer.AsStream();
    byte[] StreamBuffer = new byte[stream.Length];
    stream.Seek(0, 0);
    await stream.ReadAsync(StreamBuffer, 0, StreamBuffer.Length);
    for (i = 0; i < StreamBuffer.Length - 4; i = i + 4)
    {
        var a1 = StreamBuffer[i + 3];
        var r1 = StreamBuffer[i + 2];
        var g1 = StreamBuffer[i + 1];
        var b1 = StreamBuffer[i + 0];
        a = 0xff;
        r = (byte)((0.299 * r1) + (0.587 * g1) + (0.114 * b1));
        StreamBuffer[i + 3] = (byte)a;  // alpha​
        StreamBuffer[i + 2] = (byte)(r > 255 ? 255 : (r < 0 ? 0 : r));  //red​
        StreamBuffer[i + 1] = (byte)(r > 255 ? 255 : (r < 0 ? 0 : r));  //green​
        StreamBuffer[i + 0] = (byte)(r > 255 ? 255 : (r < 0 ? 0 : r));  //blue​
    }
    stream.Seek(0, 0);
    stream.Write(StreamBuffer, 0, StreamBuffer.Length);
    modifiedImage.Invalidate();
    lpictureBox.Source = modifiedImage;
}
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
  • 2
    What do you mean by "modify the image to just display the rgb code"? – CoderDennis Mar 26 '15 at 15:22
  • @CoderDennis so for example, instead of changing the image into grayscale, just identify the RGB value of a pixel and display it as e.g "R 255 G 255 B 255". Does that make sense? – Salman Ahmed Mar 26 '15 at 15:25
  • But what do you mean by "modify the image"? Do you really want the RGB value to be written into the bitmap itself? Or would it suffice to simply display it as text overlaid in the UI on top of the bitmap? The latter seems more reasonable, since then you don't have to keep making new copies of the bitmap every time the selected pixel changes. – Peter Duniho Mar 26 '15 at 15:36
  • @PeterDuniho Just displaying it as text in the UI would suffice! – Salman Ahmed Mar 26 '15 at 15:40
  • The code you've got acts on every pixel. Which pixel do you want the RGB value for? – CoderDennis Mar 26 '15 at 15:44
  • @PeterDuniho for now the middle pixel of the picture would be enough – Salman Ahmed Mar 26 '15 at 15:46
  • http://writeablebitmapex.codeplex.com/ adds some great features to WriteableBitmap (including GetPixel) and works on Windows Phone. I used it in my app. – CoderDennis Mar 26 '15 at 15:48
  • Okay, great. Then what's the problem? You already have code that retrieves the raw pixel data. In theory, you should be able to use that along with the bitmap width and height information to access individual pixels (though I don't know enough about the phone API to know myself how that would work...unfortunately, Windows Phone appears to be missing a lot of the useful features found in the Winforms/Media and WPF APIs), which you could then display in some other control on top of the bitmap. Unfortunately, it's just not clear what part of the problem you're having trouble with. – Peter Duniho Mar 26 '15 at 15:50
  • @PeterDuniho I am quite confused on how I would change the code I have to solve my problem. as stated above I'm fairly new to C# – Salman Ahmed Mar 26 '15 at 15:53
  • It's fine to be confused, but you need to at least present a question that itself is not confusing. MSDN is short of details of the Windows Phone `WriteableBitmap` pixel format, but according to the extension library mentioned above, stride is always 1 and the bitmap format is always 32 bpp, so you can address a specific pixel byte as `StreamBuffer[y * w * 4 + x * 4 + byteOffset]` (where `byteOffset` is 0, 1, 2, or 3 depending on whether you want B, G, R, or A, respectively). But does that answer your question or do you want help with the _display_ of that information? – Peter Duniho Mar 26 '15 at 16:01
  • @PeterDuniho how would I start it off? editing the grayscale code I have or starting from scratch after `WriteableBitmap modifiedImage = lpictureBox.Source as WriteableBitmap; if (modifiedImage == null) { BitmapSource bs = lpictureBox.Source as BitmapSource; modifiedImage = new WriteableBitmap(bs.PixelWidth, bs.PixelHeight); }` – Salman Ahmed Mar 26 '15 at 16:12

2 Answers2

0

There are lots of ways to accomplish what you're asking, including just using the third-party extensions library commenter CoderDennis mentioned.

But the code you've posted is very close to what you'd need, if I understand both your question and the Windows Phone implementation (the MSDN docs are short on details, such as pixel format and stride, two important properties of a bitmap to know when retrieving specific pixel data). Based on the code in the above-mentioned library, Windows Phone bitmaps are always 32 bits per pixel and always have a stride equal to the width of the bitmap.

Note also that if you are going to be retrieving the pixel information frequently, you probably do not want to re-read the entire bitmap every time you want to retrieve the pixel information. Instead, you'll want to cache the byte buffer for reuse each time.

So with that in mind, here's your original code, rearranged and extended to accomplish what you're asking:

class BitmapData
{
    private readonly byte[] _buffer;
    private readonly int _stride;

    private BitmapData(byte[] buffer, int stride)
    {
        _buffer = buffer;
        _stride = stride;
    }

    public Color GetPixel(int x, int y)
    {
        Color color = new Color();

        // Convert from pixel to byte offsets
        int pixelOffset = y * _stride * 4 + x * 4;

        // Retrieve pixel data
        color.A = _buffer[pixelOffset + 3];
        color.R = _buffer[pixelOffset + 2];
        color.G = _buffer[pixelOffset + 1];
        color.B = _buffer[pixelOffset + 0];

        return color;
    }

    public static async Task<BitmapData> Create(BitmapSource source)
    {
        WriteableBitmap dataSource = source as WriteableBitmap;

        if (dataSource == null)
        {
            // NOTE: in your original code, this is all the initialization you
            // did when the input isn't already a WriteableBitmap object.
            // Wouldn't it make more sense to copy the source bitmap
            // to your new WriteableBitmap object? Otherwise, you won't have
            // the pixel data from the source!
            dataSource = new WriteableBitmap(source.PixelWidth, source.PixelHeight);
        }

        Stream stream = dataSource.PixelBuffer.AsStream();
        byte[] buffer = new byte[stream.Length];
        stream.Seek(0, 0);
        await stream.ReadAsync(buffer, 0, buffer.Length);

        return new BitmapData(buffer, dataSource.PixelWidth);
    }
}

You can create like this:

BitmapData bitmapData = await BitmapData.Create(lpictureBox.Source);

You'll need to save that object in a field somewhere, so that later when you want the pixel information, you can get it:

Color pixelColor = bitmapData.GetPixel(x, y);
Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • `Color` and `Task` don't seem to be recognised? – Salman Ahmed Mar 26 '15 at 16:59
  • @SalmanAhmed: they are both types supported in Windows Phone, but you may need to add an assembly reference and/or `using` directive to make them available in your source code. – Peter Duniho Mar 26 '15 at 17:00
  • I have added the first part of code to my project. But where do I put the final two lines of code? `BitmapData bitmapData = await BitmapData.Create(lpictureBox.Source);` and `Color pixelColor = bitmapData.GetPixel(x, y); ` How do I get the rgb to show in the UI? – Salman Ahmed Mar 26 '15 at 17:32
  • @SalmanAhmed: you haven't provided enough context in your original question for me or anyone else to answer those two questions. You probably want to create the `BitmapData` object as soon as you've loaded the window/page, or when you initialize the picture box control. You would call `GetPixel()` when you need the pixel value (e.g. in a mouse-movement event). Showing the RGB value in the UI is a completely different question than this one, and again would require a lot more context. Please see http://stackoverflow.com/help/mcve and http://stackoverflow.com/help/how-to-ask – Peter Duniho Mar 26 '15 at 17:51
  • Okay sorry, let me start over. I want the user to be able to identify the RGB code of a pixel on button press and for the RGB code to be displayed in the UI for the user to see. I am a beginner at C#. Does that make sense? – Salman Ahmed Mar 27 '15 at 01:07
  • @SalmanAhmed: it makes sense, but it doesn't help in answering your questions. Only if there is an _existing_ code example could anyone tell you where in that code other statements should go. And as far as the UI aspects, i.e. pressing a button, showing the RGB values, etc. like I said: that's a whole other question (i.e. you need to post another question, and again provide a good code example to go with it). – Peter Duniho Mar 27 '15 at 01:38
-1

If you need to know the RGB code for a specific pixel on a bitmap, you can use the Bitmap.GetPixel() method.

Here is a link to Bitmap.GetPixel() on MSDN

EDIT: Perhaps 'WritableBitmapEx' is what you need.

Their description:

The WriteableBitmapEx library is a collection of extension methods for the WriteableBitmap. The WriteableBitmap class is available for Windows Phone, WPF, WinRT Windows Store XAML and Silverlight and allows the direct manipulation of a bitmap and could be used to generate fast procedural images by drawing directly to a bitmap. The WriteableBitmap API is very minimalistic and there's only the raw Pixels array for such operations. The WriteableBitmapEx library tries to compensate that with extensions methods that are easy to use like built in methods and offer GDI+ like functionality.

This provides .GetPixel() and .GetPixeli() for the WritableBitmap class.

Malte R
  • 468
  • 5
  • 16
  • To use that, you need to start with an instance of `System.Drawing.Bitmap`, which isn't what the OP has. – Peter Duniho Mar 26 '15 at 15:35
  • I have tried using that but the System.Drawing namespace isn't used for windows phone apps – Salman Ahmed Mar 26 '15 at 15:36
  • @MalteR I'm struggling with how I'd implement that into the code I have, not sure what my next step is – Salman Ahmed Mar 26 '15 at 16:22
  • @SalmanAhmed It actually seems simple. You need to add a reference to the .dll for Windows Phone they provide. Then the extensions should work on the `WritableBitmap` class. I have just tried, and the extended functionality is available when dotting. i can call both .GetPixel() and .GetPixeli() on an instance of `WritableBitmap`, and get the color of a pixel as a `Color` class or as an integer, depending on which function. – Malte R Mar 26 '15 at 16:44
  • @MalteR I have downloaded WriteableBitmapEx via package manager console, but it doesn't seem to recognise `Color` – Salman Ahmed Mar 26 '15 at 16:57
  • @SalmanAhmed I downloaded it from their website and added a reference to the dll, but using package-manager should work just as fine. Can you see the extended functions when dotting the `WritableBitmap` ? Can you call the `modifiedImage.GetPixeli(x, y)` method? – Malte R Mar 26 '15 at 17:12
  • @MalteR yes, I can call that. but `Color` is not recognised – Salman Ahmed Mar 26 '15 at 17:14
  • @SalmanAhmed I just tried installing it through PM, like you did, and it works just as fine for me. I can call the GetPixeli method on a WriteableBItmap and get and integer representing the RGB code. – Malte R Mar 26 '15 at 17:15
  • @SalmanAhmed There are two variants of the GetPixel function: `GetPixel(x, y)` returns the color as a `Color` class. `GetPixeli(x, y)` returns the RGB code as an integer. – Malte R Mar 26 '15 at 17:17
  • @SalmanAhmed Ok, does it provide the same functionality as WriteableBitmapEx, or did you need it make WriteableBitmapEx work for you? – Malte R Mar 26 '15 at 17:19
  • @MalteR still in the process of getting it to work! Sorry I didn't downvote you :) – Salman Ahmed Mar 26 '15 at 17:38