locked
Windows Store - Pixels Array

    Question

  •        

    I am working at an app where I need to get the array of pixels from an Image and to edit the Image using the pixels array.

    I am using the next code for getting the pixels array from the StorageFile object which indicates the image:

            public static async Task<byte[]> GetPixelsArrayFromStorageFileAsync(IRandomAccessStreamReference file)
            {
                using (IRandomAccessStream stream = await file.OpenReadAsync())
                {
                    using (var reader = new DataReader(stream.GetInputStreamAt(0)))
                    {
                        await reader.LoadAsync((uint)stream.Size);
                        var pixelByte = new byte[stream.Size];
                        reader.ReadBytes(pixelByte);
                        return pixelByte;
                    }
                }
            }


    Now, my questions are:

    1. Why if I load a image which is 6000 x 4000 pixels I have an array of just 8,941,799 which is actually the size of my image on disk?
    2. How can I access the RGBA channels of the pixels?



    • Edited by ArchGabriel Wednesday, July 30, 2014 8:21 PM
    Wednesday, July 30, 2014 8:19 PM

Answers

  • Hi ArchGabriel,

    1. I think this is because you're just reading the file as it is on disk. ;-) You need to grab the pixels. Play around with this snippet:

       using (IRandomAccessStream stream = await file.OpenReadAsync())
    {
      var decoder = await BitmapDecoder.CreateAsync(stream);
    var transform = new BitmapTransform();
    var pixelDataProvider = await decoder.GetPixelDataAsync(decoder.BitmapPixelFormat, decoder.BitmapAlphaMode, transform, ExifOrientationMode.IgnoreExifOrientation, ColorManagementMode.ColorManageToSRgb);
    byte[] pixelData = pixelDataProvider.DetachPixelData();
    
    // Do something with the pixelData
    }


    2. Each byte in the pixelArray above is the color. That means for bytes are one pixel. So you can do the following to loop throw the pixels:

      for (int i = 0; i < pixelData.Length; i++)
          {
            if ((i + 1) % 4 == 0)
            {
            
              byte aplha = pixelData[i];
              byte red = pixelData[i - 1];
              byte green = pixelData[i - 2];
              byte blue = pixelData[i - 3];
              
              // Do something with that pixel :-)
           }
    }

    You can e.g. build your own pixelData and write it to another Image-file with the BitmapEncoder. If you just want to display the stuff in your UI, look at the WriteableBitmap-class.



    Thomas Claudius Huber

    "If you can't make your app run faster, make it at least look & feel extremly fast"

    My latest Pluralsight-course: Windows Store Apps - Data Binding in Depth

    twitter: @thomasclaudiush
    homepage: www.thomasclaudiushuber.com
    author of: ultimate Windows Store Apps handbook | ultimate WPF handbook | ultimate Silverlight handbook

    • Marked as answer by ArchGabriel Wednesday, July 30, 2014 10:09 PM
    Wednesday, July 30, 2014 8:54 PM
  • Use a WriteableBitmap for that. You can also assign a WriteableBitmap to the Source-Property of an Image-Element (like you do with the BitmapImage). Should be something like this:

          WriteableBitmap writeableBitmap = new WriteableBitmap(6000, 4000);
    
            using (Stream bufferStream = writeableBitmap.PixelBuffer.AsStream())
          {
            bufferStream.Seek(0, SeekOrigin.Begin);
            bufferStream.Write(byteArray, 0, (int)bufferStream.Length);
          }
    
          writeableBitmap.Invalidate();
    
          return writeableBitmap;


    Thomas Claudius Huber

    "If you can't make your app run faster, make it at least look & feel extremly fast"

    My latest Pluralsight-course: Windows Store Apps - Data Binding in Depth

    twitter: @thomasclaudiush
    homepage: www.thomasclaudiushuber.com
    author of: ultimate Windows Store Apps handbook | ultimate WPF handbook | ultimate Silverlight handbook

    • Marked as answer by ArchGabriel Wednesday, July 30, 2014 10:10 PM
    Wednesday, July 30, 2014 9:37 PM

All replies

  • Hi ArchGabriel,

    1. I think this is because you're just reading the file as it is on disk. ;-) You need to grab the pixels. Play around with this snippet:

       using (IRandomAccessStream stream = await file.OpenReadAsync())
    {
      var decoder = await BitmapDecoder.CreateAsync(stream);
    var transform = new BitmapTransform();
    var pixelDataProvider = await decoder.GetPixelDataAsync(decoder.BitmapPixelFormat, decoder.BitmapAlphaMode, transform, ExifOrientationMode.IgnoreExifOrientation, ColorManagementMode.ColorManageToSRgb);
    byte[] pixelData = pixelDataProvider.DetachPixelData();
    
    // Do something with the pixelData
    }


    2. Each byte in the pixelArray above is the color. That means for bytes are one pixel. So you can do the following to loop throw the pixels:

      for (int i = 0; i < pixelData.Length; i++)
          {
            if ((i + 1) % 4 == 0)
            {
            
              byte aplha = pixelData[i];
              byte red = pixelData[i - 1];
              byte green = pixelData[i - 2];
              byte blue = pixelData[i - 3];
              
              // Do something with that pixel :-)
           }
    }

    You can e.g. build your own pixelData and write it to another Image-file with the BitmapEncoder. If you just want to display the stuff in your UI, look at the WriteableBitmap-class.



    Thomas Claudius Huber

    "If you can't make your app run faster, make it at least look & feel extremly fast"

    My latest Pluralsight-course: Windows Store Apps - Data Binding in Depth

    twitter: @thomasclaudiush
    homepage: www.thomasclaudiushuber.com
    author of: ultimate Windows Store Apps handbook | ultimate WPF handbook | ultimate Silverlight handbook

    • Marked as answer by ArchGabriel Wednesday, July 30, 2014 10:09 PM
    Wednesday, July 30, 2014 8:54 PM
  • Thank you very much. I will try the code and tell you if it worked. The problem is now how will I convert the pixel array to the a BitmapImage. I was using this code, but I suppose this won't work any more.

            public static async Task<BitmapImage> GetBitmapImageFromPixelsArrayAsync(byte[] byteArray)
            {
                using (var stream = new InMemoryRandomAccessStream())
                {
                    var image = new BitmapImage();
                    await stream.WriteAsync(byteArray.AsBuffer());
                    stream.Seek(0);
                    await image.SetSourceAsync(stream);
                    return image;
                }

            }

    Can you please help me with that too?

    Wednesday, July 30, 2014 9:16 PM
  • Use a WriteableBitmap for that. You can also assign a WriteableBitmap to the Source-Property of an Image-Element (like you do with the BitmapImage). Should be something like this:

          WriteableBitmap writeableBitmap = new WriteableBitmap(6000, 4000);
    
            using (Stream bufferStream = writeableBitmap.PixelBuffer.AsStream())
          {
            bufferStream.Seek(0, SeekOrigin.Begin);
            bufferStream.Write(byteArray, 0, (int)bufferStream.Length);
          }
    
          writeableBitmap.Invalidate();
    
          return writeableBitmap;


    Thomas Claudius Huber

    "If you can't make your app run faster, make it at least look & feel extremly fast"

    My latest Pluralsight-course: Windows Store Apps - Data Binding in Depth

    twitter: @thomasclaudiush
    homepage: www.thomasclaudiushuber.com
    author of: ultimate Windows Store Apps handbook | ultimate WPF handbook | ultimate Silverlight handbook

    • Marked as answer by ArchGabriel Wednesday, July 30, 2014 10:10 PM
    Wednesday, July 30, 2014 9:37 PM