How do I merge layers using raw pixel data in C# UWP? - c#

Because the Universal Windows Platform does not support flattening Bitmap "layers" by default, I have been creating an algorithm (based on one found here) to take each individual pixels of each layer, and overlay them one over another. The problem is that because I'm using Alpha to allow images to have transparent pixels, I am unsure how to properly merge the pixels so it looks as though one is being places on top of the other.
Here is my code so far:
private unsafe SoftwareBitmap CompileImage()
{
SoftwareBitmap softwareBitmap = new SoftwareBitmap(BitmapPixelFormat.Bgra8, Width, Height);
using (BitmapBuffer buffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.Write))
{
using (var reference = buffer.CreateReference())
{
byte* dataInBytes;
uint capacity;
((IMemoryBufferByteAccess)reference).GetBuffer(out dataInBytes, out capacity);
BitmapPlaneDescription bufferLayout = buffer.GetPlaneDescription(0);
for (int index = 0; index < layers.Length; index++)
{
for (int i = 0; i < bufferLayout.Height; i++)
{
for (int j = 0; j < bufferLayout.Width; j++)
{
if(index == 0)
{
dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 0] =
layers[index].pixelData[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 0]; //B
dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 1] =
layers[index].pixelData[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 1]; //G
dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 2] =
layers[index].pixelData[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 2]; //R
dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 3] =
layers[index].pixelData[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 3]; //A
}
else if(index > 0)
{
//Attempts to "Average" pixel data
dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 0] =
(byte)(dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 0] *
layers[index].pixelData[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 0] / 2);
dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 1] =
(byte)(dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 1] *
layers[index].pixelData[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 1] / 2);
dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 2] =
(byte)(dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 2] *
layers[index].pixelData[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 2] / 2);
dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 3] =
(byte)(dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 3] *
layers[index].pixelData[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 3] / 2);
}
}
}
}
}
}
return softwareBitmap;
}
The current algorithm will take the average BGRA values of each pixel in every layer, and return the result. This is not what I intended it to do. How do I go about correcting it? Thanks in advance

To apply an alpha mix you multiply the previous color by the inverse alpha and the current color by the alpha, of course you should do it on a 0-1 scale (that's why graphic cards work with floats for colors).
Per example, suppose you have three layers, A, B and C, you use A as a base, then mix it with B: (pseudocode)
//bAlpha is thge alpha value of the current pixel from B in byte value
var alpha = bAlpha / 255.0f;
var invAlpha = 1.0f - alpha;
//aRGB is the RGB value of the pixel on the layer A
//bRGB is the RGB value of the pixel on the layer B
var abRGB = aRGB * invAlpha + bRGB * alpha;
And now you apply the third layer:
//cAlpha is thge alpha value of the current pixel from C in byte value
alpha = cAlpha / 255.0f;
invAlpha = 1.0f - alpha;
//abRGB is the RGB value of the previously mixed layers
//cRGB is the RGB value of the pixel on the layer C
var abcRGB = abRGB * invAlpha + cRGB * alpha;
Of course all of this is on ARGB mode, there's also pARGB in which the colors are already premultiplied, so you only need to multiply the previous color by the inverse alpha and add the current color.
//bAlpha is the alpha value of the current pixel from B in byte value
var invAlpha = 1.0f - (bAlpha / 255.0f);
//aRGB is the RGB value of the pixel on the layer A
//bRGB is the RGB value of the pixel on the layer B
var abRGB = aRGB * invAlpha + bRGB;
invAlpha = 1.0f - (cAlpha / 255.0f);
//abRGB is the RGB value of the previously mixed layers
//cRGB is the RGB value of the pixel on the layer C
var abcRGB = abRGB * invAlpha + cRGB;

Related

C# double rounded down to zero [duplicate]

This question already has answers here:
Why does division result in zero instead of a decimal?
(5 answers)
Closed 5 years ago.
I have some C# code for converting an omnicam picture to a normal image. However, the formula is behaving differently than expected.
Code:
int Rin = 35; //Image specific variable
int Rout = 237; //Image specific variable
int cx = 312; //Image specific variable
int cy = 239; //Image specific variable
int width = (int) Math.Round(2 * Math.PI * Rout);
int height = (int) Math.Round((double)(Rout - Rin));
for (int i = 0; i < image.GetLength(0); i++)
{
double y1 = i / height;
double r = Rin + (1 - y1 * height);
for (int j = 0; j < image.GetLength(1); j++)
{
double x1 = j / width;
double theta = -2 * Math.PI * (j / width);
double xp = r * Math.Cos(theta) + cx;
double yp = r * Math.Sin(theta) + cy;
Console.WriteLine(theta + " = " + (-2 * Math.PI) + " * (" + j + " / " + width + ")");
Console.WriteLine(xp + " = " + r + " * " + Math.Cos(theta) + " + " + cx);
Console.WriteLine(yp + " = " + r + " * " + Math.Sin(theta) + " + " + cy);
Console.WriteLine("");
omnicam[i, j] = Color.FromArgb(1, 1, 1);
}
}
However, this prints out
0 = -6.28318530717959 * (82 / 1489)
348 = 36 * 1 + 312
239 = 36 * 0 + 239
If I print x1, it shows 0 for all values. However, I'm not sure why. The loop goes up to 895, and 895 / 1489 = 0.601... So why is it rounded down automatically?
double x1 = j / width;
Both j and width are INTs.
double x1 = j / (double)width;
Will give you the expected result.
If both of the arguments in C# is a int, then the result will be a int.
If any of the arguments in C# is a double, a double divide is used which results in a double.

How can I reset the i variable inside my for loop?

for (int k = 0; k < 32;k=k+1)
{
for (int j = 1; j < 32; j++)
{
double Score = (user0[k] * user0[j] + user1[k] * user1[j] + user2[k] * user2[j] + user3[k] * user3[j] + user4[k] * user4[j] + user5[k] * user5[j] + user6[k] * user6[j] + user7[k] * user7[j] + user8[k] * user8[j] + user9[k] * user9[j] + user10[k] * user10[j] + user11[k] * user11[j] + user12[k] * user12[j] + user13[k] * user13[j] + user14[k] * user14[j] + user15[k] * user15[j] + user16[k] * user16[j] + user17[k] * user17[j] + user18[k] * user18[j] + user19[k] * user19[j] + user20[k] * user20[j] + user21[k] * user21[j] + user22[k] * user22[j] + user23[k] * user23[j] + user24[k] * user24[j] + user25[k] * user25[j] + user26[k] * user26[j] + user27[k] * user27[j] + user28[k] * user28[j] + user29[k] * user29[j] + user30[k] * user30[j] + user31[k] * user31[j]) / ((Math.Sqrt(user0[k] * user0[k] + user1[k] * user1[k] + user2[k] * user2[k] + user3[k] * user3[k] + user4[k] * user4[k] + user5[k] * user5[k] + user6[k] * user6[k] + user7[k] * user7[k] + user8[k] * user8[k] + user9[k] * user9[k] + user10[k] * user10[k] + user11[k] * user11[k] + user12[k] * user12[k] + user13[k] * user13[k] + user14[k] * user14[k] + user15[k] * user15[k] + user16[k] * user16[k] + user17[k] * user17[k] + user18[k] * user18[k] + user19[k] * user19[k] + user20[k] * user20[k] + user21[k] * user21[k] + user22[k] * user22[k] + user23[k] * user23[k] + user24[k] * user24[k] + user25[k] * user25[k] + user26[k] * user26[k] + user27[k] * user27[k] + user28[k] * user28[k] + user29[k] * user29[k] + user30[k] * user30[k] + user31[k] * user31[k])) * (Math.Sqrt(user0[j] * user0[j] + user1[j] * user1[j] + user2[j] * user2[j] + user3[j] * user3[j] + user4[j] * user4[j] + user5[j] * user5[j] + user6[j] * user6[j] + user7[j] * user7[j] + user8[j] * user8[j] + user9[j] * user9[j] + user10[j] * user10[j] + user11[j] * user11[j] + user12[j] * user12[j] + user13[j] * user13[j] + user14[j] * user14[j] + user15[j] * user15[j] + user16[j] * user16[j] + user17[j] * user17[j] + user18[j] * user18[j] + user19[j] * user19[j] + user20[j] * user20[j] + user21[j] * user21[j] + user22[j] * user22[j] + user23[j] * user23[j] + user24[j] * user24[j] + user25[j] * user25[j] + user26[j] * user26[j] + user27[j] * user27[j] + user28[j] * user28[j] + user29[j] * user29[j] + user30[j] * user30[j] + user31[j] * user31[j])));
if (Score > simScore)
{
simScore = Score;
}
}
System.Console.WriteLine("Score =" + simScore);
}
I think you can ignore the long equation.
I need this program to compare 1 book to 32 other books.
The for (int k = 0; k < 5;k=k+1)loop is for the one selected book to be compared to 32 others (then give the greatest similarity rating).
The for (int j = 1; j < 32; j++) loop is to allow all the different books to be compared to the the book selected (book k).
The problem is that a book cannot be compared to itself because I think it ruins the equation and I get non-sensical values for the similarity ratings.
How can I omit a book?
(For example: When comparing k=3 (book 3) to other books, how can I make it so that j will not use the same reference?
Start the second loop from one past the first. Comparing the pair (a,b) is the same as comparing (b,a), so you don't need to start from the beginning with each subsequent book.
for (var k = 0; k < 5; k++)
{
for (var j = k + 1; j < 5; j++)
{
....
// if you need the complete set, you can store both
rating[j][k] = ...
rating[k][j] = ...
}
}
The most straightforward way would be to put an if statement that only evaluates the body of the second loop if k != j. I'm not familiar with C#, so there may be other ways of doing it that I'm not aware of, but this is how I would do it in Python.
How about using continue?
for (int k = 0; k < 32; k++) {
for (int j = 0; j < 32; j++) {
if (j == k)
continue;
// Compare
if (isMatch)
break;
}
}

mipmapping producing wrong results

I'm trying to generate the mipmap of an image. The pixels are stored as a byte[] and the format is {r,g,b,a,r,g,b,a,r,g,b,a ... }
What this is trying to do is get each group of four pixels in the image and find the average of those four pixels, then put that into a new image.
The result of creating all the mipmaps for a sample texture are here: http://imgur.com/KdEEzAw
If there is a way to create the mipmaps without using my own algorithm, and without directx or anything (i'm not using the mipmaps for rendering, i'm saving them to a file) that would be good
public static byte[] mipmap(byte[] inPixels, int width, int height)
{
// add one to width and height incase they are 1
byte[] outPixels = new byte[((width + 1) / 2) * ((height + 1) / 2) * 4];
for (int y = 0; y < height; y += 2)
{
for (int x = 0; x < width; x += 2)
{
// get the four red values
int[] r = new int[4];
r[0] = (int)inPixels[x + y * width + 0]; // top left
r[1] = (int)inPixels[(x + 1) + y * width + 0]; // top right
r[2] = (int)inPixels[(x + 1) + (y + 1) * width + 0]; // bottom right
r[3] = (int)inPixels[x + (y + 1) * width + 0]; // bottom left
// get the four green values
int[] g = new int[4];
g[0] = (int)inPixels[x + y * width + 1]; // top left
g[1] = (int)inPixels[(x + 1) + y * width + 1]; // top right
g[2] = (int)inPixels[(x + 1) + (y + 1) * width + 1]; // bottom right
g[3] = (int)inPixels[x + (y + 1) * width + 1]; // bottom left
// get the four blue values
int[] b = new int[4];
b[0] = (int)inPixels[x + y * width + 2]; // top left
b[1] = (int)inPixels[(x + 1) + y * width + 2]; // top right
b[2] = (int)inPixels[(x + 1) + (y + 1) * width + 2]; // bottom right
b[3] = (int)inPixels[x + (y + 1) * width + 2]; // bottom left
// get the four alpha values
int[] a = new int[4];
a[0] = (int)inPixels[x + y * width + 3]; // top left
a[1] = (int)inPixels[(x + 1) + y * width + 3]; // top right
a[2] = (int)inPixels[(x + 1) + (y + 1) * width + 3]; // bottom right
a[3] = (int)inPixels[x + (y + 1) * width + 3]; // bottom left
// the index in the new image, we divide by 2 because the image is half the size of the original image
int index = (x + y * width) / 2;
outPixels[index + 0] = (byte)((r[0] + r[1] + r[2] + r[3]) / 4);
outPixels[index + 1] = (byte)((g[0] + g[1] + g[2] + g[3]) / 4);
outPixels[index + 2] = (byte)((b[0] + b[1] + b[2] + b[3]) / 4);
outPixels[index + 3] = (byte)((a[0] + a[1] + a[2] + a[3]) / 4);
}
}
return outPixels;
}
I think the problem lies here:
inPixels[x + y * width + 0]
normally this runs correct when one array element is one pixel, only one element is one channel of one pixel. So each pixel starts a (x + (y * width)) * 4 so it should be something like this:
inPixels[((x + y * width) * 4) + 0]
I wrote some code for optimalization maybe you get some extra ideas, but i did not test it:
public static byte[] mipmap(byte[] inPixels, int width, int height)
{
// add one to width and height incase they are 0
byte[] outPixels = new byte[((width / 2) * (height / 2)) * 4];
// the offsets of the neighbor pixels (with *4 for the channel)
// this will automatically select a channel of a pixel one line down.
int[] neighborOffsets = new int[] { 0 * 4, 1 * 4, width * 4, (width + 1) * 4 };
// an 'offset for output buffer'
int outputOffset = 0;
// an 'offset for input buffer'
int inputOffset = 0;
for (int y = 0; y < height / 2; y++)
{
for (int x = 0; x < width / 2; x++)
{
// calculate the average of each channel
for (int channelIndex = 0; channelIndex < 4; channelIndex++)
{
int totalValue = 0;
for (int offset = 0; offset < 4; offset++)
totalValue = (int)inPixels[inputOffset + neighborOffsets[offset] + channelIndex];
// write it to the ouput buffer and increase the offset.
outPixels[outputOffset++] = (byte)(totalValue / 4);
}
// set the input offset on the next pixel. The next pixel is current + 2.
inputOffset += 2 * 4; // *4 for the channelcount (*4 will be optimized by the compiler)
}
inputOffset += width * 4; // skip an extra line. *4 for the channelcount (*4 will be optimized by the compiler)
}
return outPixels;
}
There maybe some little mistakes in it, but it probably run 4 times as fast. Try to avoid redundancy on multiplications.

Memory Object Allocation failure using c# and opencl

I am writing an image processing program with the express purpose to alter large images, the one I'm working with is 8165 pixels by 4915 pixels. I was told to implement gpu processing, so after some research I decided to go with OpenCL. I started implementing the OpenCL C# wrapper OpenCLTemplate.
My code takes in a bitmap and uses lockbits to lock its memory location. I then copy the order of each bit into an array, run the array through the openCL kernel, and it inverts each bit in the array. I then run the inverted bits back into the memory location of the image. I split this process into ten chunks so that i can increment a progress bar.
My code works perfectly with smaller images, but when I try to run it with my big image I keep getting a MemObjectAllocationFailure when trying to execute the kernel. I don't know why its doing this and i would appreciate any help in figuring out why or how to fix it.
using OpenCLTemplate;
public static void Invert(Bitmap image, ToolStripProgressBar progressBar)
{
string openCLInvert = #"
__kernel void Filter(__global uchar * Img0,
__global float * ImgF)
{
// Gets information about work-item
int x = get_global_id(0);
int y = get_global_id(1);
// Gets information about work size
int width = get_global_size(0);
int height = get_global_size(1);
int ind = 4 * (x + width * y );
// Inverts image colors
ImgF[ind]= 255.0f - (float)Img0[ind];
ImgF[1 + ind]= 255.0f - (float)Img0[1 + ind];
ImgF[2 + ind]= 255.0f - (float)Img0[2 + ind];
// Leave alpha component equal
ImgF[ind + 3] = (float)Img0[ind + 3];
}";
//Lock the image in memory and get image lock data
var imageData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
CLCalc.InitCL();
for (int i = 0; i < 10; i++)
{
unsafe
{
int adjustedHeight = (((i + 1) * imageData.Height) / 10) - ((i * imageData.Height) / 10);
int count = 0;
byte[] Data = new byte[(4 * imageData.Stride * adjustedHeight)];
var startPointer = (byte*)imageData.Scan0;
for (int y = ((i * imageData.Height) / 10); y < (((i + 1) * imageData.Height) / 10); y++)
{
for (int x = 0; x < imageData.Width; x++)
{
byte* Byte = (byte*)(startPointer + (y * imageData.Stride) + (x * 4));
Data[count] = *Byte;
Data[count + 1] = *(Byte + 1);
Data[count + 2] = *(Byte + 2);
Data[count + 3] = *(Byte + 3);
count += 4;
}
}
CLCalc.Program.Compile(openCLInvert);
CLCalc.Program.Kernel kernel = new CLCalc.Program.Kernel("Filter");
CLCalc.Program.Variable CLData = new CLCalc.Program.Variable(Data);
float[] imgProcessed = new float[Data.Length];
CLCalc.Program.Variable CLFiltered = new CLCalc.Program.Variable(imgProcessed);
CLCalc.Program.Variable[] args = new CLCalc.Program.Variable[] { CLData, CLFiltered };
kernel.Execute(args, new int[] { imageData.Width, adjustedHeight });
CLCalc.Program.Sync();
CLFiltered.ReadFromDeviceTo(imgProcessed);
count = 0;
for (int y = ((i * imageData.Height) / 10); y < (((i + 1) * imageData.Height) / 10); y++)
{
for (int x = 0; x < imageData.Width; x++)
{
byte* Byte = (byte*)(startPointer + (y * imageData.Stride) + (x * 4));
*Byte = (byte)imgProcessed[count];
*(Byte + 1) = (byte)imgProcessed[count + 1];
*(Byte + 2) = (byte)imgProcessed[count + 2];
*(Byte + 3) = (byte)imgProcessed[count + 3];
count += 4;
}
}
}
progressBar.Owner.Invoke((Action)progressBar.PerformStep);
}
//Unlock image
image.UnlockBits(imageData);
}
You may have reached a memory allocation limit of your OpenCL driver/device. Check the values returned by clGetDeviceInfo. There is a limit for the size of one single memory object. The OpenCL driver may allow the total size of all allocated memory objects to exceed the memory size on your device, and will copy them to/from host memory when needed.
To process large images, you may have to split them into smaller pieces, and process them separately.

Photo to grayscale in C#.NET and Windows Forms

How do I make this into a grayscale?
After much googling it seems the best I can do is change the tint of the picture (in the code bellow, it's a green tint). How can I do it?
byte[] redGreenBlueVal = new byte[numBytes];
for (int i = 0; i < redGreenBlueVal .Length; i += 4)
{
redGreenBlueVal [i + 0] = (byte)(.114 * redGreenBlueVal [i + 0]); --> blue
redGreenBlueVal [i + 1] = (byte)(.587 * redGreenBlueVal [i + 1]); --> green
redGreenBlueVal [i + 2] = (byte)(.299 * redGreenBlueVal [i + 2]); --> red
}
In effect you're adjusting HSB so this should get you a better grayscale image:
30% RED
59% GREEN
11% BLUE
byte[] redGreenBlueVal = new byte[numBytes];
for (int i = 0; i < redGreenBlueVal .Length; i += 4)
{
gray = (byte)(.11 * redGreenBlueVal [i + 0]);
gray += (byte)(.59 * redGreenBlueVal [i + 1]);
gray += (byte)(.3 * redGreenBlueVal [i + 2]);
redGreenBlueVal [i + 0] = gray;
redGreenBlueVal [i + 1] = gray;
redGreenBlueVal [i + 2] = gray;
}
You could try setting each pixels colour channels to the average value for that pixel.
i.e.
for( int i = 0; i < redGreenBlueVal.Length; += 4 )
{
int average = (redGreenBlueVal[i + 0] + redGreenBlueVal[i + 1] + redGreenBlueVal[i + 2])/3;
redGreenBlueVal[i + 0] = average;
redGreenBlueVal[i + 1] = average;
redGreenBlueVal[i + 2] = average;
}
To get a grayscale image, the basic idea is to have all the 3 channels hold the same value. There are many ways to do this, the simplest ones are:
use only one of the channels (it will look like when you select individual channels in photoshop)
calculate an average of the 3 channels

Categories