How to get raw pixel data from Texture2D? - c#

I am trying to get pixel(rgba) values from SharpDX Texture2D and then to assign them to bitmap but i get this wrong image:
I know that the problem is when i read values from srcPtr(See code below), i think the offsets are wrong...
This is the code:
SharpDX.DXGI.Resource screenResource;
OutputDuplicateFrameInformation duplicateFrameInformation;
// Try to get duplicated frame within given time is ms
duplicatedOutput.AcquireNextFrame(5, out duplicateFrameInformation, out screenResource);
// copy resource into memory that can be accessed by the CPU
using (var screenTexture2D = screenResource.QueryInterface<Texture2D>())
device.ImmediateContext.CopyResource(screenTexture2D, screenTexture);
// Get the desktop capture texture
var mapSource = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None);
// Create Drawing.Bitmap
using (var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb))
{
var boundsRect = new Rectangle(0, 0, width, height);
// Copy pixels from screen capture Texture to GDI bitmap
var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.ReadOnly, bitmap.PixelFormat);
unsafe
{
byte* srcPtr = (byte*)mapSource.DataPointer;
byte* destPtr = (byte*)mapDest.Scan0;
var curRowOffs = 0;
var stride = width * 4;
byte[] rgbaValues = new byte[width*height*4];
for (uint y = 0; y < height; y++)
{
var index = curRowOffs;
for (uint x = 0; x < width; x++)
{
// ARGB = bytes [B,G,R,A]
rgbaValues[index] = srcPtr[index];
rgbaValues[index + 1] = srcPtr[index + 1];
rgbaValues[index + 2] = srcPtr[index + 2];
rgbaValues[index + 3] = srcPtr[index + 3];
//Copy rgba to bitmap
destPtr[index + 3] = srcPtr[index + 3];
destPtr[index + 2] = srcPtr[index + 2];
destPtr[index + 1] = srcPtr[index + 1];
destPtr[index] = srcPtr[index];
index += 4;
}
// Increase row offset
curRowOffs += stride;
}
}
// Release source and dest locks
bitmap.UnlockBits(mapDest);
device.ImmediateContext.UnmapSubresource(screenTexture, 0);
What i am doing wrong here?
How to put the raw pixels data into byte array?

Related

Generate int[,] array based on Bitmap

I'm currently writing an ASCOM driver which is used to take picture with a SigmaFP camera.
Everything works fine but I'm currently hitting a wall when I have to convert my DNG to a standard image array for ASCOM (https://ascom-standards.org/Help/Developer/html/P_ASCOM_DriverAccess_Camera_ImageArray.htm)
With libraw I convert my DNG file, convert it to a bitmap and save it as a new jpeg file.
But, when I try to conver this jpeg file to an array (using Bitmap and my custom ReadBitmap function) I get this kind of pictures :
output image
Here is the input image
I don't know what I have missed during those steps
Here is my ReadBitmap function :
public int[,] ReadBitmap(Bitmap img)
{
BitmapData data = img.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadOnly, img.PixelFormat);
IntPtr ptr = data.Scan0;
int bytesCount = Math.Abs(data.Stride) * img.Height;
var result = new int[img.Width, img.Height];
byte[] bytesArray = new byte[bytesCount];
Marshal.Copy(ptr, bytesArray, 0, bytesCount);
img.UnlockBits(data);
var width = img.Width;
var height = img.Height;
Parallel.For(0, width * height, rc =>
{
var b = bytesArray[rc * 3];
var g = bytesArray[rc * 3 + 1];
var r = bytesArray[rc * 3 + 2];
int row = rc / width;
int col = rc - width * row;
//var rowReversed = height - row - 1;
result[col, row] = (int)(0.299 * r + 0.587 * g + 0.114 * b);
}
);
return result;
}
Here is the method used to convert DNG to jpeg (which will be used to get the int array)
IntPtr data = LoadRaw(path);
NativeMethods.libraw_dcraw_process(data);
// extract raw data into allocated memory buffer
var errc = 0;
var ptr = NativeMethods.libraw_dcraw_make_mem_image(data, out errc);
// convert pointer to structure to get image info and raw data
var img = PtrToStructure<libraw_processed_image_t>(ptr);
Console.WriteLine("\nImage type: " + img.type);
Console.WriteLine("Image height: " + img.height);
Console.WriteLine("Image width: " + img.width);
Console.WriteLine("Image colors: " + img.colors);
Console.WriteLine("Image bits: " + img.bits);
Console.WriteLine("Data size: " + img.data_size);
Console.WriteLine("Checksum: " + img.height * img.width * img.colors * (img.bits / 8));
// rqeuired step before accessing the "data" array
Array.Resize(ref img.data, (int)img.data_size);
var adr = ptr + OffsetOf(typeof(libraw_processed_image_t), "data").ToInt32();
Copy(adr, img.data, 0, (int)img.data_size);
// calculate padding for lines and add padding
var num = img.width % 4;
var padding = new byte[num];
var stride = img.width * img.colors * (img.bits / 8);
var line = new byte[stride];
var tmp = new List<byte>();
for (var i = 0; i < img.height; i++)
{
Buffer.BlockCopy(img.data, stride * i, line, 0, stride);
tmp.AddRange(line);
tmp.AddRange(padding);
}
// release memory allocated by [libraw_dcraw_make_mem_image]
NativeMethods.libraw_dcraw_clear_mem(ptr);
// create/save bitmap from mem image/array
var bmp = new Bitmap(img.width, img.height, PixelFormat.Format24bppRgb);
var bmd = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
Copy(tmp.ToArray(), 0, bmd.Scan0, (int)img.data_size);
NativeMethods.libraw_close(data);
var outJPEG = path.Replace(Path.GetExtension(path), ".jpg");
Console.WriteLine("Saving image to: " + outJPEG);
bmp.Save(outJPEG, ImageFormat.Jpeg);
CurrentFilePath = outJPEG;
Bitmaps use a stride to describe the number of bytes of a row, this is to ensure all rows are aligned properly, i.e. stride >= width * bitsPerPixel. Additionally you use a parallel loop over all pixels and this is to fine grained parallelism, you should go parallel over rows, and process each row sequentially.
So your code should look something like
var result = new int[height, width];
Parallel.For(0, height, y =>
{
var rowStart = y * data.Stride;
for(var x = 0 ; x < width; x++){
var i = rowStart + x*3;
var b = bytesArray[i];
var g = bytesArray[i + 1];
var r = bytesArray[i + 2];
result[y, x] = (int) (0.299 * r + 0.587 * g + 0.114 * b);
}
}
);
You might also consider using unsafe code to access the pixelvalues from the datapointer directly, instead of using a large temporary buffer that is allocated on the large object heap and immediately thrown away.
You should probably also do a check of the pixel format to ensure it is bgr24, since your code will fail otherwise.
Node that multidimensional arrays may not be ideal for images. Images are usually indexed like [x,y] and converted to a linear index like var i = y * width + x. For multidimensional arrays the storage order is inverse, this might require indexing like [y,x], or doing unnecessary transpositions of images instead of a straight memory-copy whenever converting data to some other format. So I usually prefer to write my own 2D array since this usually make interoperability much easier:
public class Array2D<T> : IReadOnlyList<T>
{
public int Width { get; }
public int Height { get; }
public T[] Data { get; }
public Array2D(int width, int height)
{
Width = width;
Height = height;
Data = new T[width * height];
}
/// <summary>
/// Index operator
/// Note that this does not check if x is valid, it may wrap around to the next row
/// </summary>
public T this[int x, int y]
{
get => Data[y * Width + x];
set => Data[y * Width + x] = value;
}
}

Convert 8Mono pixel data array into 24bit image in c#

I am using mvfox-igc camera sdk for getting images from that camera my problem is that i have byte array of mono8 image and i want to convert that pixel data into 24bit image my programming language
can anybody help me please my code looks like this but is is not doing its work properly
processedBitmap = new Bitmap(data.request.imageWidth.read(),data.request.imageHeight.read(),PixelFormat.Format24bppRgb);
unsafe
{
BitmapData bitmapData = processedBitmap.LockBits(new Rectangle(0, 0, processedBitmap.Width, processedBitmap.Height), ImageLockMode.ReadWrite, processedBitmap.PixelFormat);
int bytesPerPixel = System.Drawing.Bitmap.GetPixelFormatSize(processedBitmap.PixelFormat) / 8;
int heightInPixels = bitmapData.Height;
int widthInBytes = bitmapData.Width * bytesPerPixel;
byte* PtrFirstPixel = (byte*)bitmapData.Scan0;
byte* firstby = (byte*)data.request.imageData.read();
int indexx=0;
Parallel.For(0, heightInPixels, y =>
{
byte* currentLine = PtrFirstPixel + (y * bitmapData.Stride);
for (int x = 0; x < widthInBytes; x = x + bytesPerPixel)
{
currentLine[x] = firstby[indexx];//((byte*)data.request.imageData.read())[0];//(byte)oldBlue;
currentLine[x + 1] = firstby[indexx];//((byte*)data.request.imageData.read())[0];//(byte)oldGreen;
currentLine[x + 2] = firstby[indexx];//((byte*)data.request.imageData.read())[0];//(byte)oldRed;
indexx = indexx + 1;
}
});
processedBitmap.UnlockBits(bitmapData);
processedBitmap.Save("bmp.jpg");

How to change value of red/green/blue/alpha values from pixels

I'm working on a processing image software and now i want to change white balance and add some saturation on my photos, so i have to work with each pixel value.
I've tried to transform my BitmapImage to WritableBitmap and then I've tried this code. But if i run it, my image turns to green.
I want to take pixels from that image and then set them to new values.
I've searched many solutions but found none, so maybe someone can help me with my code. Thanks.
public static WriteableBitmap Colors(Bitmap Image)
{
int width = Image.Width;
int height = Image.Width;
byte[,,] pixels = new byte[height, width, 4];
WriteableBitmap wbitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgra32, null);
var target = new Bitmap(Image.Width, Image.Height);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
pixels[i, j, 1] -= 30;
pixels[i, j, 3] -= 30;
}
}
byte[] pixels1d = new byte[height * width * 4];
int index = 0;
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
for (int i = 0; i < 4; i++)
pixels1d[index++] = pixels[row, col, i];
}
}
Int32Rect rect = new Int32Rect(0, 0, width, height);
int stride = 4 * width;
wbitmap.WritePixels(rect, pixels1d, stride, 0);
return wbitmap;
}
And then I'm running it like this:
private void run_OnClick(object sender, RoutedEventArgs e)
{
// Contrastt(BitmapImage2Bitmap(bitmap), 10);
// MedianFilter(BitmapImage2Bitmap(bitmap),5);
WriteableBitmap writeableBitmap = new WriteableBitmap(imageX);
BitmapImage colored = ConvertWriteableBitmapToBitmapImage(Colors((BitmapImage2Bitmap(bitmap))));
ImageBrush brush = new ImageBrush();
brush.ImageSource = imageX ;
CanvasProcessedImage.Background = brush;
}
Assuming that you already have a BitmapSource (or an instance of a derived class like BitmapImage) in a source variable, you would first create a BitmapSource with a known pixel format, and then create a WritabelBitmap from that:
// source is a BitmapSource with whatever PixelFormat
var format = PixelFormats.Bgra32;
var temp = new FormatConvertedBitmap(source, format, null, 0); // force BGRA
var bitmap = new WriteableBitmap(temp);
Now you could either directly manipulate the data in the WriteableBitmap's BackBuffer property, or first do it a bit simpler and create a copy of the buffer like this:
var width = bitmap.PixelWidth;
var height = bitmap.PixelHeight;
var stride = bitmap.BackBufferStride;
var buffer = new byte[stride * height];
bitmap.CopyPixels(buffer, stride, 0);
Then manipulate the buffer and write it back to the WriteableBitmap. Since you have specified the PixelFormat, you know which byte of each 4-byte pixel value is B, G, R and A respectively.
for (int i = 0; i < buffer.Length; i += 4)
{
// Blue
//buffer[i + 0] = ...
// Green
buffer[i + 1] = (byte)Math.Max(buffer[i + 1] - 30, 0);
// Red
buffer[i + 2] = (byte)Math.Max(buffer[i + 2] - 30, 0);
// Alpha
//buffer[i + 3] = ...
}
bitmap.WritePixels(new Int32Rect(0, 0, width, height), buffer, stride, 0);
When you have assigned the WriteableBitmap to the Source property of an Image element, or the ImageSource property of an ImageBrush, you can call WritePixels later (and as often as necessary) without needing to reassign it it to the Source/ImageSource property. It is changed "in place".

RenderTargetBitmap with format rgba32

I am not very familiar with bitmaps and I need to save a FrameworkElement (specificaly Grid) as bitmap and copy it to buffer. The problem is I need to save it in Rgba format, not Pgrba, which isn't supported in RenderTargetBitmap. Relevant code is here:
_targetBitmap = new RenderTargetBitmap(xres, yres, 96, 96, PixelFormats.Pbgra32);
_targetBitmap.Clear();
// Child is grid
_targetBitmap.Render(Child);
// copy the pixels into the buffer
_targetBitmap.CopyPixels(new Int32Rect(0, 0, xres, yres), bufferPtr, _bufferSize, _stride);
I tried using WriteableBitmap, but I didn't how to render the Child. Any suggestions?
The CopyPixels function is already giving you direct access to the pixel data, so all you need to do is convert between formats. In this case, you need to swap the channel orders around and undo the premultiplication of alpha values.
NOTE: This code assumes your bufferPtr is a byte array or a byte pointer.
for (int y = 0; y < yres; y++)
{
for (int x = 0; x < xres; x++)
{
// Calculate array offset for this pixel
int offset = y * _stride + x * 4;
// Extract individual color channels from pixel value
int pb = bufferPtr[offset];
int pg = bufferPtr[offset + 1];
int pr = bufferPtr[offset + 2];
int alpha = bufferPtr[offset + 3];
// Remove premultiplication
int r = 0, g = 0, b = 0;
if (alpha > 0)
{
r = pr * 255 / alpha;
g = pg * 255 / alpha;
b = pb * 255 / alpha;
}
// Write color channels in desired order
bufferPtr[offset] = (byte)r;
bufferPtr[offset + 1] = (byte)g;
bufferPtr[offset + 2] = (byte)b;
bufferPtr[offset + 3] = (byte)alpha;
}
}

Using LockBits generates weird image

I'm trying to write out a grayscale image using Lockbits, my current code looks is
/// <summary>
/// Save the content of the FrameProc out to a bitmap
/// </summary>
public void Save(string path)
{
Bitmap bmp = new Bitmap(this.size.Width, this.size.Height
,PixelFormat.Format32bppRgb);
var data = bmp.LockBits(this.size, ImageLockMode.WriteOnly, bmp.PixelFormat);
unsafe
{
for (int y = 0; y < this.size.Height; y++)
{
byte* row = (byte*)data.Scan0 + (y * data.Stride);
for (int x = 0; x < this.size.Width; x++)
{
byte value = (byte)this.buffer[y, x];
row[x*Bits+r] = value;
row[x*Bits+g] = value;
row[x*Bits+b] = value;
}
}
}
bmp.UnlockBits(data);
bmp.Save(path, ImageFormat.Bmp);
}
where
/// <summary>
/// The amount of Bytes per pixel in the image
/// </summary>
private const int Bits = 4;
/// <summary>
/// Image components
/// </summary>
private const int a=3, r = 2, g = 1, b = 0;
However the image i receive is not correct:
Maybe this is related to how i'm reading them in? So here's that code
public FrameProc(Bitmap bmp)
{
this.size=new Rectangle(new Point(0,0), bmp.Size);
var data = bmp.LockBits(this.size
,ImageLockMode.ReadOnly
,bmp.PixelFormat);
this.buffer = new Matrix(this.size.Height, this.size.Width);
unsafe
{
for (int y = 0; y < this.size.Height; y++)
{
byte* row = (byte*)data.Scan0 + (y * data.Stride);
for (int x = 0; x < this.size.Width; x++)
{
this.buffer[y,x] = 0.299*row[x*Bytes+r]
+ 0.587*row[x*Bytes+g]
+ 0.114*row[x*Bytes+b];
}
}
}
bmp.UnlockBits(data);
}
From the results you're getting - it looks exactly as if each pixel is three bytes big and not four as you have declared it - and as one would expect. (Note: you called it Bits - but that's wrong - it should be namned Bytes, not Bits).
I'd experiment with any one of this:
change from 4 to 3 bytes
change from Format32bppRgb to Format32bppArgb and fill out the alpha with 255
change from 4 to 3 bytes and from Format32bppRgb to from Format24bppRgb
I would also rewrite the loop slightly for performance (sorry, I can't help myself):
for (int x = 0; x < this.size.Width; x++, row += Bits)
{
byte value = (byte)this.buffer[y, x];
row[r] = value;
row[g] = value;
row[b] = value;
}
But were you really would get more speed if you get a pointer to this.buffer using the fixed keyword. Yes, you're not having any performance problems, but I couldn't help myself from mentioning it!
Use this function indeed:
public Bitmap MakeGrayscale(Bitmap original)
{
unsafe
{
//create an empty bitmap the same size as original
Bitmap newBitmap = new Bitmap(original.Width, original.Height);
//lock the original bitmap in memory
BitmapData originalData = original.LockBits(
new Rectangle(0, 0, original.Width, original.Height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
//lock the new bitmap in memory
BitmapData newData = newBitmap.LockBits(
new Rectangle(0, 0, original.Width, original.Height),
ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
//set the number of bytes per pixel
// here is set to 3 because I use an Image with 24bpp
int pixelSize = 3;
for (int y = 0; y < original.Height; y++)
{
//get the data from the original image
byte* oRow = (byte*)originalData.Scan0 + (y * originalData.Stride);
//get the data from the new image
byte* nRow = (byte*)newData.Scan0 + (y * newData.Stride);
for (int x = 0; x < original.Width; x++)
{
//create the grayscale version
byte grayScale =
(byte)((oRow[x * pixelSize] * .11) + //B
(oRow[x * pixelSize + 1] * .59) + //G
(oRow[x * pixelSize + 2] * .3)); //R
//set the new image's pixel to the grayscale version
nRow[x * pixelSize] = grayScale; //B
nRow[x * pixelSize + 1] = grayScale; //G
nRow[x * pixelSize + 2] = grayScale; //R
}
}
//unlock the bitmaps
newBitmap.UnlockBits(newData);
original.UnlockBits(originalData);
return newBitmap;
}
}
Source and other interesting examples (with theory behind) could be taken from here

Categories

Resources