I'm trying to convert this snippet of code in Emgu CV using C#. I think I converted most of the thing in what they are supposed to be in EmguCV but the cvKMeans2 keeps shooting me Exception that doesn't make sense.
Here is my code:
Image<Bgr, float> src = new Image<Bgr, float>("c:\\blanc.jpg");
Matrix<Single> samples = new Matrix<Single>(src.Rows * src.Cols, 3);
for (int y = 0; y < src.Rows; y++)
{
for (int x = 0; x < src.Cols; x++)
{
for( int z = 0; z < 3; z++)
{
if(z == 0)
samples[y + x * src.Rows, z] = Convert.ToSingle(src[y, x].Blue);
else if(z == 1)
samples[y + x * src.Rows, z] = Convert.ToSingle(src[y, x].Green);
else if (z == 2)
samples[y + x * src.Rows, z] = Convert.ToSingle(src[y, x].Red);
}
}
}
MCvTermCriteria term = new MCvTermCriteria(10000, 0.0001);
term.type = TERMCRIT.CV_TERMCRIT_ITER | TERMCRIT.CV_TERMCRIT_EPS;
int clusterCount = 3;
Matrix<Int32> labels = new Matrix<Int32>(src.Width, 1);
int attempts = 5;
Matrix<Single> centers = new Matrix<Single>(clusterCount, samples.Cols);
CvInvoke.cvKMeans2(samples, clusterCount, labels, term, attempts, IntPtr.Zero, KMeansInitType.PPCenters, centers, IntPtr.Zero );
Image<Bgr, float> new_image = new Image<Bgr, float>(src.Size);
for (int y = 0; y < src.Rows; y++)
{
for (int x = 0; x < src.Cols; x++)
{
//nTotal++;
int cluster_idx = labels[y + x * src.Rows, 0];
float n1 = centers[cluster_idx, 0];
float n2 = centers[cluster_idx, 1];
float n3 = centers[cluster_idx, 2];
MCvScalar sca = new MCvScalar(n1, n2, n3);
CvInvoke.cvSet2D(new_image, y, x, sca);
}
}
CvInvoke.cvShowImage( "clustered image", new_image );
CvInvoke.cvWaitKey( 0 );
I keep receiving this exception:
Additional information: OpenCV: labels.isContinuous() && labels.type()
== CV_32S && (labels.cols == 1 || labels.rows == 1) && labels.cols + labels.rows - 1 == data.rows
It doesn't make sense that the label needs to be of Single type because I need to use it as an index in the loop just after the cvKMeans2. Can anyone help me get this code working? If this code works, there is big chance that we will buy a commercial license of Emgu to use in our software.
Thanks!
EDIT
Based on the answer below, I've adapted my code and got it working like this:
Image<Bgr, float> src = new Image<Bgr, float>(#"C:\\test.png");
Matrix<float> samples = new Matrix<float>(src.Rows * src.Cols, 1, 3);
Matrix<int> finalClusters = new Matrix<int>(src.Rows * src.Cols, 1);
for (int y = 0; y < src.Rows; y++)
{
for (int x = 0; x < src.Cols; x++)
{
samples.Data[y + x * src.Rows, 0] = (float)src[y, x].Blue;
samples.Data[y + x * src.Rows, 1] = (float)src[y, x].Green;
samples.Data[y + x * src.Rows, 2] = (float)src[y, x].Red;
}
}
MCvTermCriteria term = new MCvTermCriteria(10000, 0.0001);
term.type = TERMCRIT.CV_TERMCRIT_ITER | TERMCRIT.CV_TERMCRIT_EPS;
int clusterCount = 3;
int attempts = 5;
Matrix<Single> centers = new Matrix<Single>(clusterCount, samples.Cols, 3);
CvInvoke.cvKMeans2(samples, clusterCount, finalClusters, term, attempts, IntPtr.Zero, KMeansInitType.PPCenters, centers, IntPtr.Zero);
Image<Bgr, Byte> new_image = new Image<Bgr, Byte>(src.Size);
for (int y = 0; y < src.Rows; y++)
{
for (int x = 0; x < src.Cols; x++)
{
int cluster_idx = finalClusters[y + x * src.Rows, 0];
MCvScalar sca1 = CvInvoke.cvGet2D(centers, cluster_idx, 0);
Bgr color = new Bgr(sca1.v0, sca1.v1, sca1.v2);
PointF p = new PointF(x, y);
new_image.Draw(new CircleF(p, 1.0f), color, 1);
}
}
CvInvoke.cvShowImage("clustered image", new_image);
CvInvoke.cvWaitKey(0);
I take a look at the example you refers to and I write some vanilla code that works on an input rgb image performing kmeans in rgb space. You can tweak some parameters to adapt it to your needs.
Taking for example this input image:
EMGUCV CODE
Bgr[] clusterColors = new Bgr[] {
new Bgr(0,0,255),
new Bgr(0, 255, 0),
new Bgr(255, 100, 100),
new Bgr(255,0,255),
new Bgr(133,0,99),
new Bgr(130,12,49),
new Bgr(0, 255, 255)};
Image<Bgr, float> src = new Image<Bgr, float>("fotobp.jpg");
Matrix<float> samples = new Matrix<float>(src.Rows * src.Cols, 1, 3);
Matrix<int> finalClusters = new Matrix<int>(src.Rows * src.Cols, 1);
for (int y = 0; y < src.Rows; y++)
{
for (int x = 0; x < src.Cols; x++)
{
samples.Data[y + x * src.Rows, 0] = (float)src[y, x].Blue;
samples.Data[y + x * src.Rows, 1] = (float)src[y, x].Green;
samples.Data[y + x * src.Rows, 2] = (float)src[y, x].Red;
}
}
MCvTermCriteria term = new MCvTermCriteria(100, 0.5);
term.type = TERMCRIT.CV_TERMCRIT_ITER | TERMCRIT.CV_TERMCRIT_EPS;
int clusterCount = 4;
int attempts = 5;
Matrix<Single> centers = new Matrix<Single>(clusterCount, src.Rows * src.Cols);
CvInvoke.cvKMeans2(samples, clusterCount, finalClusters, term, attempts, IntPtr.Zero, KMeansInitType.PPCenters, IntPtr.Zero, IntPtr.Zero);
Image<Bgr, float> new_image = new Image<Bgr, float>(src.Size);
for (int y = 0; y < src.Rows; y++)
{
for (int x = 0; x < src.Cols; x++)
{
PointF p = new PointF(x, y);
new_image.Draw(new CircleF(p, 1.0f), clusterColors[finalClusters[y + x * src.Rows, 0]], 1);
}
}
CvInvoke.cvShowImage("clustered image", new_image);
CvInvoke.cvWaitKey(0);
RESULT (CLUSTER_NUM = 4)
Related
This is my first question and I'm relatively new to C#. (Excuse my bad English)
I'm writing a template matching algorithm in C# using the .NET Framework with a WindowsForms Application in Visual Studio. Sadly the new indices and ranges functions from C#8.0, especially the range operator (..), didn't get implemented in the .NET Framework. I know there is a workaround for this as you can see in this thread but it is not supported by Microsoft. So I'm searching for another way to parallelize my elementwise 2dArray (matrix) calculations to make my program faster.
In my program, I'm calculating the differential square (ds) of an area (with the size of my template) inside a 2dArray (my image) and a 2dArray (my template). These values a written to a new 2dAary (DS) which holds all differential squares in the corresponding positions to the image. I can search the indices of DS where the differential square is minimal which is equal to the matching position (highest correspondence between template and image) of the template inside the image.
In python the calculation of DS is very quick using the index range operator (:) and will look like this:
H,W = I.shape # read out Height H & Width W from Image I
h,w = T.shape # read out Height h & Width w from Template T
for i in range(H-h+1):
for j in range(W-w+1):
DS[i,j] = np.sum((I[i:i+h,j:j+w] - T)**2)
But in C# I have to make the calculation of DS elementwise therefore it looks like this and takes for ever:
int Tw = template.Width;
int Th = template.Height;
int Iw = image.Width;
int Ih = image.Height;
int d = 0;
int[,] ds = new int[Tw, Th];
int[,] DS = new int[Iw - Tw + 1, Ih - Th + 1];
for (int y = 0; y < Ih - Th + 1; y++)
{
for (int x = 0; x < Iw - Tw + 1; x++)
{
for (int yT = 0; yT < Th; yT++)
{
for (int xT = 0; xT < Tw; xT++)
{
d = I[x + xT, y + yT] - T[xT, yT];
ds[xt, yt] = d * d;
}
}
int sum = ds.Cast<int>().Sum();
DS[x, y] = sum;
}
}
I know that I could use threads but that would be a little complex for me.
Or maybe I could use CUDA with my Nvidia GPU to speed things up a little.
But I am asking you and myself is there another way to parallelize (optimize) my elementwise 2dArray calculations?
I look forward to any help.
Many thanks in advance!!!
EDIT:
Here I have a working example of my code for a .NET Framework Console App. As you can see I make a lot of elementwise 2d and 3d Array calculations which I would like to process in parallel (or perform them faster in any other way):
using System;
using System.Drawing;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TemplateMatcher_Console
{
class Program
{
public static int[,,] bitmapToMatrix(Bitmap bmp)
{
int[,,] I = new int[bmp.Width, bmp.Height, 3];
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x++)
{
Color pix = bmp.GetPixel(x, y);
I[x, y, 0] = Convert.ToInt32(pix.R);
I[x, y, 1] = Convert.ToInt32(pix.G);
I[x, y, 2] = Convert.ToInt32(pix.B);
}
}
return I;
}
public static int[] indexOfMiniumValue(int[,] matrix)
{
int value = 0;
int minValue = 999999999;
int minFirstIndex = 0;
int minSecondIndex = 0;
int[] ij = new int[2];
for (int i = 0; i < matrix.GetLength(0); i++)
{
for (int j = 0; j < matrix.GetLength(1); j++)
{
value = matrix[i, j];
if (value < minValue)
{
minValue = value;
minFirstIndex = i;
minSecondIndex = j;
}
}
}
ij[0] = minFirstIndex;
ij[1] = minSecondIndex;
return ij;
}
public static void Print2DArray<T>(T[,] matrix)
{
for (int i = 0; i < matrix.GetLength(0); i++)
{
for (int j = 0; j < matrix.GetLength(1); j++)
{
Console.Write(matrix[i, j] + "\t");
}
Console.WriteLine();
}
}
static void Main(string[] args)
{
// Deklaration & Eingabe
Console.WriteLine("Type the filepath for your image and then press Enter");
string im = Console.ReadLine();
Console.WriteLine("\nType the filepath for your template and then press Enter");
string temp = Console.ReadLine();
Bitmap template = new Bitmap(#temp);
Bitmap image = new Bitmap(#im);
int Tw = template.Width;
int Th = template.Height;
int Iw = image.Width;
int Ih = image.Height;
int[,] ds = new int[Tw, Th];
int[,] DS = new int[Iw - Tw + 1, Ih - Th + 1];
int[,,] DS_rgb = new int[Iw - Tw + 1, Ih - Th + 1, 3];
int[] xy = new int[2];
// Verarbeitung
// int[,,] I = Array.ConvertAll(image_I, new Converter<byte, int>(Convert.ToInt32));
int[,,] I = bitmapToMatrix(image);
int[,,] T = bitmapToMatrix(template);
for (int rgb = 0; rgb < 3; rgb++)
{
for (int y = 0; y < Ih - Th + 1; y++)
{
for (int x = 0; x < Iw - Tw + 1; x++)
{
//DS_rgb[x, y, rgb] = (I[x .. x + template.Width, y .. y + template.Height, rgb] - T[0 .. template.Width, 0 .. template.Height, rgb]);
for (int yT = 0; yT < Th; yT++)
{
for (int xT = 0; xT < Tw; xT++)
{
ds[xT, yT] = (I[x + xT, y + yT, rgb] - T[xT, yT, rgb]) * (I[x + xT, y + yT, rgb] - T[xT, yT, rgb]);
}
}
int sum = ds.Cast<int>().Sum();
DS_rgb[x, y, rgb] = sum;
}
}
}
//DS[.., ..] = DS_rgb[.., .., 0] + DS_rgb[.., .., 1] + DS_rgb[.., .., 2];
for (int y = 0; y < Ih - Th + 1; y++)
{
for (int x = 0; x < Iw - Tw + 1; x++)
{
DS[x, y] = DS_rgb[x, y, 0] + DS_rgb[x, y, 1] + DS_rgb[x, y, 2];
}
}
//xy = DS.FindIndex(z => z == Math.Min(DS));
xy = indexOfMiniumValue(DS);
// Ausgabe
// Ausgeben der Matrix DS
/*
Console.WriteLine("\nMatrix with all differtial squares:");
Print2DArray(DS);
*/
Console.WriteLine($"\nPosition of your template in your image (upper left corner): ({xy[0]}, {xy[1]})");
Console.Write("\nPress any key to close the TemplateMatcher console app...");
Console.ReadKey();
}
}
}
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));
}
}
}
SSM screenshot
Hi, I have implemeted the Spectral Saliency map from Matlab code to C#.net,
But result is some strange.
Result map is not correctly mapped to original image.
What's wrong with me?
Original Matlab code(Octave) is here:
%% Read image from file
inImg = im2double(rgb2gray(imread('1.jpg')));
inImg = imresize(inImg, 64/size(inImg, 2));
%% Spectral Residual
myFFT = fft2(inImg);
myLogAmplitude = log(abs(myFFT));
myPhase = angle(myFFT);
mySpectralResidual = myLogAmplitude - imfilter(myLogAmplitude, fspecial('average', 3), 'replicate');
saliencyMap = abs(ifft2(exp(mySpectralResidual + i*myPhase))).^2;
%% After Effect
saliencyMap = mat2gray(imfilter(saliencyMap, fspecial('gaussian', [10, 10], 2.5)));
imshow(saliencyMap);
My Code is here : C#.net + AForge.net -------------
Windows10, VS2017, AForge & Accord.net
// 이미지 로드
image2 = (Bitmap)pictureBox1.Image;
// 박스 블러
// logAmp smoothing
Accord.Imaging.Filters.FastBoxBlur filtBox1 =
new Accord.Imaging.Filters.FastBoxBlur(3, 3);
// apply the filter
filtBox1.ApplyInPlace(image2);
// 크기줄이기 64x
AForge.Imaging.Filters.ResizeBilinear filtResize =
new AForge.Imaging.Filters.ResizeBilinear(64, 64);
// apply the filter
Bitmap smallImage = filtResize.Apply(image2);
// 2D FFT -------------------------------------------
//cv::merge(planes, 2, complexImg);
AForge.Imaging.ComplexImage complexImage =
AForge.Imaging.ComplexImage.FromBitmap(smallImage);
//double[,] logAmpl = new double[64, 64];
for (int x = 0; x < 64; x++)
{
for (int y = 0; y < 64; y++)
{
//Calculating elementwise complex conjugate of the shifted image 2d vector
complexImage.Data[x, y].Im = 0;
}
}// for y
//cv::dft(complexImg, complexImg);
complexImage.ForwardFourierTransform();
// Calc Log AmpliTude of magnitude
double[,] logAmpl = new double[64, 64];
for (int x = 0; x < 64; x++)
{
for (int y = 0; y < 64; y++)
{
//Calculating elementwise complex conjugate of the shifted image 2d vector
logAmpl[x, y] = Math.Log(Complex.Multiply(
complexImage.Data[x, y], complexImage.Data[x, y]).Magnitude);
}
}// for y
Bitmap logAmp = logAmpl.ToBitmap();
// extract Phase, int[,] array = new int[4, 2];
double[,] myPhase = new double[64, 64];
for (int x = 0; x < 64; x++)
{
for (int y = 0; y < 64; y++)
{
//Calculating elementwise complex conjugate of the shifted image 2d vector
myPhase[x, y] = complexImage.Data[x, y].Phase;
}
}// for y
// calc Spectral Residual -------------------------
// logAmp smoothing
Accord.Imaging.Filters.FastBoxBlur filtBox =
new Accord.Imaging.Filters.FastBoxBlur(3, 3);
// apply the filter
Bitmap smoothAmp= filtBox.Apply(logAmp);
// subtraction
AForge.Imaging.Filters.Subtract filter =
new AForge.Imaging.Filters.Subtract(smoothAmp);
// apply the filter
Bitmap specResi = filter.Apply(logAmp);
// Calc Saliency map -----------------
// residual for Re
AForge.Imaging.ComplexImage totalSpec =
AForge.Imaging.ComplexImage.FromBitmap(specResi);
// + j*im
for (int x = 0; x < 64; x++)
{
for (int y = 0; y < 64; y++)
{
totalSpec.Data[x, y].Im = myPhase[x, y];
}
}// for y
// exp of re + j*im
for (int x = 0; x < 64; x++)
{
for (int y = 0; y < 64; y++)
{
totalSpec.Data[x, y] = Complex.Exp(totalSpec.Data[x, y]);
}
}// for y
// ifft2
//complexImage.ForwardFourierTransform();
totalSpec.ForwardFourierTransform();
// .^2 of ifft2
for (int x = 0; x < 64; x++)
{
for (int y = 0; y < 64; y++)
{
totalSpec.Data[x, y] =
Complex.Multiply(totalSpec.Data[x, y], totalSpec.Data[x, y]);
}
}// for y
// abs .^2
Bitmap salMap= totalSpec.ToBitmap();
// 가우시안 블러
Accord.Imaging.Filters.GaussianBlur filt2 =
new Accord.Imaging.Filters.GaussianBlur(2.5, 9);
// apply the filter
filt2.ApplyInPlace(salMap);
// create filter
Accord.Imaging.Filters.ContrastStretch filtNorm =
new Accord.Imaging.Filters.ContrastStretch();
// process image
filtNorm.ApplyInPlace(salMap);
//ImageBox.Show(salMap);
pictureBox3.Image = salMap;
What;s wrong with me?
I have a weird problem. I have been searching the internet for a good and fast Gaussian blur algorithm. And I finally found one!
So what I did was to try it in a new project - it worked just fine.
When I then was going to import the class into my main project and then tried it, I got a System.OverflowException. I'm finding this really weird that it works in one project, but not in another.
Here's the algorithm:
(I am calling the function almost the same. I tried calling it the exact same way, but I still got an exception)
public static void FastBlur(Bitmap SourceImage, int radius)
{
var rct = new Rectangle(0, 0, SourceImage.Width, SourceImage.Height);
var dest = new int[rct.Width * rct.Height];
var source = new int[rct.Width * rct.Height];
var bits = SourceImage.LockBits(rct, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
Marshal.Copy(bits.Scan0, source, 0, source.Length);
SourceImage.UnlockBits(bits);
if (radius < 1) return;
int w = rct.Width;
int h = rct.Height;
int wm = w - 1;
int hm = h - 1;
int wh = w * h;
int div = radius + radius + 1;
var r = new int[wh];
var g = new int[wh];
var b = new int[wh];
int rsum, gsum, bsum, x, y, i, p1, p2, yi;
var vmin = new int[max(w, h)];
var vmax = new int[max(w, h)];
var dv = new int[256 * div];
for (i = 0; i < 256 * div; i++)
{
dv[i] = (i / div);
}
int yw = yi = 0;
for (y = 0; y < h; y++)
{ // blur horizontal
rsum = gsum = bsum = 0;
for (i = -radius; i <= radius; i++)
{
int p = source[yi + min(wm, max(i, 0))];
rsum += (p & 0xff0000) >> 16;
gsum += (p & 0x00ff00) >> 8;
bsum += p & 0x0000ff;
}
for (x = 0; x < w; x++)
{
r[yi] = dv[rsum];
g[yi] = dv[gsum];
b[yi] = dv[bsum];
if (y == 0)
{
vmin[x] = min(x + radius + 1, wm);
vmax[x] = max(x - radius, 0);
}
p1 = source[yw + vmin[x]];
p2 = source[yw + vmax[x]];
rsum += ((p1 & 0xff0000) - (p2 & 0xff0000)) >> 16;
gsum += ((p1 & 0x00ff00) - (p2 & 0x00ff00)) >> 8;
bsum += (p1 & 0x0000ff) - (p2 & 0x0000ff);
yi++;
}
yw += w;
}
for (x = 0; x < w; x++)
{ // blur vertical
rsum = gsum = bsum = 0;
int yp = -radius * w;
for (i = -radius; i <= radius; i++)
{
yi = max(0, yp) + x;
rsum += r[yi];
gsum += g[yi];
bsum += b[yi];
yp += w;
}
yi = x;
for (y = 0; y < h; y++)
{
dest[yi] = (int)(0xff000000u | (uint)(dv[rsum] << 16) | (uint)(dv[gsum] << 8) | (uint)dv[bsum]); // <--- Here's where I get the exception
if (x == 0)
{
vmin[y] = min(y + radius + 1, hm) * w;
vmax[y] = max(y - radius, 0) * w;
}
p1 = x + vmin[y];
p2 = x + vmax[y];
rsum += r[p1] - r[p2];
gsum += g[p1] - g[p2];
bsum += b[p1] - b[p2];
yi += w;
}
}
// copy back to image
var bits2 = SourceImage.LockBits(rct, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
Marshal.Copy(dest, 0, bits2.Scan0, dest.Length);
SourceImage.UnlockBits(bits);
}
Here's how I call the function:
public Bitmap DownloadBlurredImage(List<string> uris, int blurradius)
{
Uri urlUri = new Uri(uris.ElementAt(0));
if (RandomSelect == true)
{
Random r = new Random();
urlUri = new Uri(uris.ElementAt(r.Next(0, uris.Count - 1)));
}
WebRequest webRequest = WebRequest.CreateDefault(urlUri);
webRequest.ContentType = "image/jpeg";
WebResponse webResponse = webRequest.GetResponse();
Stream mystream;
if ((mystream = webResponse.GetResponseStream()) != null)
{
Bitmap b = new Bitmap(mystream);
imageFilters.FastBlur(b, 10); //<--- here's where I'm calling
return b;
}
else
{
return null;
}
}
Does anyone know why this is acting up? I myself have NO clue..
You're using signed 'ints' to hold unsigned data - if the MSB is set, the overflow will occur. Use unsigned ints (for example for dest), or surround in an unchecked { } directive.
I need to implement Single Scale retinex and multiscale retinex algorithm in C#,
I searched a bit but couldn't find any useful practice projects and artilces with code
As I understood correctly I should:
Convert RGB to YUV
Blur the image using Gaussian blur filter
Use I'(x, y) = 255*log10( I(x, y)/G(x, y) ) + 127.5
I - is illumination, G - Gaussian kernel, I' - the result image
Сonvert back YUV to RGB
This code is not working correctly
public static Image<Bgr, byte> SingleScaleRetinex(this Image<Bgr, byte> img, int gaussianKernelSize, double sigma)
{
var radius = gaussianKernelSize / 2;
var kernelSize = 2 * radius + 1;
var ycc = img.Convert<Ycc, byte>();
var sum = 0f;
var gaussKernel = new float[kernelSize * kernelSize];
for (int i = -radius, k = 0; i <= radius; i++, k++)
{
for (int j = -radius; j <= radius; j++)
{
var val = (float)Math.Exp(-(i * i + j * j) / (sigma * sigma));
gaussKernel[k] = val;
sum += val;
}
}
for (int i = 0; i < gaussKernel.Length; i++)
gaussKernel[i] /= sum;
var gray = new Image<Gray, byte>(ycc.Size);
CvInvoke.cvSetImageCOI(ycc, 1);
CvInvoke.cvCopy(ycc, gray, IntPtr.Zero);
// Размеры изображения
var width = img.Width;
var height = img.Height;
var bmp = gray.Bitmap;
var bitmapData = bmp.LockBits(new Rectangle(Point.Empty, gray.Size), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
unsafe
{
for (var y = 0; y < height; y++)
{
var row = (byte*)bitmapData.Scan0 + y * bitmapData.Stride;
for (var x = 0; x < width; x++)
{
var color = row + x;
float val = 0;
for (int i = -radius, k = 0; i <= radius; i++, k++)
{
var ii = y + i;
if (ii < 0) ii = 0; if (ii >= height) ii = height - 1;
var row2 = (byte*)bitmapData.Scan0 + ii * bitmapData.Stride;
for (int j = -radius; j <= radius; j++)
{
var jj = x + j;
if (jj < 0) jj = 0; if (jj >= width) jj = width - 1;
val += *(row2 + jj) * gaussKernel[k];
}
}
var newColor = 127.5 + 255 * Math.Log(*color / val);
if (newColor > 255)
newColor = 255;
else if (newColor < 0)
newColor = 0;
*color = (byte)newColor;
}
}
}
bmp.UnlockBits(bitmapData);
CvInvoke.cvCopy(gray, ycc, IntPtr.Zero);
CvInvoke.cvSetImageCOI(ycc, 0);
return ycc.Convert<Bgr, byte>();
}
Look at:
http://www.fer.unizg.hr/ipg/resources/color_constancy
These algorithms are modifications of the Retinex algorithm (with speed improvement) although the author gave them funny names :)
There is a full source code (C++, but it is written very nicely).
Sorry for necro-posting, but it seems that there's a mistake in step 3 of your procedure that can mislead someone passing by.
In order to apply the correction, you want to divide source image by Gauss-filtered copy of it, not the Gaussian kernel itself. Approximately, in pseudo-code:
I_filtered(x,y) = G(x,y) * I(x,y)
I'(x,y) = log(I(x,y) / I_filtered(x,y))
And then apply casting of I'(x,y) to required numeric type (uint8, as I can refer from original post).
More on that topic can be found in this paper:
Ri(x, y) = log(Ii(x, y)) − log(Ii(x, y) ∗ F(x, y))
where Ii
is the input image on the i-th color channel, Ri
is the retinex output image on the i-th
channel and F is the normalized surround function.
.