Convert from Yuv 420 to RGB and then to Bitmap - c#

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;
}

Related

C# Bitmap blur with transparent background

I'd like to add blur effect on my text. In order to do this, I'm using this method:
public static Bitmap ConvolutionFilter(Bitmap sourceBitmap, double[,] filterMatrix, double factor = 1, int bias = 0)
{
BitmapData sourceData = sourceBitmap.LockBits(new Rectangle(0, 0,
sourceBitmap.Width, sourceBitmap.Height),
ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
byte[] pixelBuffer = new byte[sourceData.Stride * sourceData.Height];
byte[] resultBuffer = new byte[sourceData.Stride * sourceData.Height];
Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length);
sourceBitmap.UnlockBits(sourceData);
double blue = 0.0;
double green = 0.0;
double red = 0.0;
int filterWidth = filterMatrix.GetLength(1);
int filterHeight = filterMatrix.GetLength(0);
int filterOffset = (filterWidth - 1) / 2;
int calcOffset = 0;
int byteOffset = 0;
for (int offsetY = filterOffset; offsetY < sourceBitmap.Height - filterOffset; offsetY++)
{
for (int offsetX = filterOffset; offsetX < sourceBitmap.Width - filterOffset; offsetX++)
{
blue = 0;
green = 0;
red = 0;
byteOffset = offsetY * sourceData.Stride + offsetX * 4;
for (int filterY = -filterOffset; filterY <= filterOffset; filterY++)
{
for (int filterX = -filterOffset; filterX <= filterOffset; filterX++)
{
calcOffset = byteOffset +
(filterX * 4) +
(filterY * sourceData.Stride);
blue += (double)(pixelBuffer[calcOffset]) *
filterMatrix[filterY + filterOffset,
filterX + filterOffset];
green += (double)(pixelBuffer[calcOffset + 1]) *
filterMatrix[filterY + filterOffset,
filterX + filterOffset];
red += (double)(pixelBuffer[calcOffset + 2]) *
filterMatrix[filterY + filterOffset,
filterX + filterOffset];
}
}
blue = factor * blue + bias;
green = factor * green + bias;
red = factor * red + bias;
blue = (blue > 255 ? 255 : (blue < 0 ? 0 : blue));
green = (green > 255 ? 255 : (green < 0 ? 0 : green));
red = (red > 255 ? 255 : (red < 0 ? 0 : red));
resultBuffer[byteOffset] = (byte)(blue);
resultBuffer[byteOffset + 1] = (byte)(green);
resultBuffer[byteOffset + 2] = (byte)(red);
resultBuffer[byteOffset + 3] = 255;
}
}
Bitmap resultBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);
BitmapData resultData = resultBitmap.LockBits(new Rectangle(0, 0, resultBitmap.Width, resultBitmap.Height), ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);
resultBitmap.UnlockBits(resultData);
return resultBitmap;
}
Code from: https://softwarebydefault.com/2013/06/09/image-blur-filters/
This metod blurs my text, but it changes transparent background to black background.
Before:
After:
Text is a Bitmap rendered by Graphics.DrawText(...) method.
I'd like to get blurred text but on transparent background. How can I achive it?
You are setting the alpha value to 255 (not transparent) in the filter.
resultBuffer[byteOffset + 3] = 255; // Fully opaque - not transparent at all
If you treat the alpha component just like the color components (red/gree/blue), I think you'll get what you're after.

How to add noise to image or convert it to 24bpp?

Noob needs help!
I have an image and I need to add noise on it. I tried using AForge libs to do it but this method works only with 24bpp bitmaps and I get something different after resizing. The question is how to convert a bitmap to 24bpp or how to add noise on it? Maybe there are some libs for making this easier.
Resizing:
private Image Fit(Image image)
{
Image img = image;
if (filepath != null)
{
if (img.Width > pictureBox1.Width)
{
double op = ((pictureBox1.Width - (pictureBox1.Width % 100)) % 100) + (pictureBox1.Width % 100) * 0.01;
double percent = img.Width / (pictureBox1.Width * 0.01);
double temp = ((percent - percent % 100 + 100) - percent) * pictureBox1.Height * 0.01;
double height = pictureBox1.Height * 0.01 * ((percent - percent % 100 + 100) - percent);
System.Drawing.Size sz = new Size(pictureBox1.Width, (int)height);
img = resizeImage(img, sz);
}
if (img.Height > pictureBox1.Height)
{
double percent = img.Height / (pictureBox1.Height * 0.01);
double temp = ((percent - percent % 100 + 100) - percent) * pictureBox1.Width * 0.01;
double width = pictureBox1.Width * 0.01 * ((percent - percent % 100 + 100) - percent);
System.Drawing.Size sz = new Size((int)width, pictureBox1.Height);
img = resizeImage(img, sz);
}
}
return img;
}
P.S.> I have a type of bug - system totally refuses to divide 1 by 100 so I had to multiply 1 by 0.01 or I get 0.
Resizing the image to maintain aspect ratio is pretty easy. You compute the horizontal and vertical scaling factors, and then select the smallest of the two.
double vscale = 1.0;
double hscale = 1.0;
if (img.Width > pictureBox1.Width)
{
hscale = (double)pictureBox1.Width/img.Width;
}
if (img.Height > pictureBox1.Height)
{
vscale = (double)pictureBox1.Height/img.Height;
}
double scale = Math.Min(hscale, vscale);
double width = scale * img.Width;
double height = scale * img.Height;
Size sz = new Size((int)width, (int)height);
img = resizeImage(img, sz)
Note that this only scales if the image is larger than the box. It won't zoom the image to make it fit the box if the image is smaller than the box.
Haven't found anything good. That's how I've solved it:
public void GenerateNoise(Image img, int intense)
{
Bitmap finalBmp = img as Bitmap;
Random r = new Random();
int width = img.Width;
int height = img.Height;
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
int def = r.Next(0, 100);
if (def < intense)
{
int op = r.Next(0, 1);
if (op == 0)
{
int num = r.Next(0, intense);
Color clr = finalBmp.GetPixel(x, y);
int R = (clr.R + clr.R + num)/2;
if (R > 255) R = 255;
int G = (clr.G + clr.G + num) / 2;
if (G > 255) G = 255;
int B = (clr.B + clr.B + num) / 2;
if (B > 255) B = 255;
Color result = Color.FromArgb(255, R, G, B);
finalBmp.SetPixel(x, y, result);
}
else
{
int num = r.Next(0, intense);
Color clr = finalBmp.GetPixel(x, y);
Color result = Color.FromArgb(255, (clr.R + clr.R - num) / 2, (clr.G + clr.G - num) / 2,
(clr.B + clr.B - num) / 2);
finalBmp.SetPixel(x, y, result);
}
}
}
}
}

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;

RGB to Ycbcr conversion

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]);

How to speed up my color thresholding in C#

I'm working on tracking objects based on color and I was using EmguCV library to threshold my color image to binary black and white image. Thresholding itself was quite fast, 50 ms on 320x240 image. I'm using RG Chromaticity color space, so there are some necessarily calculations.
Now I'm trying to speed it up using pointers, but the result is very similar with what I did with emguCV (around 50 ms per image).
I want to ask, if there is some expert who can help me, what I am doing wrong. Here is my short code snippet of my color thresholding implementation. It's based on this one: https://web.archive.org/web/20140906075741/http://bobpowell.net/onebit.aspx.
public static Bitmap ThresholdRGChroma(Bitmap original, double angleMin,
double angleMax, double satMin, double satMax)
{
Bitmap bimg = new Bitmap(original.Width, original.Height, PixelFormat.Format1bppIndexed);
BitmapData imgData = original.LockBits(new Rectangle(0, 0, original.Width, original.Height), ImageLockMode.ReadOnly, original.PixelFormat);
BitmapData bimgData = bimg.LockBits(new Rectangle(0, 0, bimg.Width, bimg.Height), ImageLockMode.ReadWrite, bimg.PixelFormat);
int pixelSize = 3;
double r, g, angle, sat;
unsafe
{
byte* R, G, B;
byte* row;
int RGBSum;
for (int y = original.Height - 1; y >= 0; y--)
{
row = (byte*)imgData.Scan0 + (y * imgData.Stride);
for (int x = original.Width - 1; x >= 0; x--)
{
// get rgb values
B = &row[x * pixelSize];
G = &row[x * pixelSize + 1];
R = &row[x * pixelSize + 2];
RGBSum = *R + *G + *B;
if (RGBSum == 0)
{
SetIndexedPixel(x, y, bimgData, false);
continue;
}
//calculate r ang g for rg chroma color space
r = (double)*R / RGBSum;
g = (double)*G / RGBSum;
//and angle and saturation
angle = GetAngleRad(r, g) * (180.0 / Math.PI);
sat = Math.Sqrt(Math.Pow(g, 2) + Math.Pow(r, 2));
//conditions to set pixel black or white
if ((angle >= angleMin && angle <= angleMax) && (sat >= satMin && sat <= satMax))
SetIndexedPixel(x, y, bimgData, true);
else
SetIndexedPixel(x, y, bimgData, false);
}
}
}
bimg.UnlockBits(bimgData);
original.UnlockBits(imgData);
return bimg;
}
private unsafe static void SetIndexedPixel(int x, int y, BitmapData bmd, bool pixel)
{
int index = y * bmd.Stride + (x >> 3);
byte* p = (byte*)bmd.Scan0.ToPointer();
byte mask = (byte)(0x80 >> (x & 0x7));
if (pixel)
p[index] |= mask;
else
p[index] &= (byte)(mask ^ 0xff);
}
private static double GetAngleRad(double x, double y)
{
if (x - _rgChromaOriginX == 0)
return 0.0;
double angle = Math.Atan((y - _rgChromaOriginY) / (x - _rgChromaOriginX)); // 10ms
if (x < _rgChromaOriginX && y > _rgChromaOriginY)
angle = angle + Math.PI;
else if (x < _rgChromaOriginX && y < _rgChromaOriginY)
angle = angle + Math.PI;
else if (x > _rgChromaOriginX && y < _rgChromaOriginY)
angle = angle + 2 * Math.PI;
return angle;
}
You're doing a lot of unnecessary math for each pixel, calculating exact values only to check to see if they're inside some limits. You can simplify the comparisons by precomputing some adjustments to the limits.
The easiest substitution is for the saturation. You're doing a square root which you can avoid by squaring the limits instead.
double satMin2 = satMin*satMin;
double satMax2 = satMax*satMax;
// ...
sat2 = g*g + r*r;
//conditions to set pixel black or white
if ((angle >= angleMin && angle <= angleMax) && (sat2 >= satMin2 && sat <= satMax2))
A similar trick can be used with the angle. Rather than calculating the angle with Math.Atan, figure out what those limits equate to in your r and g ranges.
For completeness, here is modified version of thresholding image in RG Chromaticity color space it's more than 2 times faster than the version in my Question.
public static Bitmap ThresholdRGChroma(Bitmap original, Rectangle roi, double angle,
double width, double satMin, double satMax)
{
Bitmap bimg = new Bitmap(original.Width, original.Height, PixelFormat.Format1bppIndexed);
BitmapData imgData = original.LockBits(new Rectangle(0, 0, original.Width, original.Height), ImageLockMode.ReadOnly, original.PixelFormat);
BitmapData bimgData = bimg.LockBits(new Rectangle(0, 0, bimg.Width, bimg.Height), ImageLockMode.ReadWrite, bimg.PixelFormat);
int pixelSize = 3;
double r, g, sat, m;
double satMin2 = satMin * satMin;
double satMax2 = satMax * satMax;
double cr = Math.Sin((2 * Math.PI * angle) / 360.0);
double cg = Math.Cos((2 * Math.PI * angle) / 360.0);
// Instead of (Math.Cos(2 * width / 180.0) + 1) / 2.0 I'm using pre-calculated <1; 0> values.
double w2 = -width;
unsafe
{
byte* R, G, B;
byte* row;
int RGBSum;
for (int y = original.Height - 1; y >= 0; y--)
{
row = (byte*)imgData.Scan0 + (y * imgData.Stride);
for (int x = original.Width - 1; x >= 0; x--)
{
B = &row[x * pixelSize];
G = &row[x * pixelSize + 1];
R = &row[x * pixelSize + 2];
RGBSum = *R + *G + *B;
if (RGBSum == 0)
{
SetIndexedPixel(x, y, bimgData, false);
continue;
}
r = (double)*R / RGBSum - _rgChromaOriginX;
g = (double)*G / RGBSum - _rgChromaOriginY;
m = cr * r + cg * g;
sat = r * r + g * g;
if (m > 0 && m * m > w2 * w2 * sat && sat >= satMin2 && sat <= satMax2)
SetIndexedPixel(x, y, bimgData, true);
else
SetIndexedPixel(x, y, bimgData, false);
}
}
}
bimg.UnlockBits(bimgData);
original.UnlockBits(imgData);
return bimg;
}

Categories

Resources