RGB to Ycbcr conversion - c#

I am doing image processing and I have a 3D array with the rgb values of an image and I am trying to convery those values into ycbcr ( I made a copy of the rgb array and called it ycbcr, and
public static void rgb2ycbcr(System.Drawing.Bitmap bmp, ref byte[, ,] arrayrgb, ref byte[, ,] arrayycbcr)
{
byte Y;
byte Cb;
byte Cr;
for (int i = 1; i < (bmp.Height + 1); i++) //don't worry about bmp.height/width+2 its for my project
{
for (int j = 1; j < (bmp.Width + 1); j++)
{
byte R = arrayrgb[i, j, 0];
byte G = arrayrgb[i, j, 1];
byte B = arrayrgb[i, j, 2];
Y = (byte)((0.257 * R) + (0.504 * G) + (0.098 * B) + 16);
Cb = (byte)(-(0.148 * R) - (0.291 * G) + (0.439 * B) + 128);
Cr = (byte)((0.439 * R) - (0.368 * G) - (0.071 * B) + 128);
arrayycbcr[i, j, 0] = Y;
arrayycbcr[i, j, 1] = Cb;
arrayycbcr[i, j, 2] = Cr;
}
}
}
the problem is I am not getting the same values for ycbcr as I would get in matlab when I use rgb2ycbcr, is there something missing in my code?

Faster and acurate code.
Output values ​​between 0 and 255 (JPG formula)
width = bmp.Width;
height = bmp.Height;
yData = new byte[width, height]; //luma
bData = new byte[width, height]; //Cb
rData = new byte[width, height]; //Cr
unsafe
{
BitmapData bitmapData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, bmp.PixelFormat);
int heightInPixels = bitmapData.Height;
int widthInBytes = width * 3;
byte* ptrFirstPixel = (byte*)bitmapData.Scan0;
//Convert to YCbCr
for (int y = 0; y < heightInPixels; y++)
{
byte* currentLine = ptrFirstPixel + (y * bitmapData.Stride);
for (int x = 0; x < width; x++)
{
int xPor3 = x * 3;
float blue = currentLine[xPor3++];
float green = currentLine[xPor3++];
float red = currentLine[xPor3];
yData[x, y] = (byte)((0.299 * red) + (0.587 * green) + (0.114 * blue));
bData[x, y] = (byte)(128 - (0.168736 * red) + (0.331264 * green) + (0.5 * blue));
rData[x, y] = (byte)(128 + (0.5 * red) + (0.418688 * green) + (0.081312 * blue));
}
}
bmp.UnlockBits(bitmapData);
}

Convert R/G/B to uint before assign.
uint R =ConvertToUint(arrayrgb[i, j, 0]);
uint G =ConvertToUint(arrayrgb[i, j, 1]);
uint B =ConvertToUint(arrayrgb[i, j, 2]);

Related

Convert from Yuv 420 to RGB and then to Bitmap

I'm having an issue converting image from byte YUV420p[] to byte RGB[] and then to a Bitmap.
This is method to convert from YUV to RGB that I'm using:
double[,] YUV2RGB_CONVERT_MATRIX = new double[3, 3]
{
{ 1, 0, 1.4022 },
{ 1, -0.3456, -0.7145 },
{ 1, 1.771, 0 }
};
unsafe Bitmap ConvertYUV2RGB(byte[] YUVFrame, int width, int height)
{
int uIndex = width * height;
int vIndex = uIndex + ((width * height) >> 2);
int gIndex = width * height;
int bIndex = gIndex * 2;
byte[] rgbFrame = new byte[uIndex * 3];
//图片为pic1,RGB颜色的二进制数据转换得的int r,g,b;
Bitmap bitmap = new Bitmap(width, height);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// R分量
int temp = (int)(YUVFrame[y * width + x] + (YUVFrame[vIndex + (y / 2) * (width / 2) + x / 2] - 128) * YUV2RGB_CONVERT_MATRIX[0, 2]);
rgbFrame[y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));
// G分量
temp = (int)(YUVFrame[y * width + x] + (YUVFrame[uIndex + (y / 2) * (width / 2) + x / 2] - 128) * YUV2RGB_CONVERT_MATRIX[1, 1] + (YUVFrame[vIndex + (y / 2) * (width / 2) + x / 2] - 128) * YUV2RGB_CONVERT_MATRIX[1, 2]);
rgbFrame[gIndex + y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));
// B分量
temp = (int)(YUVFrame[y * width + x] + (YUVFrame[uIndex + (y / 2) * (width / 2) + x / 2] - 128) * YUV2RGB_CONVERT_MATRIX[2, 1]);
rgbFrame[bIndex + y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));
System.Drawing.Color c = System.Drawing.Color.FromArgb(RGBFrame[y * width + x], RGBFrame[gIndex + y * width + x], RGBFrame[bIndex + y * width + x]);
bitmap.SetPixel(x, y, c);
}
}
return bitmap;
}
That function works 100% but it is very slow for obvious reasons:
System.Drawing.Color c = System.Drawing.Color.FromArgb(RGBFrame[y * width + x], RGBFrame[gIndex + y * width + x], RGBFrame[bIndex + y * width + x]);
bitmap.SetPixel(x, y, c);
Here's the generated image:
So, to avoid calling bitmap.SetPixel(x, y, c) inside the loop I changed the code to:
unsafe Bitmap ConvertYUV2RGB(byte[] YUVFrame, int width, int height)
{
int uIndex = width * height;
int vIndex = uIndex + ((width * height) >> 2);
int gIndex = width * height;
int bIndex = gIndex * 2;
byte[] RGBFrame = new byte[uIndex * 3];
//图片为pic1,RGB颜色的二进制数据转换得的int r,g,b;
//Bitmap bitmap = new Bitmap(width, height);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// R分量
int temp = (int)(YUVFrame[y * width + x] + (YUVFrame[vIndex + (y / 2) * (width / 2) + x / 2] - 128) * YUV2RGB_CONVERT_MATRIX[0, 2]);
RGBFrame[y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));
// G分量
temp = (int)(YUVFrame[y * width + x] + (YUVFrame[uIndex + (y / 2) * (width / 2) + x / 2] - 128) * YUV2RGB_CONVERT_MATRIX[1, 1] + (YUVFrame[vIndex + (y / 2) * (width / 2) + x / 2] - 128) * YUV2RGB_CONVERT_MATRIX[1, 2]);
RGBFrame[gIndex + y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));
// B分量
temp = (int)(YUVFrame[y * width + x] + (YUVFrame[uIndex + (y / 2) * (width / 2) + x / 2] - 128) * YUV2RGB_CONVERT_MATRIX[2, 1]);
RGBFrame[bIndex + y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));
// Commented to avoid calling functions from inside the for loop
// System.Drawing.Color c = System.Drawing.Color.FromArgb(RGBFrame[y * width + x], RGBFrame[gIndex + y * width + x], RGBFrame[bIndex + y * width + x]);
// bitmap.SetPixel(x, y, c);
}
}
return CreateBitmap(RGBFrame, width, height);
}
private Bitmap CreateBitmap(byte[] RGBFrame, int width, int height)
{
PixelFormat pxFormat = PixelFormat.Format24bppRgb;
Bitmap bmp = new Bitmap(width, height, pxFormat);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, pxFormat);
IntPtr pNative = bmpData.Scan0;
Marshal.Copy(RGBFrame, 0, pNative, RGBFrame.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
But I'm unable to create the image correctly. This is the result:
What is going on here?
I finally resolved my issue with this function:
unsafe Bitmap ConvertYUV2RGB(byte[] YUVFrame, int width, int height)
{
int numOfPixel = width * height;
int positionOfV = numOfPixel;
int positionOfU = numOfPixel / 4 + numOfPixel;
byte[] rgb = new byte[numOfPixel * 3];
int R = 0;
int G = 1;
int B = 2;
for (int i = 0; i < height; i++)
{
int startY = i * width;
int step = (i / 2) * (width / 2);
int startU = positionOfU + step;
int startV = positionOfV + step;
for (int j = 0; j < width; j++)
{
int Y = startY + j;
int U = startU + j / 2;
int V = startV + j / 2;
int index = Y * 3;
double r = ((YUVFrame[Y] & 0xff) + 1.4075 * ((YUVFrame[V] & 0xff) - 128));
double g = ((YUVFrame[Y] & 0xff) - 0.3455 * ((YUVFrame[U] & 0xff) - 128) - 0.7169 * ((YUVFrame[V] & 0xff) - 128));
double b = ((YUVFrame[Y] & 0xff) + 1.779 * ((YUVFrame[U] & 0xff) - 128));
r = (r < 0 ? 0 : r > 255 ? 255 : r);
g = (g < 0 ? 0 : g > 255 ? 255 : g);
b = (b < 0 ? 0 : b > 255 ? 255 : b);
rgb[index + R] = (byte)r;
rgb[index + G] = (byte)g;
rgb[index + B] = (byte)b;
}
}
return CreateBitmap(rgb, width, height);
}
And to create a Bitmap from RGB[]:
Bitmap CreateBitmap(byte[] RGBFrame, int width, int height)
{
PixelFormat pxFormat = PixelFormat.Format24bppRgb;
Bitmap bmp = new Bitmap(width, height, pxFormat);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, pxFormat);
IntPtr pNative = bmpData.Scan0;
Marshal.Copy(RGBFrame, 0, pNative, RGBFrame.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
Or a BitmapSource for WPF:
BitmapSource FromArray(byte[] data, int w, int h, int ch)
{
System.Windows.Media.PixelFormat format = PixelFormats.Default;
if (ch == 1)
format = PixelFormats.Gray8; //grey scale image 0-255
if (ch == 3)
format = PixelFormats.Bgr24; //RGB
if (ch == 4)
format = PixelFormats.Bgr32; //RGB + alpha
WriteableBitmap wbm = new WriteableBitmap(w, h, 96, 96, format, null);
wbm.WritePixels(new Int32Rect(0, 0, w, h), data, ch * w, 0);
return wbm;
}

Why does this edge detector not work correctly when bitmap width not divisible by 4?

Using the Sobel edge detector code below I find that the output bitmap has a diagonal line of zero values superimposed over detected edges if the input bitmap has a width not divisible by 4. The red square marked in the output bitmap at co-ords (80,80) is broken up and incorrectly placed in this case. Why is this and how can I make the code work with any bitmap width?
private Bitmap SobelEdgeDetect2(Bitmap original, byte Threshold = 128)
{
// https://stackoverflow.com/questions/16747257/edge-detection-with-lockbits-c-sharp
int width = original.Width;
int height = original.Height;
int BitsPerPixel = Image.GetPixelFormatSize(original.PixelFormat);
int OneColorBits = BitsPerPixel / 8;
BitmapData bmpData = original.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, original.PixelFormat);
int position;
int[,] gx = new int[,] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } };
int[,] gy = new int[,] { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } };
Bitmap dstBmp = new Bitmap(width, height, original.PixelFormat);
BitmapData dstData = dstBmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, dstBmp.PixelFormat);
int byteCount = dstData.Stride * dstBmp.Height;
byte[] input = new byte[byteCount];
byte[] processed = new byte[byteCount];
IntPtr ptr = bmpData.Scan0;
IntPtr dst = dstData.Scan0;
Marshal.Copy(ptr, input, 0, input.Length);
Marshal.Copy(dst,processed, 0, input.Length);
int BlackPoints = 0;
int WhitePoints = 0;
for (int i = 1; i < height - 1; i++) // y
{
for (int j = 1; j < width - 1; j++) // x
{
int NewX = 0, NewY = 0;
for (int ii = 0; ii < 3; ii++)
{
for (int jj = 0; jj < 3; jj++)
{
int I = i + ii - 1;
int J = j + jj - 1;
byte Current = input[(I * (width) + J) * OneColorBits];
NewX += gx[ii, jj] * Current;
NewY += gy[ii, jj] * Current;
}
}
position = (i * (width) + j) * OneColorBits;
if (NewX * NewX + NewY * NewY > Threshold * Threshold)
{
processed[position] = 255;
processed[position + 1] = 255;
processed[position + 2] = 255;
WhitePoints++;
}
else
{
processed[position] = 0;
processed[position + 1] = 0;
processed[position + 2] = 0;
BlackPoints++;
}
if (j >= 78 && j <= 82 && i >= 78 && i <= 82)
{
processed[position] = 0;
processed[position + 1] = 0;
processed[position + 2] = 255;
}
}
}
Marshal.Copy(processed, 0, dst, input.Length);
dstBmp.UnlockBits(dstData);
return dstBmp;
}
For a 201 pixel wide bitmap, dstData.Stride was 604. For a 200 pixel wide bitmap dstData.Stride was 612, which explains why width had to be divisible by 4 for my code.
Replacing
position = (i * (width) + j) * OneColorBits;
by
position = i * dstData.Stride + j * OneColorBits;
and
byte Current = input[(I * (width) + J) * OneColorBits];
by
byte Current = input[I * dstData.Stride + J * OneColorBits];
fixed the problem.

Reading Pixels from an image byte array

I have a jpeg image. I save this bitmapdata to a byte array.
This jpeg has a width of 100 and a height of 100.
I want to extract an image of Rectanlge(10,10,20,20);
Obviously, I can interact through this byte array but I am unsure how to relate the x,y pixels of what I want to this byte array. I know that I have to use the stride and pixel size which is 4 as it is rgba.
I have this which was from this link cropping an area from BitmapData with C#.
Bitmap bmp = new Bitmap(_file);
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData rawOriginal = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
int origByteCount = rawOriginal.Stride * rawOriginal.Height;
byte[] origBytes = new Byte[origByteCount];
System.Runtime.InteropServices.Marshal.Copy(rawOriginal.Scan0, origBytes, 0, origByteCount);
int BPP = 4;
int width = 20;
int height = 20;
int startX = 10;
int startY = 10;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width * BPP; j += BPP)
{
int origIndex = (startX * rawOriginal.Stride) + (i * rawOriginal.Stride) + (startY * BPP) + (j);
int croppedIndex = (i * width * BPP) + (j);
//copy data: once for each channel
for (int k = 0; k < BPP; k++)
{
croppedBytes[croppedIndex + k] = origBytes[origIndex + k];
}
}
}
But this:
int origIndex = (startX * rawOriginal.Stride) + (i * rawOriginal.Stride) + (startY * BPP) + (j);
I found is incorrect.
Does anyone know what value I should set here please?
thanks
When you're working with bitmap data, there are 2 important things to keep in mind:
the BPP (bytes per pixel): which here is 4
the stride (number of bytes on one line of the image), which here would be 4 * width
so if you want to get the offset of a pixel, just use this formula:
int offset = x * BPP + y * stride;
if you want to extract only a part of your bitmap you could just do this:
int i = 0;
for(int y = startY; y < startY + height; y++)
{
for(int k = startX * bpp + y * stride; k < (startX + width) * bpp + y * stride; k++)
{
croppedBytes[i] = origBytes[k];
i++;
}
}
Stride is bytes per line (Y), you shouldn't multiply x at any point by Stride
int y = startY + i;
int x = startX;
int origIndex = y * rawOriginal.Stride + x * BPP;

Draw rotated sine image

I'm working with 2D Fourier transforms, and I have a method that will output the following result:
The code looks like this:
private Bitmap PaintSin(int s, int n)
{
Data = new int[Width, Height];
Bitmap buffer = new Bitmap(Width, Height);
double inner = (2 * Math.PI * s) / n;
BitmapData originalData = buffer.LockBits(
new Rectangle(0, 0, buffer.Width, buffer.Height),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
for (int i = 0; i < buffer.Width; i++)
{
for (int j = 0; j < buffer.Height; j++)
{
double val;
int c = 0;
if (j == 0)
{
val = Math.Sin(inner * i);
val += 1;
val *= 128;
val = val > 255 ? 255 : val;
c = (int)val;
Color col = Color.FromArgb(c, c, c);
SetPixel(originalData, i, j, col);
}
else
SetPixel(originalData, i, j, GetColor(originalData, i, 0));
Data[i, j] = c;
}
}
buffer.UnlockBits(originalData);
return buffer;
}
Now, I'm trying to think of a formula that will accept an angle and will out put an image where the solid lines are at the given angle.
For example, if I inputted 45 Degrees, the result would be:
Thank you for any help!
Here is the SetPixel code if that is needed:
private unsafe void SetPixel(BitmapData originalData, int x, int y, Color color)
{
//set the number of bytes per pixel
int pixelSize = 3;
//get the data from the original image
byte* oRow = (byte*)originalData.Scan0 + (y * originalData.Stride);
//set the new image's pixel to the grayscale version
oRow[x * pixelSize] = color.B; //B
oRow[x * pixelSize + 1] = color.G; //G
oRow[x * pixelSize + 2] = color.R; //R
}
This should be possible by using a rotated coordinate system. Transformation of i and j is as following:
x = i * cos(angle) - j * sin(angle);
y = j * cos(angle) + i * sin(angle);
Note: I'm not sure about degree/radians here, so adjust angle so it fits to the unit that cos and sin do need. Also, you might have to negate the angle depending on your desired rotation direction.
In fact, you only need x which replaces your use of i. We'll precompute sin(angle) and cos(angle) because these are quite costy operations we don't want in inner loops. Additionaly, your optimization is removed as we can't draw only one line and repeat it:
[...]
// double angle = ...
double cos_angle = cos(angle);
double sin_angle = sin(angle);
for (int i = 0; i < buffer.Width; i++)
{
for (int j = 0; j < buffer.Height; j++)
{
double val;
double x;
int c = 0;
x = i * cos_angle - j * sin_angle;
val = Math.Sin(inner * x);
val += 1;
val *= 128;
val = val > 255 ? 255 : val;
c = (int)val;
Color col = Color.FromArgb(c, c, c);
SetPixel(originalData, i, j, col);
Data[i, j] = c;
}
}
You can write a simple function rotate(x,y,angle) and use its result in SetPixel. You can google for rotation matrix.
A call with angle = 0, should produce default output.

Sharpen on a Bitmap using C#

I want to put a sharpen filter on an image. I have found a web with short tutorial.
I tried to do it in C# so here is my code. Anyway, I tried to find out why it is not working. I do not know if I am doing something wrong, if yes, please advise me what to do to make it work as it should be. Thanks
public static Bitmap sharpen(Bitmap image)
{
Bitmap sharpenImage = new Bitmap(image.Width, image.Height);
int filterWidth = 3;
int filterHeight = 3;
int w = image.Width;
int h = image.Height;
double[,] filter = new double[filterWidth, filterHeight];
filter[0, 0] = filter[0, 1] = filter[0, 2] = filter[1, 0] = filter[1, 2] = filter[2, 0] = filter[2, 1] = filter[2, 2] = -1;
filter[1, 1] = 9;
double factor = 1.0;
double bias = 0.0;
Color[,] result = new Color[image.Width, image.Height];
for (int x = 0; x < w; ++x)
{
for (int y = 0; y < h; ++y)
{
double red = 0.0, green = 0.0, blue = 0.0;
Color imageColor = image.GetPixel(x, y);
for (int filterX = 0; filterX < filterWidth; filterX++)
{
for (int filterY = 0; filterY < filterHeight; filterY++)
{
int imageX = (x - filterWidth / 2 + filterX + w) % w;
int imageY = (y - filterHeight / 2 + filterY + h) % h;
red += imageColor.R * filter[filterX, filterY];
green += imageColor.G * filter[filterX, filterY];
blue += imageColor.B * filter[filterX, filterY];
}
int r = Math.Min(Math.Max((int)(factor * red + bias), 0), 255);
int g = Math.Min(Math.Max((int)(factor * green + bias), 0), 255);
int b = Math.Min(Math.Max((int)(factor * blue + bias), 0), 255);
result[x, y] = Color.FromArgb(r, g, b);
}
}
}
for (int i = 0; i < w; ++i)
{
for (int j = 0; j < h; ++j)
{
sharpenImage.SetPixel(i, j, result[i, j]);
}
}
return sharpenImage;
}
public static Bitmap sharpen(Bitmap image)
{
Bitmap sharpenImage = new Bitmap(image.Width, image.Height);
int filterWidth = 3;
int filterHeight = 3;
int w = image.Width;
int h = image.Height;
double[,] filter = new double[filterWidth, filterHeight];
filter[0, 0] = filter[0, 1] = filter[0, 2] = filter[1, 0] = filter[1, 2] = filter[2, 0] = filter[2, 1] = filter[2, 2] = -1;
filter[1, 1] = 9;
double factor = 1.0;
double bias = 0.0;
Color[,] result = new Color[image.Width, image.Height];
for (int x = 0; x < w; ++x)
{
for (int y = 0; y < h; ++y)
{
double red = 0.0, green = 0.0, blue = 0.0;
//=====[REMOVE LINES]========================================================
// Color must be read per filter entry, not per image pixel.
Color imageColor = image.GetPixel(x, y);
//===========================================================================
for (int filterX = 0; filterX < filterWidth; filterX++)
{
for (int filterY = 0; filterY < filterHeight; filterY++)
{
int imageX = (x - filterWidth / 2 + filterX + w) % w;
int imageY = (y - filterHeight / 2 + filterY + h) % h;
//=====[INSERT LINES]========================================================
// Get the color here - once per fiter entry and image pixel.
Color imageColor = image.GetPixel(imageX, imageY);
//===========================================================================
red += imageColor.R * filter[filterX, filterY];
green += imageColor.G * filter[filterX, filterY];
blue += imageColor.B * filter[filterX, filterY];
}
int r = Math.Min(Math.Max((int)(factor * red + bias), 0), 255);
int g = Math.Min(Math.Max((int)(factor * green + bias), 0), 255);
int b = Math.Min(Math.Max((int)(factor * blue + bias), 0), 255);
result[x, y] = Color.FromArgb(r, g, b);
}
}
}
for (int i = 0; i < w; ++i)
{
for (int j = 0; j < h; ++j)
{
sharpenImage.SetPixel(i, j, result[i, j]);
}
}
return sharpenImage;
}
I took Daniel's answer and modified it for performance, by using BitmapData class, since using GetPixel/SetPixel is very expensive and inappropriate for performance-hungry systems. It works exactly the same as the previous solution and can be used instead.
public static Bitmap Sharpen(Bitmap image)
{
Bitmap sharpenImage = (Bitmap)image.Clone();
int filterWidth = 3;
int filterHeight = 3;
int width = image.Width;
int height = image.Height;
// Create sharpening filter.
double[,] filter = new double[filterWidth, filterHeight];
filter[0, 0] = filter[0, 1] = filter[0, 2] = filter[1, 0] = filter[1, 2] = filter[2, 0] = filter[2, 1] = filter[2, 2] = -1;
filter[1, 1] = 9;
double factor = 1.0;
double bias = 0.0;
Color[,] result = new Color[image.Width, image.Height];
// Lock image bits for read/write.
BitmapData pbits = sharpenImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
// Declare an array to hold the bytes of the bitmap.
int bytes = pbits.Stride * height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(pbits.Scan0, rgbValues, 0, bytes);
int rgb;
// Fill the color array with the new sharpened color values.
for (int x = 0; x < width; ++x)
{
for (int y = 0; y < height; ++y)
{
double red = 0.0, green = 0.0, blue = 0.0;
for (int filterX = 0; filterX < filterWidth; filterX++)
{
for (int filterY = 0; filterY < filterHeight; filterY++)
{
int imageX = (x - filterWidth / 2 + filterX + width) % width;
int imageY = (y - filterHeight / 2 + filterY + height) % height;
rgb = imageY * pbits.Stride + 3 * imageX;
red += rgbValues[rgb + 2] * filter[filterX, filterY];
green += rgbValues[rgb + 1] * filter[filterX, filterY];
blue += rgbValues[rgb + 0] * filter[filterX, filterY];
}
int r = Math.Min(Math.Max((int)(factor * red + bias), 0), 255);
int g = Math.Min(Math.Max((int)(factor * green + bias), 0), 255);
int b = Math.Min(Math.Max((int)(factor * blue + bias), 0), 255);
result[x, y] = Color.FromArgb(r, g, b);
}
}
}
// Update the image with the sharpened pixels.
for (int x = 0; x < width; ++x)
{
for (int y = 0; y < height; ++y)
{
rgb = y * pbits.Stride + 3 * x;
rgbValues[rgb + 2] = result[x, y].R;
rgbValues[rgb + 1] = result[x, y].G;
rgbValues[rgb + 0] = result[x, y].B;
}
}
// Copy the RGB values back to the bitmap.
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, pbits.Scan0, bytes);
// Release image bits.
sharpenImage.UnlockBits(pbits);
return sharpenImage;
}
This will create a softer sharpening effect. You can expand the filter array if you need to, or change the 16 to something larger, but I found this isn't as harsh as the one you have.
const int filterWidth = 5;
const int filterHeight = 5;
double[,] filter = new double[filterWidth,filterHeight] {
{ -1, -1, -1, -1, -1 },
{ -1, 2, 2, 2, -1 },
{ -1, 2, 16, 2, -1 },
{ -1, 2, 2, 2, -1 },
{ -1, -1, -1, -1, -1 }
};
double factor = 1.0 / 16.0;
I combined niaher's and David's answer and fixed the "bias" property. Now you can pass a "strength" between 0.0 and 1.0 to the Sharpen() function.
/// <summary>
/// Sharpens the specified image.
/// </summary>
/// <param name="image">The image.</param>
/// <param name="strength">The strength between 0.0 and 1.0.</param>
/// <returns></returns>
public static Bitmap Sharpen(Image image, double strength)
{
using (var bitmap = image as Bitmap)
{
if (bitmap != null)
{
var sharpenImage = bitmap.Clone() as Bitmap;
int width = image.Width;
int height = image.Height;
// Create sharpening filter.
const int filterWidth = 5;
const int filterHeight = 5;
var filter = new double[,]
{
{-1, -1, -1, -1, -1},
{-1, 2, 2, 2, -1},
{-1, 2, 16, 2, -1},
{-1, 2, 2, 2, -1},
{-1, -1, -1, -1, -1}
};
double bias = 1.0 - strength;
double factor = strength/16.0;
var result = new Color[image.Width,image.Height];
// Lock image bits for read/write.
if (sharpenImage != null)
{
BitmapData pbits = sharpenImage.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
// Declare an array to hold the bytes of the bitmap.
int bytes = pbits.Stride*height;
var rgbValues = new byte[bytes];
// Copy the RGB values into the array.
Marshal.Copy(pbits.Scan0, rgbValues, 0, bytes);
int rgb;
// Fill the color array with the new sharpened color values.
for (int x = 0; x < width; ++x)
{
for (int y = 0; y < height; ++y)
{
double red = 0.0, green = 0.0, blue = 0.0;
for (int filterX = 0; filterX < filterWidth; filterX++)
{
for (int filterY = 0; filterY < filterHeight; filterY++)
{
int imageX = (x - filterWidth/2 + filterX + width)%width;
int imageY = (y - filterHeight/2 + filterY + height)%height;
rgb = imageY*pbits.Stride + 3*imageX;
red += rgbValues[rgb + 2]*filter[filterX, filterY];
green += rgbValues[rgb + 1]*filter[filterX, filterY];
blue += rgbValues[rgb + 0]*filter[filterX, filterY];
}
rgb = y*pbits.Stride + 3*x;
int r = Math.Min(Math.Max((int) (factor*red + (bias*rgbValues[rgb + 2])), 0), 255);
int g = Math.Min(Math.Max((int) (factor*green + (bias*rgbValues[rgb + 1])), 0), 255);
int b = Math.Min(Math.Max((int) (factor*blue + (bias*rgbValues[rgb + 0])), 0), 255);
result[x, y] = Color.FromArgb(r, g, b);
}
}
}
// Update the image with the sharpened pixels.
for (int x = 0; x < width; ++x)
{
for (int y = 0; y < height; ++y)
{
rgb = y*pbits.Stride + 3*x;
rgbValues[rgb + 2] = result[x, y].R;
rgbValues[rgb + 1] = result[x, y].G;
rgbValues[rgb + 0] = result[x, y].B;
}
}
// Copy the RGB values back to the bitmap.
Marshal.Copy(rgbValues, 0, pbits.Scan0, bytes);
// Release image bits.
sharpenImage.UnlockBits(pbits);
}
return sharpenImage;
}
}
return null;
}
Ok, fixed the problem with distored edges. Here´s the updated one:
/// <summary>
/// Sharpens the specified image.
/// </summary>
/// <param name="image">The image.</param>
/// <param name="strength">The strength.</param>
/// <returns></returns>
public static Bitmap Sharpen(Image image, double strength)
{
using (var bitmap = image as Bitmap)
{
if (bitmap != null)
{
var sharpenImage = bitmap.Clone() as Bitmap;
int width = image.Width;
int height = image.Height;
// Create sharpening filter.
const int filterSize = 5;
var filter = new double[,]
{
{-1, -1, -1, -1, -1},
{-1, 2, 2, 2, -1},
{-1, 2, 16, 2, -1},
{-1, 2, 2, 2, -1},
{-1, -1, -1, -1, -1}
};
double bias = 1.0 - strength;
double factor = strength/16.0;
const int s = filterSize/2;
var result = new Color[image.Width,image.Height];
// Lock image bits for read/write.
if (sharpenImage != null)
{
BitmapData pbits = sharpenImage.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
// Declare an array to hold the bytes of the bitmap.
int bytes = pbits.Stride*height;
var rgbValues = new byte[bytes];
// Copy the RGB values into the array.
Marshal.Copy(pbits.Scan0, rgbValues, 0, bytes);
int rgb;
// Fill the color array with the new sharpened color values.
for (int x = s; x < width - s; x++)
{
for (int y = s; y < height - s; y++)
{
double red = 0.0, green = 0.0, blue = 0.0;
for (int filterX = 0; filterX < filterSize; filterX++)
{
for (int filterY = 0; filterY < filterSize; filterY++)
{
int imageX = (x - s + filterX + width)%width;
int imageY = (y - s + filterY + height)%height;
rgb = imageY*pbits.Stride + 3*imageX;
red += rgbValues[rgb + 2]*filter[filterX, filterY];
green += rgbValues[rgb + 1]*filter[filterX, filterY];
blue += rgbValues[rgb + 0]*filter[filterX, filterY];
}
rgb = y * pbits.Stride + 3 * x;
int r = Math.Min(Math.Max((int)(factor * red + (bias * rgbValues[rgb + 2])), 0), 255);
int g = Math.Min(Math.Max((int)(factor * green + (bias * rgbValues[rgb + 1])), 0), 255);
int b = Math.Min(Math.Max((int)(factor * blue + (bias * rgbValues[rgb + 0])), 0), 255);
result[x, y] = Color.FromArgb(r, g, b);
}
}
}
// Update the image with the sharpened pixels.
for (int x = s; x < width - s; x++)
{
for (int y = s; y < height - s; y++)
{
rgb = y*pbits.Stride + 3*x;
rgbValues[rgb + 2] = result[x, y].R;
rgbValues[rgb + 1] = result[x, y].G;
rgbValues[rgb + 0] = result[x, y].B;
}
}
// Copy the RGB values back to the bitmap.
Marshal.Copy(rgbValues, 0, pbits.Scan0, bytes);
// Release image bits.
sharpenImage.UnlockBits(pbits);
}
return sharpenImage;
}
}
return null;
}
hi i edit the code a litle bit and add two other matrices
for me this works now perfect
/// <summary>
/// Sharpens the specified image.
/// </summary>
/// <param name="image">The image.</param>
/// <param name="strength">strength erwartet werte zwische 0 - 99</param>
/// <returns></returns>
public Bitmap Sharpen(Image image, whichMatrix welcheMatrix , double strength)
{
double FaktorKorrekturWert = 0;
//strenght muß für den jeweiligen filter angepasst werden
switch (welcheMatrix)
{
case whichMatrix.Gaussian3x3:
//diese Matrix benötigt einen strenght Wert von 0 bis -9.9 default ist -2.5
//und einen korekturwert von 16
strength = (strength * -1) / 10;
FaktorKorrekturWert = 16;
break;
case whichMatrix.Mean3x3:
//diese Matrix benötigt einen strenght Wert von 0 bis -9 default ist -2.25
//und einen Korrekturwert von 10
strength = strength * -9 / 100;
FaktorKorrekturWert = 10;
break;
case whichMatrix.Gaussian5x5Type1:
//diese Matrix benötigt einen strenght Wert von 0 bis 2.5 default ist 1.25
//und einen Korrekturwert von 12
strength = strength * 2.5 / 100;
FaktorKorrekturWert = 12;
break;
default:
break;
}
using (var bitmap = image as Bitmap)
{
if (bitmap != null)
{
var sharpenImage = bitmap.Clone() as Bitmap;
int width = image.Width;
int height = image.Height;
// Create sharpening filter.
var filter = Matrix(welcheMatrix);
//const int filterSize = 3; // wenn die Matrix 3 Zeilen und 3 Spalten besitzt dann 3 bei 4 = 4 usw.
int filterSize = filter.GetLength(0);
double bias = 1.0 - strength;
double factor = strength / FaktorKorrekturWert;
//const int s = filterSize / 2;
int s = filterSize / 2; // Filtersize ist keine Constante mehr darum wurde der befehl const entfernt
var result = new Color[image.Width, image.Height];
// Lock image bits for read/write.
if (sharpenImage != null)
{
BitmapData pbits = sharpenImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
// Declare an array to hold the bytes of the bitmap.
int bytes = pbits.Stride * height;
var rgbValues = new byte[bytes];
// Copy the RGB values into the array.
Marshal.Copy(pbits.Scan0, rgbValues, 0, bytes);
int rgb;
// Fill the color array with the new sharpened color values.
for (int x = s; x < width - s; x++)
{
for (int y = s; y < height - s; y++)
{
double red = 0.0, green = 0.0, blue = 0.0;
for (int filterX = 0; filterX < filterSize; filterX++)
{
for (int filterY = 0; filterY < filterSize; filterY++)
{
int imageX = (x - s + filterX + width) % width;
int imageY = (y - s + filterY + height) % height;
rgb = imageY * pbits.Stride + 3 * imageX;
red += rgbValues[rgb + 2] * filter[filterX, filterY];
green += rgbValues[rgb + 1] * filter[filterX, filterY];
blue += rgbValues[rgb + 0] * filter[filterX, filterY];
}
rgb = y * pbits.Stride + 3 * x;
int r = Math.Min(Math.Max((int)(factor * red + (bias * rgbValues[rgb + 2])), 0), 255);
int g = Math.Min(Math.Max((int)(factor * green + (bias * rgbValues[rgb + 1])), 0), 255);
int b = Math.Min(Math.Max((int)(factor * blue + (bias * rgbValues[rgb + 0])), 0), 255);
result[x, y] = System.Drawing.Color.FromArgb(r, g, b);
}
}
}
// Update the image with the sharpened pixels.
for (int x = s; x < width - s; x++)
{
for (int y = s; y < height - s; y++)
{
rgb = y * pbits.Stride + 3 * x;
rgbValues[rgb + 2] = result[x, y].R;
rgbValues[rgb + 1] = result[x, y].G;
rgbValues[rgb + 0] = result[x, y].B;
}
}
// Copy the RGB values back to the bitmap.
Marshal.Copy(rgbValues, 0, pbits.Scan0, bytes);
// Release image bits.
sharpenImage.UnlockBits(pbits);
}
return sharpenImage;
}
}
return null;
}
public enum whichMatrix
{
Gaussian3x3,
Mean3x3,
Gaussian5x5Type1
}
private double[,] Matrix(whichMatrix welcheMatrix)
{
double[,] selectedMatrix = null;
switch (welcheMatrix)
{
case whichMatrix.Gaussian3x3:
selectedMatrix = new double[,]
{
{ 1, 2, 1, },
{ 2, 4, 2, },
{ 1, 2, 1, },
};
break;
case whichMatrix.Gaussian5x5Type1:
selectedMatrix = new double[,]
{
{-1, -1, -1, -1, -1},
{-1, 2, 2, 2, -1},
{-1, 2, 16, 2, -1},
{-1, 2, -1, 2, -1},
{-1, -1, -1, -1, -1}
};
break;
case whichMatrix.Mean3x3:
selectedMatrix =new double[,]
{
{ 1, 1, 1, },
{ 1, 1, 1, },
{ 1, 1, 1, },
};
break;
}
return selectedMatrix;
}

Categories

Resources