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.
Related
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.
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;
I have the following code:
if (source != null)
{
int count = 0;
int stride = (source.PixelWidth * source.Format.BitsPerPixel + 7) / 8;
byte[] pixels = new byte[source.PixelHeight * stride];
source.CopyPixels(pixels, stride, 0);
for (int y = 0; y < source.PixelHeight; y = y + 2)
{
for (int x = 0; x < source.PixelWidth; x = x + 2)
{
int index = y * stride + 4 * x;
count = index;
byte red = pixels[index];
byte green = pixels[index + 1];
byte blue = pixels[index + 2];
byte alpha = pixels[index + 3];
}
}
MessageBox.Show("Array Length, pixels: " + pixels.Count() + "," + count);
}
However, i am having an issue where certain bitmap images, when stepped through throw an exception
"System.IndexOutOfRangeException" as the index passes the pixel [ ] array count, does anyone know how to solve this efficiently without oversizing the array?
I want to display the progress as i go along hence the need for an accurate array :)
Thanks in advance.
You should be able to step through your code and figure out when index is larger than your pixels buffer size.
Add some debugging output to your code and step through it. Something like:
if (source != null)
{
int count = 0;
int stride = (source.PixelWidth * source.Format.BitsPerPixel + 7) / 8;
byte[] pixels = new byte[source.PixelHeight * stride];
source.CopyPixels(pixels, stride, 0);
for (int y = 0; y < source.PixelHeight; y = y + 2)
{
for (int x = 0; x < source.PixelWidth; x = x + 2)
{
int index = y * stride + 4 * x;
count = index;
int bufsize = source.PixelHeight * stride;
System.Diagnostics.Debug.WriteLine($"bufsize={bufsize}, index={index}, x={x}, y={y}");
System.Diagnostics.Debug.Assert((index+3) <= bufsize);
byte red = pixels[index];
byte green = pixels[index + 1];
byte blue = pixels[index + 2];
byte alpha = pixels[index + 3];
}
}
MessageBox.Show("Array Length, pixels: " + pixels.Count() + "," + count);
}
A big part about writing code is learning how to debug, use the debugger, and to verify the correctness of your algorithms. Good luck with your project.
This code will crash on any image where Format.BitsPerPixel is less than 32 e.g. 24-bit RGB with no alpha. You also shouldn't assume that stride is what you think it is, you should use the value returned from LockBits.
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.
Can you guys help me converting this C# code to Objective-C?
I don't have a clue about C#/Visual Studio!
public static class BezierSpline
{
public static void GetCurveControlPoints(Point[] knots,
out Point[] firstControlPoints, out Point[] secondControlPoints)
{
int n = knots.Length - 1;
// Calculate first Bezier control points
// Right hand side vector
double[] rhs = new double[n];
// Set right hand side X values
for (int i = 1; i < n - 1; ++i)
rhs[i] = 4 * knots[i].X + 2 * knots[i + 1].X;
rhs[0] = knots[0].X + 2 * knots[1].X;
rhs[n - 1] = (8 * knots[n - 1].X + knots[n].X) / 2.0;
// Get first control points X-values
double[] x = GetFirstControlPoints(rhs);
// Set right hand side Y values
for (int i = 1; i < n - 1; ++i)
rhs[i] = 4 * knots[i].Y + 2 * knots[i + 1].Y;
rhs[0] = knots[0].Y + 2 * knots[1].Y;
rhs[n - 1] = (8 * knots[n - 1].Y + knots[n].Y) / 2.0;
// Get first control points Y-values
double[] y = GetFirstControlPoints(rhs);
// Fill output arrays.
firstControlPoints = new Point[n];
secondControlPoints = new Point[n];
for (int i = 0; i < n; ++i)
{
// First control point
firstControlPoints[i] = new Point(x[i], y[i]);
// Second control point
if (i < n - 1)
secondControlPoints[i] = new Point(2 * knots
[i + 1].X - x[i + 1], 2 *
knots[i + 1].Y - y[i + 1]);
else
secondControlPoints[i] = new Point((knots
[n].X + x[n - 1]) / 2,
(knots[n].Y + y[n - 1]) / 2);
}
}
private static double[] GetFirstControlPoints(double[] rhs)
{
int n = rhs.Length;
double[] x = new double[n]; // Solution vector.
double[] tmp = new double[n]; // Temp workspace.
double b = 2.0;
x[0] = rhs[0] / b;
for (int i = 1; i < n; i++) // Decomposition and forward substitution.
{
tmp[i] = 1 / b;
b = (i < n - 1 ? 4.0 : 3.5) - tmp[i];
x[i] = (rhs[i] - x[i - 1]) / b;
}
for (int i = 1; i < n; i++)
x[n - i - 1] -= tmp[n - i] * x[n - i]; // Backsubstitution.
return x;
}
}
thanks.
double[] tmp = new double[n];
tmp is an array of length n. Each value is not initialized explicitly, but it is implicitly set to the default value of the double type, which is 0. So tmp is an n length array of zeros. {0,0,0,0,0, ... 0}