Imager Sharpening Kernel producing odd result C# - c#

I'm making a small app, where you can sharpen or blur an image in C#. So far blur is working flawlessly with any kernels of all sizes. However sharpening seems to cause this weird noisy/static image (something you'd see on a TV with no signal > see below). I've recoded this multiple times, and this time I tried to copy a solution from python, in case I keep messing it up somehow. Here's what I have for sharpening:
Color[,] clone = ud.CurrentSelected._bitmap; //gets the current image as a matrix
int[,] kernel = {{0, -1, 0}, {-1, 5, -1}, {0, -1, 0}};
int offset = 1;
int size = 100; //Image is 100x100 atm
for (int x = 0; x < size - offset; x++) {
for (int y = 0; y < size - offset; y++) {
NonDisplayedColour acc = Color.Black; //custom struct for storing and casting to regular Color without bounds -> not for displaying
for (int A = 0; A < 3; A++) {
for (int B = 0; B < 3; B++) {
int deltaX = x + A - offset;
int deltaY = y + B - offset;
NonDisplayedColour pixel = ud.CurrentSelected._bitmap[deltaX, deltaY];
int r = pixel.R * kernel[A, B];
int g = pixel.G * kernel[A, B];
int b = pixel.B * kernel[A, B];
acc.R += r;
acc.G += g;
acc.B += b;
}
}
clone[x,y] = acc;
}
}
ud.CurrentSelected._bitmap = clone;
This is just the calculation part, what displays the image is this:
Bitmap b = new Bitmap(ud.CurrentSelected._bitmap.Width, ud.CurrentSelected._bitmap.Height);
for (int x = 0; x < b.Width; ++x) {
for (int y = 0; y < b.Height; ++y) {
b.SetPixel(x, y, ud.CurrentSelected._bitmap[x, y]);
}
}
The NonDisplayedColour struct is just this, if anyone wants to know:
public struct NonDisplayedColour {
public NonDisplayedColour(int r, int g, int b) {
R = r;
G = g;
B = b;
A = 255;
}
public int A;
public int R;
public int G;
public int B;
public static implicit operator Color(NonDisplayedColour c) {
return Color.FromArgb((byte) c.A, (byte) c.R, (byte) c.G, (byte) c.B);
}
public static implicit operator NonDisplayedColour(Color c) {
return new NonDisplayedColour(c.R, c.G, c.B) {A = c.A};
}
}
The end result, is the following image.
Thanks for the help in advance.
PS.: I know locking bits would be much faster, but I'm going for simplicity rather than speed and SetPixel (to me at least) is much simpler.

Related

Converting a c# function into openCL which is basically c

I am trying to implement a blur-function I wrote in c#. I want it to run in an openCL Kernel
The function is as follows:
private static int cSharpBlur(double[,] blur, int width, int height, int[,] imageToInt, int[,] outputImage, int i, int j)
{
int maskSize = 1;
double sum = 0.0f;
// Collect neighbor values and multiply with gaussian
for (int a = -maskSize; a < maskSize + 1; a++)
{
for (int b = -maskSize; b < maskSize + 1; b++)
{
sum += blur[a+1, b+1] * imageToInt[Clamp(i+a,0,width-1), Clamp(j+b,0,height-1)];
}
}
byte[] values = BitConverter.GetBytes(imageToInt[i,j]);
int alpha = values[3];
int alphasum = alpha * (int)sum;
values[3] = (byte)alphasum;
int newValue = BitConverter.ToInt32(values,0);
return newValue;
}
Now I obviously don't have .GetBytes and BitConverter.ToInt32 in openCL.
Neither do I have a 2 dimensional array.
I solved this via
__kernel void gaussianBlur(__global int* imageToInt, int width, int height, __global double* blurBuffer, int blurBufferSize, __global int* outputBuffer){
int col = get_global_id(0);
int row = get_global_id(1);
double sum = 0.0f;
for (int a = -1; a < 2; a++)
{
for (int b = -1; b < 2; b++)
{
sum += blurBuffer[a+b+2] * imageToInt[col+width*row];
}
}
outputBuffer[col + width * row] = sum;
What's missing is the entire getBytes and ToInt stuff.
How can I do that in openCL?
Thanks in advance and have a great weekend!
For getBytes, use as_uchar4:
uchar4 values = as_uchar4(imageToInt[col+width*row]);
and for ToInt32, use as_int:
int newValue = as_int(values);
So your kernel should look something like this:
__kernel void gaussianBlur(__global int* imageToInt, int width, int height, __global double* blurBuffer, int blurBufferSize, __global int* outputBuffer) {
int col = get_global_id(0); int row = get_global_id(1);
int maskSize = 1;
double sum = 0.0f;
for(int a = -maskSize; a < maskSize + 1; a++) { // Collect neighbor values and multiply with gaussian
for(int b = -maskSize; b < maskSize + 1; b++) {
sum += blurBuffer[a+1+(b+1)*2*maskSize] * imageToInt[col+width*row];
}
}
uchar4 values = as_uchar4(imageToInt[col+width*row]);
int alpha = (int)values.s3;
int alphasum = alpha * (int)sum;
values.s3 = (uchar)alphasum;
int newValue = as_int(values);
outputBuffer[col+width*row] = newValue;
}
These as_... functions come built-in with OpenCL C, and enable you to reinterpret the bits that make up a number, as long as the total number of bits remains the same. In your case, an int is made up o 4 bytes, just like the uchar4 vector data type. With uchar4, you can address the individual bytes with .s1, .s1, .s2, .s3, or with .x, .y, .z, .w.
The as_... functions can also be used to get the individual bits of a float number for example:
float x = 1.0f;
int bits = as_int(x);
In plain C, you can also manually write such a function:
uint as_int(const float x) {
return *(int*)&x;
}
OpenCL C comes packed with math functionality, more than most other languages. All of the built-in functions are listed in this super helpful reference card.

C# find image within image with tolerance

I have a function which will find a smaller image within a larger image and return me it's position. I want to set a tolerance threshold so that even if a similar (but not exactly the same) is present, it returns it's position as well.
If possible I would also want it to work if the smaller image is rotated.
I tried finding edges in the image using OpenCV, but the edges in the haystack and those in the needle image are never the exact same and it never matches.
public Point? Find(Bitmap haystack, Bitmap needle)
{
if (null == haystack || null == needle)
{
return null;
}
if (haystack.Width < needle.Width || haystack.Height < needle.Height)
{
return null;
}
var haystackArray = GetPixelArray(haystack);
var needleArray = GetPixelArray(needle);
foreach (var firstLineMatchPoint in FindMatch(haystackArray.Take(haystack.Height - needle.Height), needleArray[0]))
{
if (IsNeedlePresentAtLocation(haystackArray, needleArray, firstLineMatchPoint, 1))
{
return firstLineMatchPoint;
}
}
return null;
}
private int[][] GetPixelArray(Bitmap bitmap)
{
var result = new int[bitmap.Height][];
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
for (int y = 0; y < bitmap.Height; ++y)
{
result[y] = new int[bitmap.Width];
Marshal.Copy(bitmapData.Scan0 + y*bitmapData.Stride, result[y], 0, result[y].Length);
}
bitmap.UnlockBits(bitmapData);
return result;
}
private IEnumerable<Point> FindMatch(IEnumerable<int[]> haystackLines, int[] needleLine)
{
var y = 0;
foreach (var haystackLine in haystackLines)
{
for (int x = 0, n = haystackLine.Length - needleLine.Length; x < n; ++x)
{
if (ContainSameElements(haystackLine, x, needleLine, 0, needleLine.Length))
{
yield return new Point(x, y);
}
}
y += 1;
}
}
private bool ContainSameElements(int[] first, int firstStart, int[] second, int secondStart, int length)
{
for (int i = 0; i < length; ++i)
{
if (first[i + firstStart] != second[i + secondStart])
{
return false;
}
}
return true;
}
private bool IsNeedlePresentAtLocation(int[][] haystack, int[][] needle, Point point, int alreadyVerified)
{
//we already know that "alreadyVerified" lines already match, so skip them
for (int y = alreadyVerified; y < needle.Length; ++y)
{
if ( ! ContainSameElements(haystack[y + point.Y], point.X, needle[y], 0, needle.Length))
{
return false;
}
}
return true;
}
How can I achieve this ?
For the first: You have to define your metric for comparing two pixels
I can imagine a distance in the cube defined by RGB, or a distance in the cylinder of HSV (HSV should be more accurate)
Example:
static double GetMetric(Pixel a, Pixel b)
{
double dR = a.R - b.R;
double dG = a.G - b.G;
double dB = a.B - b.B;
return Math.Sqrt(dR * dR + dG * dG + dB * dB);
}
Then just create a window search algorithm. Create window in the haste (same size as needle). Then try every possible position of window and calculate window distance as the sum of pixel distances.
You do not recalculate whole window. While moving window to the right, just recalculate and subtract left column (the one which was removed) and calculate and add right (new) column.
Then you need to remember minimal distance and its location and just compare it.
The result is the closest window to the needle. (Depends on used metric).
A simplified example:
static void Find(Bitmap Haste, Bitmap Needle)
{
//Simplified for Haste.Height = Needle.Height
//But Haste.Width > Needle.Width
int minX = 0;
int minY = 0;
double minMetric = double.MaxValue;
//Setup first window
double actualMetric = 0;
for (int i = 0; i < Needle.Width; i++)
{
for (int j = 0; j < Needle.Height; j++)
{
actualMetric += GetMatric(Needle.GetPixel(i, j), Haste.GetPixel(i, j));
}
}
minMetric = actualMetric;
//Move window to the right
for (int i = 0; i < Haste.Width - Needle.Width; i++)
{
for (int j = 0; j < Needle.Height; j++)
{
//Subtract left column pixel
actualMetric -= GetMatric(Needle.GetPixel(i, j), Haste.GetPixel(i, j));
//Add right column pixel
actualMetric += GetMatric(Needle.GetPixel(i + Needle.Width, j), Haste.GetPixel(i + Needle.Width, j));
}
//Compare
if(actualMetric < minMetric)
{
minX = i;
minY = 0; // Because Y is fixed while simplification Haste and Needle Height
}
}
}

Sharpen operation giving an output with incorrect contrast

Take a look at this source code. A 3x3 Sharpen filter with the following kernel
0 -1 0
-1 5 -1
0 -1 0
gives the following output:
I tried to replicate the outcome using my own code which is pretty much straight forward:
public partial class FilterForm : Form
{
public FilterForm()
{
InitializeComponent();
Bitmap image = (Bitmap)Bitmap.FromFile("lena.jpg");
InputPictureBox.Image = image;
double[,] dImage = ToDouble2d(image);
double[,] dMask = { { 0,-1, 0, },
{ -1, 5, -1, },
{ 0,-1, 0, }, };
double[,]dConv = LinearConvolutionSpatial(dImage, dMask);
Bitmap conv = ToBitmap2d(dConv, PixelFormat.Format32bppArgb);
OutputPictureBox.Image = conv;
}
public double[,] ToDouble2d(Bitmap input)
{
int width = input.Width;
int height = input.Height;
double[,] array2d = new double[width, height];
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
Color cl = input.GetPixel(x, y);
double gray = ((cl.R * 0.3) + (cl.G * 0.59) + (cl.B * 0.11));
array2d[x, y] = gray / 255.0;
}
}
return array2d;
}
public Bitmap ToBitmap2d(double[,] image, PixelFormat pixelFormat)
{
int Width = image.GetLength(0);
int Height = image.GetLength(1);
Bitmap bmp = new Bitmap(Width, Height, pixelFormat);
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
int i = (int)(image[x, y] * 255.0);
if (i > 255) i = 255;
if (i < 0) i = 0;
Color clr = Color.FromArgb(i, i, i);
bmp.SetPixel(x, y, clr);
}
}
return bmp;
}
private double[,] LinearConvolutionSpatial(double[,] paddedImage, double[,] mask)
{
int paddedImageWidth = paddedImage.GetLength(0);
int paddedImageHeight = paddedImage.GetLength(1);
int maskWidth = mask.GetLength(0);
int maskHeight = mask.GetLength(1);
int imageWidth = paddedImageWidth - maskWidth;
int imageHeight = paddedImageHeight - maskHeight;
double[,] convolve = new double[imageWidth, imageHeight];
for (int y = 0; y < imageHeight; y++)
{
for (int x = 0; x < imageWidth; x++)
{
double sum = Sum(paddedImage, mask, x, y);
int xxx = x;
int yyy = y;
convolve[xxx, yyy] = sum;
string str = string.Empty;
}
}
Rescale(convolve);
return convolve;
}
double Sum(double[,] paddedImage1, double[,] mask1, int startX, int startY)
{
double sum = 0;
int maskWidth = mask1.GetLength(0);
int maskHeight = mask1.GetLength(1);
for (int y = startY; y < (startY + maskHeight); y++)
{
for (int x = startX; x < (startX + maskWidth); x++)
{
double img = paddedImage1[x, y];
double msk = mask1[maskWidth - x + startX - 1, maskHeight - y + startY - 1];
sum = sum + (img * msk);
}
}
return sum;
}
void Rescale(double[,] convolve)
{
int imageWidth = convolve.GetLength(0);
int imageHeight = convolve.GetLength(1);
double minAmp = 0.0;
double maxAmp = 0.0;
for (int j = 0; j < imageHeight; j++)
{
for (int i = 0; i < imageWidth; i++)
{
minAmp = Math.Min(minAmp, convolve[i, j]);
maxAmp = Math.Max(maxAmp, convolve[i, j]);
}
}
double scale = 1 / (Math.Abs(minAmp) + maxAmp);
for (int j = 0; j < imageHeight; j++)
{
for (int i = 0; i < imageWidth; i++)
{
double d = (convolve[i, j] + Math.Abs(minAmp)) * scale;
convolve[i, j] = d;
}
}
}
}
But, the problem I am facing is, my output has a different contrast.
What points I should work on?
Some of the above code has a min/max effect and balances the histogram.
Remove the normalizing bit from this line:
array2d[x, y] = gray;// / 255.0;
I've removed the rescale multiplier of 255 in this piece:
public Bitmap ToBitmap2d(double[,] image, PixelFormat pixelFormat)
{
int Width = image.GetLength(0);
int Height = image.GetLength(1);
Bitmap bmp = new Bitmap(Width, Height, pixelFormat);
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
int i = (int)(image[x, y] * 1);
if (i > 255) i = 255;
if (i < 0) i = 0;
Color clr = Color.FromArgb(i, i, i);
bmp.SetPixel(x, y, clr);
}
}
return bmp;
}
And I've also removed the scaling factor from this function:
void Rescale(double[,] convolve)
{
int imageWidth = convolve.GetLength(0);
int imageHeight = convolve.GetLength(1);
double minAmp = 0.0;
double maxAmp = 255.0;
for (int j = 0; j < imageHeight; j++)
{
for (int i = 0; i < imageWidth; i++)
{
minAmp = Math.Min(minAmp, convolve[i, j]);
maxAmp = Math.Max(maxAmp, convolve[i, j]);
}
}
double scale = 1 / (Math.Abs(minAmp) + maxAmp);
for (int j = 0; j < imageHeight; j++)
{
for (int i = 0; i < imageWidth; i++)
{
double d = (convolve[i, j]);// + Math.Abs(minAmp)) * scale;
convolve[i, j] = d;
}
}
}
Appears to work as intended after those changes are made, in this case.
I have found the solution from this link. The main clue was to introduce an offset and a factor.
factor is the sum of all values in the kernel.
offset is an arbitrary value to fix the output further.
The following source code is supplied in the given link:
private void SafeImageConvolution(Bitmap image, ConvMatrix fmat)
{
//Avoid division by 0
if (fmat.Factor == 0)
return;
Bitmap srcImage = (Bitmap)image.Clone();
int x, y, filterx, filtery;
int s = fmat.Size / 2;
int r, g, b;
Color tempPix;
for (y = s; y < srcImage.Height - s; y++)
{
for (x = s; x < srcImage.Width - s; x++)
{
r = g = b = 0;
// Convolution
for (filtery = 0; filtery < fmat.Size; filtery++)
{
for (filterx = 0; filterx < fmat.Size; filterx++)
{
tempPix = srcImage.GetPixel(x + filterx - s, y + filtery - s);
r += fmat.Matrix[filtery, filterx] * tempPix.R;
g += fmat.Matrix[filtery, filterx] * tempPix.G;
b += fmat.Matrix[filtery, filterx] * tempPix.B;
}
}
r = Math.Min(Math.Max((r / fmat.Factor) + fmat.Offset, 0), 255);
g = Math.Min(Math.Max((g / fmat.Factor) + fmat.Offset, 0), 255);
b = Math.Min(Math.Max((b / fmat.Factor) + fmat.Offset, 0), 255);
image.SetPixel(x, y, Color.FromArgb(r, g, b));
}
}
}

Fast inversion of an axis of 3D data stored in a 1D array

I have a large dataset that represents a 3D image (approx 100,000,000 pixels). I want to invert the pixels along the 'z' axis of the image. My data is stored in a byte array where the data is ordered x, y, z (i.e [] = { (x=0,y,z=0), (x=1,y=0,z=0), (x=2,y=0,z=0) ...)
I can easily sort them using the following code, however I am looking to reduce computation time where possible (currently reporting around 7 seconds). I am considering using an array 'sort' function, but am not sure how to handle the indexing.
Here is my current code:
private int GetIndex(Image _image, int _x, int _y, int _z)
{
return (_z * _image.Size.X * _image.Size.Y) + (_y * _image.Size.X) + _x;;
}
private void InvertZ(Image _image)
{
for (int z = 0; z < _image.Size.Z/2; z++)
{
for (int y = 0; y < _image.Size.Y; y++)
{
for (int x = 0; x < _image.Size.X; x++)
{
int srcIndex = GetIndex(_image, x, y, z);
int destIndex = GetIndex(_image, x, y, _image.Size.Z - z - 1);
byte src = _image.Buffer[srcIndex];
byte dest = _image.Buffer[destIndex];
_image.Buffer[srcIndex] = dest;
_image.Buffer[destIndex] = src;
}
}
}
}
One solution is to copy each frame. Reduces the number of iterations drastically.
private void InvertZ(Image _image)
{
int frameSize = _image.Size.X * _image.Size.Y;
byte[] temp = new byte[frameSize];
for (int z = 0; z < _image.Size.Z / 2; z++)
{
int inverseZ = _image.Size.Z - z - 1;
Array.Copy(_image.Buffer, z * frameSize, temp, 0, frameSize);
Array.Copy(_image.Buffer, inverseZ * frameSize, _image.Buffer, z * frameSize, frameSize);
Array.Copy(temp, 0, _image.Buffer, inverseZ * frameSize, frameSize);
}
}
Runtime approx < 18 ms compared to 3175 ms.
This might be faster that your one loop solution even if it uses several loops, one for each axis.
There are 2 major speed enhancements (very common algorithm enhancements within image processing).
The Image is converted to a array for quick legwork
The index become partly precomputed within the the loops making sure the you calculate as much as possible as few times as possible. (no more use of the built-in thing in Image which is nice for one and another pixel but not suitable for the whole map)
I usually only do this work on 2d so the z axis is just added. it might be faster having it as the last loop. (fringe example running in 2d if y is before x then you loose about 30-40% speed because of the way memory/cache is built Src this happens at 3d as well (that's why I placed the z (levels/pages at the front))
Here is the code I would base my solution on.
private void InvertZ(Image _image)
{
byte[] array =imageToByteArray(_image);
int pageSize = _Image.Size.Y * _Image.Size.X;
for (int z = 0; z < _image.Size.Z/2; z++)
{
int level = z * pageSize;
int dstLevel = (_image.Size.Z - z - 1) * pageSize;
for (int x = 0; x < _image.Size.X; x++)
{
int Row = x*_Image.Size.Y;
int RowOnLevel = level + Row ;
int dstRowOnLevel = dstLevel + xRow;
for (int y = 0; y < _image.Size.Y; y++)
{
int srcIndex = RowOnLevel + y;
int destIndex = dstRowOnLevel + y;
byte tmpDest = array[destIndex];
array[destIndex] = array[srcIndex];
array[srcIndex] = tmpDest;
}
}
}
return byteArrayToImage(array);
}
public Image byteArrayToImage(byte[] byteArrayIn)
{
MemoryStream ms = new MemoryStream(byteArrayIn);
Image returnImage = Image.FromStream(ms);
return returnImage;
}
public byte[] imageToByteArray(System.Drawing.Image imageIn)
{
MemoryStream ms = new MemoryStream();
imageIn.Save(ms,System.Drawing.Imaging.ImageFormat.Gif);
return ms.ToArray();
}
You can use the fact that the array layout is like this
Z Length
==== ======
[0] x * y
[1] x * y
[2] x * y
...
[z-1] x * y
This allows us to greatly reduce index calculations. We can use a variation of the classic O(N) reverse algorithm like this
static void InvertZ(Image _image)
{
int len = _image.Size.X * _image.Size.Y;
for (int lo = 0, hi = (_image.Size.Z - 1) * len; lo < hi; lo += len, hi -= len)
for (int i = 0; i < len; i++)
Swap(ref _image.Buffer[lo + i], ref _image.Buffer[hi + i]);
}
static void Swap<T>(ref T a, ref T b) { var c = a; a = b; b = c; }

mandlebrot fractal error in the plotted function

Hello guys i am trying plotting the mandlebrot fractal but the result is very far from it, can you help me to find the why?
Here is the code:
void Button1Click(object sender, EventArgs e)
{
Graphics g = pbx.CreateGraphics();
Pen p = new Pen(Color.Black);
double xmin = -2.0;
double ymin = -1.2;
double xmax = 0;
double ymax = 0;
double x = xmin, y = ymin;
int MAX = 1000;
double stepY = Math.Abs(ymin - ymax)/(pbx.Height);
double stepX = Math.Abs(xmin - xmax)/(pbx.Width);
for(int i = 0; i < pbx.Width; i++)
{
y = ymin;
for(int j = 0; j < pbx.Height; j++)
{
double rez = x;
double imz = y;
int iter = 0;
while(rez * rez + imz * imz <= 4 && iter < MAX)
{
rez = rez * rez - imz * imz + x;
imz = 2 * rez * imz + y;
iter++;
}
if(iter == MAX)
{
p.Color = Color.Black;
g.DrawRectangle(p, i, j, 1, 1);
}
else
{
p.Color = Color.White;
g.DrawRectangle(p, i, j, 1, 1);
}
y += stepY;
}
x += stepX;
}
}
please help me my mind is getting crushed thinking how to get the beautiful mandlebrot set...
and sorry if i committed some mistakes but English is not my speaked language!
You have some irregularities elsewhere. The range you're plotting isn't the entire set, and I would calculate x and y directly for each pixel, rather than using increments (so as to avoid rounding error accumulating).
But it looks to me as though your main error is in the iterative computation. You are modifying the rez variable before you use it in the computation of the new imz value. Your loop should look more like this:
while(rez * rez + imz * imz <= 4 && iter < MAX)
{
double rT = rez * rez - imz * imz + x;
imz = 2 * rez * imz + y;
rez = rT;
iter++;
}
Additionally to Peters answer, you should use a color palette instead of drawing just black and white pixels.
Create a array of colors, like this: (very simple example)
Color[] colors = new Colors[768];
for (int i=0; i<256; i++) {
colors[i ]=Color.FromArgb( i, 0, 0);
colors[i+256]=Color.FromArgb(255-i, i, 0);
colors[i+512]=Color.FromArgb(0 , 255-i, i);
}
Then use the iter value to pull a color and draw it:
int index=(int)((double)iter/MAX*767);
p.Color c = colors[index];
g.DrawRectangle(p, i, j, 1, 1);
Replace the entire if (iter == MAX) ... else ... statement with this last step.

Categories