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));
}
}
}
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();
}
}
}
See: Image convolution in spatial domain
The following code achieves Linear convolution in Spatial domain.
public static double[,] ConvolutionSpatial(double[,] paddedImage, double[,] mask, double offset)
{
double min = 0.0;
double max = 1.0;
double factor = GetFactor(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);
convolve[x, y] = Math.Min(Math.Max((sum / factor) + offset, min), max);
string str = string.Empty;
}
}
return convolve;
}
public static 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;
}
public static double GetFactor(double[,] kernel)
{
double sum = 0.0;
int width = kernel.GetLength(0);
int height = kernel.GetLength(1);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
sum += kernel[x, y];
}
}
return (sum == 0) ? 1 : sum;
}
Its performance is as follows:
image-size kernel-size time-elapsed
------------------------------------------
100x100 3x3 13ms
512x512 3x3 291ms
1018x1280 3x3 1687ms
100x100 100x100 4983ms
512x512 512x512 35624394ms
1018x1280 1018x1280 [practically unusable]
I have two questions:
Does it look like a descent performance?
If not, how can I increase performance?
It depends on your final requirements.
The obvious things to do: replace multidimensional arrays [,] with jagged arrays [][] and reverse the order of nested loops:
for (int x = 0; ...; x++)
{
for (int y = 0; ...; y++)
{
...
}
}
instead of
for (int y = 0; ...; y++)
{
for (int x = 0; ...; x++)
{
...
}
}
In the first case the CPU cache lines are used much more efficiently (because the left index represents continuous rows) while the latter one invalidates a cache line on every iteration.
So benchmarks of code with jagged arrays and reversed loops has shown 2 times better performance for the 1018x1280 3x3 convolution comparing to the original implementation:
BenchmarkDotNet=v0.11.1, OS=Windows 10.0.17134.167 (1803/April2018Update/Redstone4)
Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
Frequency=3515624 Hz, Resolution=284.4445 ns, Timer=TSC
[Host] : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3131.0
RyuJitX64 : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3131.0
Job=RyuJitX64 Jit=RyuJit Platform=X64
Method | Mean | Error | StdDev |
------------- |---------:|----------:|----------:|
BenchmarkOld | 61.82 ms | 0.3979 ms | 0.3527 ms |
BenchmarkNew | 26.98 ms | 0.1050 ms | 0.0982 ms |
Here is the code:
public static double[][] ConvolutionSpatial(double[][] paddedImage, double[][] mask, double offset)
{
double min = 0.0;
double max = 1.0;
double factor = GetFactor(mask);
int paddedImageWidth = paddedImage.Length;
int paddedImageHeight = paddedImage[0].Length;
int maskWidth = mask.Length;
int maskHeight = mask[0].Length;
int imageWidth = paddedImageWidth - maskWidth;
int imageHeight = paddedImageHeight - maskHeight;
double[][] convolve = new double[imageWidth][];
for (int x = 0; x < imageWidth; x++)
{
convolve[x] = new double[imageHeight];
for (int y = 0; y < imageHeight; y++)
{
double sum = Sum(paddedImage, mask, x, y);
convolve[x][y] = Math.Min(Math.Max((sum / factor) + offset, min), max);
string str = string.Empty;
}
}
return convolve;
}
public static double Sum(double[][] paddedImage1, double[][] mask1, int startX, int startY)
{
double sum = 0;
int maskWidth = mask1.Length;
for (int x = startX; x < (startX + maskWidth); x++)
{
var maskHeight = mask1[maskWidth - x + startX - 1].Length;
for (int y = startY; y < (startY + maskHeight); y++)
{
double img = paddedImage1[x][y];
double msk = mask1[maskWidth - x + startX - 1][maskHeight - y + startY - 1];
sum = sum + (img * msk);
}
}
return sum;
}
public static double GetFactor(double[][] kernel)
{
double sum = 0.0;
int width = kernel.Length;
for (int x = 0; x < width; x++)
{
var height = kernel[x].Length;
for (int y = 0; y < height; y++)
{
sum += kernel[x][y];
}
}
return (sum == 0) ? 1 : sum;
}
And I think it can improved more with application of SIMD operations.
I am calculating the average of the RGB channels of images in C# and matlab and getting slightly different result?? (am using 0-255 pixel values...)
The difference is not large but I just can't seem to understand the reason...
Is this common?? Or is it due to bitmap implementation of image?? Or precision issue?? Or does it mean their is something wrong with my code??
Code:
Matlab
I = imread('Photos\hv2512.jpg');
Ir=double(I(:,:,1));
Ig=double(I(:,:,2));
Ib=double(I(:,:,3));
avRed=mean2(Ir)
avGn=mean2(Ig)
avBl=mean2(Ib)
C#
Bitmap bmp= new Bitmap(open.FileName)
double[,] Red = new double[bmp.Width, bmp.Height];
double[,] Green = new double[bmp.Width, bmp.Height];
double[,] Blue = new double[bmp.Width, bmp.Height];
int PixelSize = 3;
BitmapData bmData = null;
if (Safe)
{
Color c;
for (int j = 0; j < bmp.Height; j++)
{
for (int i = 0; i < bmp.Width; i++)
{
c = bmp.GetPixel(i, j);
Red[i, j] = (double) c.R;
Green[i, j] = (double) c.G;
Blue[i, j] = (double) c.B;
}
}
}
double avRed = 0, avGrn = 0, avBlue = 0;
double sumRed = 0, sumGrn = 0, sumBlue = 0;
int cnt = 0;
for (int rws = 0; rws < Red.GetLength(0); rws++)
for (int clms = 0; clms < Red.GetLength(1); clms++)
{
sumRed = sumRed + Red[rws, clms];
sumGrn = sumGrn + Green[rws, clms];
sumBlue = sumBlue + Blue[rws, clms];
cnt++;
}
avRed = sumRed / cnt;
avGrn = sumGrn / cnt;
avBlue = sumBlue / cnt;
This is the image I am using
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.
.
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)