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.
Related
I'm working on image processing in C#.
I have a problem getting the performance of offset large memory access in C#.
The speed is significantly different from the same size of zero offset memory.
In the case of C ++, the difference was not as great as in C#.
Can you please tell me why my code has this problem ?
Also, are there any solutions ?
Source
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Numerics;
namespace Test
{
class Program
{
unsafe static void Main(string[] args)
{
var width = 8000;
var height = 8000;
// var data = new Vector4[height * width]; <- similar problem occur
var data = (Vector4*)Marshal.AllocHGlobal(height * width * sizeof(Vector4));
var data2 = (Vector4*)Marshal.AllocHGlobal(height * width * sizeof(Vector4));
// MATRIX
float m11 = .7297023F, m12 = 0, m13 = 0, m14 = 0, m21 = 0, m22 = .6109577F,
m23 = 0, m24 = 0, m31 = 0, m33 = .597218F, m32 = 0, m34 = 0, m41 = 0, m42 = 0,
m43 = 0, m44 = 1F, m51 = .105F, m52 = .145F, m53 = .155F, m54 = 0;
var sw = new Stopwatch();
sw.Start();
for (int y = 0; y < height; ++y)
{
var offset = width * y;
for (int x = 0; x < width; ++x)
{
// Slow ( 600ms )
ref var sData = ref data[offset + x];
ref var dData = ref data2[offset + x];
// Fast ( 200ms )
// ref var sData = ref data[x];
// ref var dData = ref data2[x];
float b = sData.X;
float g = sData.Y;
float r = sData.Z;
float a = sData.W;
dData.X = (b * m11) + (g * m21) + (r * m31) + (a * m41) + m51;
dData.Y = (b * m12) + (g * m22) + (r * m32) + (a * m42) + m52;
dData.Z = (b * m13) + (g * m23) + (r * m33) + (a * m43) + m53;
dData.W = (b * m14) + (g * m24) + (r * m34) + (a * m44) + m54;
}
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
Marshal.FreeHGlobal((IntPtr)data);
Marshal.FreeHGlobal((IntPtr)data2);
}
}
}
When using managed array pointer
var array1 = new Vector4[width * height];
var array2 = new Vector4[width * height];
fixed (Vector4* data = &array1[0])
fixed (Vector4* data2 = &array2[0])
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
// Slow ( 600ms )
ref var sData = ref data[width * y + x];
ref var dData = ref data2[width * y + x];
Offsetting the pointer in the outer loop
(A little improvement)
for (int y = 0; y < height; ++y)
{
var offsetData1 = data + width * y;
var offsetData2 = data2 + width * y;
for (int x = 0; x < width; ++x)
{
// Slow ( 470ms )
ref var sData = ref offsetData1[x];
ref var dData = ref offsetData2[x];
C++ ver
#include <iostream>
#include <chrono>
struct Vector4 {
float X = 0;
float Y = 0;
float Z = 0;
float W = 0;
};
int main()
{
long width = 8000;
long height = 8000;
auto buffer = new Vector4[width * height];
auto buffer2 = new Vector4[width * height];
// MATRIX
float m11 = .7297023F, m12 = 0, m13 = 0, m14 = 0, m21 = 0, m22 = .6109577F,
m23 = 0, m24 = 0, m31 = 0, m33 = .597218F, m32 = 0, m34 = 0, m41 = 0, m42 = 0,
m43 = 0, m44 = 1, m51 = .105F, m52 = .145F, m53 = .155F, m54 = 0;
std::chrono::system_clock::time_point start, end;
start = std::chrono::system_clock::now();
for (int y = 0; y < height; ++y)
{
int offset = width * y;
for (int x = 0; x < width; ++x)
{
Vector4& sData = buffer[offset + x];
Vector4& dData = buffer2[offset + x];
float b = sData.X;
float g = sData.Y;
float r = sData.Z;
float a = sData.W;
dData.X = (b * m11) + (g * m21) + (r * m31) + (a * m41) + m51;
dData.Y = (b * m12) + (g * m22) + (r * m32) + (a * m42) + m52;
dData.Z = (b * m13) + (g * m23) + (r * m33) + (a * m43) + m53;
dData.W = (b * m14) + (g * m24) + (r * m34) + (a * m44) + m54;
}
}
end = std::chrono::system_clock::now();
double elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << elapsed << "\n";
delete[] buffer;
delete[] buffer2;
}
Benchmarks
Lang
description
time (ms)
C#
Zero offset pointer
600ms
C#
Offset pointer
200ms
C++
Zero offset pointer
190ms
C++
Offset pointer
260ms
C#
Offsetting the pointer in the outer loop
370ms
C#
Managed array pointer with offset
990ms
Other info
IL in SharpLab
CPU
Intel Core i7-6700k
Memory
DDR4 16GB
OS
Windows 10 20H2
Runtime
.NET 5
Lang ver
C# 9
Platform
X64
I got an array of data voltages and I want to get the RMS value from the FFT that has been applied before to that data. I've seen that RMS in time domain should be equal to RMS(fft) / sqrt(nFFT) from Parseval's Theorem, but gives me different results. I'm using these functions:
1)FFT
public static VectorDPoint FFT(double[] trama, double samplingFreq)
{
double fs = samplingFreq; // Sampling frequency
double t1 = 1 / fs; // Sample time
int l = trama.Length; // Length of signal
// Time vector
//Vector t = Normal(0, l, 1) * t1;
//// Values vector
//Vector y = new Vector(trama);
// We just use half of the data as the other half is simetric. The middle is found in NFFT/2 + 1
int nFFT = (int)Math.Pow(2, NextPow2(l));
if (nFFT > 655600)
{ }
// Create complex array for FFT transformation. Use 0s for imaginary part
Complex[] samples = new Complex[nFFT];
for (int i = 0; i < nFFT; i++)
{
if (i >= trama.Length)
{
samples[i] = new MathNet.Numerics.Complex(0, 0);
}
else
{
samples[i] = new MathNet.Numerics.Complex(trama[i], 0);
}
}
ComplexFourierTransformation fft = new ComplexFourierTransformation(TransformationConvention.Matlab);
fft.TransformForward(samples);
ComplexVector s = new ComplexVector(samples);
s = s / l;
Vector f = (fs / 2.0) * Linspace(0, 1, (nFFT / 2) + 1);
VectorDPoint result = new VectorDPoint();
for (int i = 0; i < (nFFT / 2) + 1; i++)
{
result.Add(new DPoint(f[i], 2 * s[i].Modulus));
}
s = null;
f = null;
samples = null;
return result;
2) RMS
public static double RMSCalculate(double[] channelValues, int samplesNumber, double sampleRate, DateTime currentDate)
{
double[] times = new double[channelValues.Length];
double sampleTime = 0.0;
double period = 0;
times[0] = currentDate.Second + currentDate.Millisecond / 1000.0;
sampleTime = 1 / sampleRate; //s
// Limited samples
for (int i = 1; i < channelValues.Length; i++)
{
times[i] = times[i - 1] + sampleTime;
}
DPoint RMSValues = new DPoint();
RMSValues.Y = 0;
if (channelValues.Length == 1)
{
double x = channelValues[0];
double y = channelValues[0];
RMSValues = new DPoint(x, Math.Abs(y));
}
else
{
for (int i = 0; i < times.Length - 1; i++)
{
period = 0;
if (i + 1 < times.Length)
{
RMSValues.Y += channelValues[i + 1] * channelValues[i + 1] * (times[i + 1] - times[i]);
}
}
period = times[times.Length - 1] - times[0];
RMSValues.Y = RMSValues.Y / period;
RMSValues.Y = Math.Sqrt(RMSValues.Y);
}
return RMSValues.Y;
}
I purchased the book Numerical Methods, Algorithms and Tools in C# by Waldemar Dos Passos.
On page 463, there is the method:
public static double FactorialLn(int n)
{
if(n < 0)
{
throw new Exception("Input value must be > 0");
}
else
{
return GammaLn(n+1.0);
}
}
I found the namespace where the function GammaLn resides: MicrosoftResearch.Infer.Maths
but Visual Studio 2010 does not recognize it and I was unable to find it in the .NET reference.
Please help me get the program to compile.
Thank you in advance.
I finally found the dource code of the GammaLn method itself here: http://seungwon.tistory.com/9
One does need nothing else when one possesses the very real thing, as Andrey correctly pointed out, but until then ...
I reproduce the method here for future reference, in case the site above stops from functioning.
/// <summary>
/// http://seungwon.tistory.com/9
/// GammaLn函数
/// </summary>
/// <param name="x"></param>
/// <returns></returns>
public static double GammaLn(double x)
{
double result = 0.0;
double d1 = -5.772156649015328605195174e-1;
double[,] p1 = {{4.945235359296727046734888e0},{ 2.018112620856775083915565e2},
{2.290838373831346393026739e3},{ 1.131967205903380828685045e4},
{2.855724635671635335736389e4},{ 3.848496228443793359990269e4},
{2.637748787624195437963534e4},{ 7.225813979700288197698961e3}};
double[,] q1 = {{6.748212550303777196073036e1},{ 1.113332393857199323513008e3},
{7.738757056935398733233834e3},{ 2.763987074403340708898585e4},
{5.499310206226157329794414e4},{ 6.161122180066002127833352e4},
{3.635127591501940507276287e4},{ 8.785536302431013170870835e3}};
double d2 = 4.227843350984671393993777e-1;
double[,] p2 = {{4.974607845568932035012064e0},{ 5.424138599891070494101986e2},
{1.550693864978364947665077e4},{ 1.847932904445632425417223e5},
{1.088204769468828767498470e6},{ 3.338152967987029735917223e6},
{5.106661678927352456275255e6},{ 3.074109054850539556250927e6}};
double[,] q2 = {{1.830328399370592604055942e2},{ 7.765049321445005871323047e3},
{1.331903827966074194402448e5},{ 1.136705821321969608938755e6},
{5.267964117437946917577538e6},{ 1.346701454311101692290052e7},
{1.782736530353274213975932e7},{ 9.533095591844353613395747e6}};
double d4 = 1.791759469228055000094023e0;
double[,] p4 = {{1.474502166059939948905062e4},{ 2.426813369486704502836312e6},
{1.214755574045093227939592e8},{ 2.663432449630976949898078e9},
{2.940378956634553899906876e10},{ 1.702665737765398868392998e11},
{4.926125793377430887588120e11},{5.606251856223951465078242e11}};
double[,] q4 = {{2.690530175870899333379843e3},{ 6.393885654300092398984238e5},
{4.135599930241388052042842e7},{ 1.120872109616147941376570e9},
{1.488613728678813811542398e10},{1.016803586272438228077304e11},
{3.417476345507377132798597e11},{ 4.463158187419713286462081e11}};
double[,] c = {{-1.910444077728e-03},{ 8.4171387781295e-04},
{-5.952379913043012e-04},{ 7.93650793500350248e-04},
{-2.777777777777681622553e-03},{ 8.333333333333333331554247e-02},
{ 5.7083835261e-03}};
double eps = 2.2204e-016;
if (x <= 0)
{
//报错!
}
else
{
double xden = 0.0;
double xnum = 0.0;
result = x;
if (x > 0 && x <= eps)
{
result = -Math.Log(x);
}
else if ((x > eps) && (x <= 0.5))
{
double y = x;
xden = 1;
xnum = 0;
for (int i = 0; i < 8; i++)
{
xnum = xnum * y + p1[i, 0];
xden = xden * y + q1[i, 0];
}
result = -Math.Log(y) + (y * (d1 + y * (xnum / xden)));
}
else if ((x > 0.5) && (x <= 0.6796875))
{
double xm1 = (x - 0.5) - 0.5;
xden = 1;
xnum = 0;
for (int i = 0; i < 8; i++)
{
xnum = xnum * xm1 + p2[i, 0];
xden = xden * xm1 + q2[i, 0];
}
result = -Math.Log(x) + xm1 * (d2 + xm1 * (xnum / xden));
}
else if ((x > 0.6796875) && (x <= 1.5))
{
double xm1 = (x - 0.5) - 0.5;
xden = 1;
xnum = 0;
for (int i = 0; i < 8; i++)
{
xnum = xnum * xm1 + p1[i, 0];
xden = xden * xm1 + q1[i, 0];
}
result = xm1 * (d1 + xm1 * (xnum / xden));
}
else if ((x > 1.5) && (x <= 4))
{
double xm2 = x - 2;
xden = 1;
xnum = 0;
for (int i = 0; i < 8; i++)
{
xnum = xnum * xm2 + p2[i, 0];
xden = xden * xm2 + q2[i, 0];
}
result = xm2 * (d2 + xm2 * (xnum / xden));
}
else if ((x > 4) && (x <= 12))
{
double xm4 = x - 4;
xden = -1;
xnum = 0;
for (int i = 0; i < 8; i++)
{
xnum = xnum * xm4 + p4[i, 0];
xden = xden * xm4 + q4[i, 0];
}
result = d4 + xm4 * (xnum / xden);
}
else if (x > 12)
{
double y = x;
double r = c[6, 0];// 等于:double r = repmat(c[6, 0], 1)[0,0];
double ysq = y * y;
for (int i = 0; i < 6; i++)
{
r = r / ysq + c[i, 0];
}
r = r / y;
double corr = Math.Log(y);
double spi = 0.9189385332046727417803297;
result = r + spi - 0.5 * corr + y * (corr - 1);
}
}
return result;
}
I am trying to calculate the value of a single dimensional Array, here is my code:
So when I click "Detect", it should start a threshold through my Image, beginning from i = 0 to Image height and from j = 0 to Image width:
public void detektieren_Click(object sender, RoutedEventArgs e)
{
for (i = 0; i < bitmap.Height; i++)
{
for (j = 0; j < bitmap.Width; j++)
{
stride = bitmap.PixelWidth * (bitmap.Format.BitsPerPixel / 8);
data = new byte[stride * bitmap.PixelHeight];
bitmap.CopyPixels(data, stride, 0);
index = i * stride + 4 * j;
Now accessing the ARGB data:
byte A = data[index + 3];
byte R = data[index + 2];
byte G = data[index + 1];
byte B = data[index];
After the threshold, if there are any Pixels meet the condition R=0 & G=0 & B=255:
if (Convert.ToInt32(R) == 0 && Convert.ToInt32(G) == 0 && Convert.ToInt32(B) == 255)
{
// Create a writer and open the file:
StreamWriter Messdaten;
if (!File.Exists("C:/Users/.../Messdaten.csv"))
{
Messdaten = new StreamWriter("C:/Users/.../Messdaten.csv");
}
else
{
Messdaten = File.AppendText("C:/Users/.../Messdaten.csv");
}
// Write to the file:
Messdaten.WriteLine(j + ";" + i);
// Close the stream:
Messdaten.Close();
for (y = 0; y < bitmap.Height; y++)
{
for (x = 0; x < bitmap.Width; x++)
{
double x_mw = 0; double y_mw = 0;
int[] x_array = new int[(int)bitmap.Width];
int[] y_array = new int[(int)bitmap.Height];
x_array[x] = j;
x_mw = x_array.Average();
y_array[y] = i;
y_mw = y_array.Average();
xy_coord.Content = (int) x_mw + ";" + (int) y_mw;
}
}
}
}
}
}
Everything works perfectly in the CSV file, I can detect a Pixel (e.g. blue with R=0 G=0 B=255). But I also want to copy the data of each single Pixel into Array. But apparently it doesn't really deliver what I want. It doesn't calculate the average value of sum of blue Pixels (= the centroid of the blue Pixels scatter), instead it just Shows x_mw = 0 and y_mw = 0. What did I do wrong?
After I did some modification it works. So this is the code:
public void detektieren_Click(object sender, RoutedEventArgs e)
{
int x_sum = 0; int y_sum = 0; int x_count = 0; int y_count = 0; int x_mw; int y_mw;
int[] x_array = new int[(int)bitmap.Width];
int[] y_array = new int[(int)bitmap.Height];
int[] x_array_copy = new int[(int)bitmap.Width];
int[] y_array_copy = new int[(int)bitmap.Height];
stride = bitmap.PixelWidth * (bitmap.Format.BitsPerPixel / 8);
data = new byte[stride * bitmap.PixelHeight];
bitmap.CopyPixels(data, stride, 0);
for (i = 0; i < (int) bitmap.Height; i++)
{
for (j = 0; j < (int) bitmap.Width; j++)
{
index = i * stride + 4 * j;
byte A = data[index + 3];
byte R = data[index + 2];
byte G = data[index + 1];
byte B = data[index];
if (Convert.ToInt32(R) == 0 && Convert.ToInt32(G) == 0 && Convert.ToInt32(B) == 255)
{
x_array[j] = j;
x_count++;
x_array_copy[j] = x_array_copy[j] + j;
x_sum = (int) x_array_copy.Sum();
x_mw = x_sum / x_count;
y_array[i] = i;
y_count++;
y_array_copy[i] = y_array_copy[i] + i;
y_sum = (int) y_array_copy.Sum();
y_mw = y_sum / y_count;
xy_coord.Content = x_mw + ";" + y_mw;
}
}
}
}
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)