In my application I read RGB pixel values from several images using fast unmanaged code and then convert them to HSB colors. Now I'd like to build an HSB histogram using the following partitions:
Hue: 18 partitions, resulting in intervals of 20 from 0...360
Saturation: 3 partitions, resulting in intervals of 0,33 from 0...1
Brightness: 3 partitions, resulting in intervals of 0,33 from 0...1
So my histogram has a total of 18*3*3=162 partitions (bins) which consist of the lower interval borders for each channel:
Bin1: [0, 0, 0]
Bin2: [0, 0, 0.33]
Bin3: [0, 0, 0.66]
Bin4: [0, 0.33, 0]
Bin5: [0, 0.33, 0.33]
...
Bin162: [340, 0.66, 0.66]
I implemented this pretending that each bin would be an HSB color itself. So I calculated the bin interval borders, created HsbColor instances from those values and put the colors (wrapped in the HsbHistogramBin class) in a simple list.
When adding a new HsbColor to my histogram, I use the following code to determine which bin I need to increment:
private HsbHistogramBin FindBin(HsbColor color)
{
HsbHistogramBin bin = null;
bool foundBin = false;
for (int i = Bins.Count - 1; i >= 0; i--)
{
bin = Bins[i];
if (bin.Color.Hue > color.Hue)
continue;
if (bin.Color.Saturation > color.Saturation)
continue;
if (bin.Color.Brightness > color.Brightness)
continue;
foundBin = true;
break;
}
return foundBin ? bin : null;
}
public void AddColor(HsbColor color)
{
FindBin(color).Value++;
}
Obviously this is way too slow. In a worst-case scenario, each pixel needs 162 iterations to find its bin which results in at least millions of iterations for one single image.
My question is: How can I speed this data structure up so that I can immediately find the right bin for my pixels? A simple array with the length of 162 might work but how do I calculate the right bin index for a given pixel that isn't yet reduced to the mentioned partitions and might contain values like [259.234, 0.5634, 0.90534]?
Why not just simply use a 3 dimensional array? Like so:
int[,,] histogram = new int[18, 3, 3];
// initialize to 0
for(int h = 0; h < 18; h++) {
for(int s = 0; s < 3; s++) {
for(int b = 0; b < 3; b++) {
histogram[h, s, b] = 0;
}
}
}
// foreach pixel...
HsbColor c = ... // color of pixel
int h = (int)(c.Hue / 20);
int s = (int)(c.Saturation * 3);
int b = (int)(c.Brighthess * 3);
// take care of boundary cases (Hue, Saturation or Brightness maxed out)
if(h >= 18) h = 17;
if(s >= 3) s = 2;
if(b >= 3) b = 2;
histogram[h, s, b]++;
NB: I'm assuming here that your total pixel count (more precisely, the maximum number of pixels that will fall into 1 bin) will not exceed int.MaxValue. Otherwise, consider using long datatype for the histogram instead of int.
You can convert your HSV number into one unsigned long like so:
ulong colorLookupValue = (color.Hue/18) * 9 + (ulong)((color.Saturation*3) * 3) + (ulong)(color.Brightness * 3)
This is your bin index.
Related
I need to apply a 1d gaussian filter to a list of floats in c#, ie, to smooth a graph.
I got as far as simply averaging each value with n neighbors, but the result wasn't quite right and so I discovered that I need to apply a normal distribution weight to the contributions of the values per iteration.
I can't find a library like scipy that has a function for this, and I don't quite understand the algebraic formulas I have found for computing a gaussian kernal. Examples are generally geared towards a 2D implementation for images.
Can anyone suggest the modifications that would need to be made to the following code to achieve the proper gaussian effect?
public static List<float> MeanFloats(List<float> floats, int width)
{
List<float> results = new List<float>();
if (width % 2 == 0)
width -= 1; // make sure width is odd
int halfWidthMinus1 = width / 2; // width is known to be odd, divide by 2 will round down
for (int i = 0; i < floats.Count; i++) // iterate through all floats in list
{
float result = 0;
for (int j = 0; j < width; j++)
{
var index = i - halfWidthMinus1 + j;
index = math.max(index, 0); // clamp index - the first and last elements of the list will be used when the algorithm tries to access outside the bounds of the list
index = math.min(index, floats.Count-1);
result += floats[index]; // multiply with kernal here??
}
result /= width; // calculate mean
results.Add(result);
}
return results;
}
If relevant this is for use in a Unity game.
A 1-dimensional Gaussian Kernel is defined as
where sigma is the standard deviation of your list, and x is the index distance.
You then create a kernel by filling each of its array slots with a multiplier. Here is an (untested) example:
private static float[] GaussianKernel(int width, float sigma)
{
float[] kernel = new float[width + 1 + width];
for (int i = -width; i <= width; i++)
{
kernel[width + i] = Mathf.Exp(-(i * i) / (2 * sigma * sigma)) / (Math.PI * 2 * sigma * sigma);
}
return kernel;
}
In your smoothing function you apply this multiplier to the floats[index] value. Finally, before adding the result, instead of dividing it by the width, you divide it by the total sum of the kernel weights (the values of the kernel array).
You could compile the values of the current kernel weight during each iteration in your j-loop weightSum += kernel[j].
I wish to create a function AllCombnations(d, maxValue) which will create a d-dimensions array of all number combinations from 0 to maxValue.
For example, a hardcoded version of creating all number combinations in 3D space, from 0 to maxValue would possibly be something like:
for (int i = 0; i < maxValue; i++)
for (int j = 0; j < maxValue; j++)
for (int k = 0; k < maxValue; k++)
{
// code here
}
The issue I face is that I cannot nest n for loops, and am unsure how I would go about this. I have considered recursion, but have had no success. Any help would be greatly appreciated.
Actually, you can loop over dimensions. Please, have a look at Array class
Demo:
// [6, 6, 6] array
int rank = 3; // 3D array - 3 dimensions
int maxValue = 6; // Each dimension is of size 6
int[] lengths = Enumerable // {6, 6, 6} - lengths of the dimensions:
.Repeat(maxValue, rank) // rank times maxValue
.ToArray(); // materialized as array
//TODO: put the right type of arrays' items
// In demo, let array be of type string: "string[6, 6, 6] array"
var array = Array.CreateInstance(typeof(string), lengths);
// we can't use hardcoded set (i, j, k) of variables
// we have to address array's item via array of rank length
int[] address = new int[array.Rank];
// Single loop over all array's items (and dimensions)
do {
//TODO: put the right value here by given address:
// (i == address[0], j == address[1], k == address[2] etc.)
array.SetValue(
string.Concat(address.Select(i => (char) (i + 'A'))), // value: "AAA", "AAB" etc.
address); // address: [0,0,0], [0,0,1],
// here we compute next address
for (int i = 0; i < address.Length; ++i)
if (address[i] >= array.GetLength(i) - 1)
address[i] = 0;
else {
address[i] += 1;
break;
}
// if we get {0, 0, ..., 0} address, we've exhausted all the items
}
while (!address.All(index => index == 0));
Let's have a look at the array (20 top items):
Console.WriteLine(string.Join(Environment.NewLine, array.OfType<string>().Take(20)));
Outcome:
AAA
AAB
AAC
AAD
AAE
AAF
ABA
ABB
ABC
ABD
ABE
ABF
ACA
ACB
ACC
ACD
ACE
ACF
ADA
ADB
I know this is an old post now, but I DID create a solution to this problem.
Let me go through this issue with an example script.
class Program
{
static void Main()
{
// Print all combinations from a to b, for n dimensions
// e.g. 0000 to 2222 <- each dimension goes from 0 to 2, with 4 dimensions
// Note that each dimension can have a unique start/end point
// e.g. 1234 to 5678, so the 2nd dimensions is bound 2 <= x <= 6
int dimensions = 4;
int[] startValues = { 0, 0, 0, 0 };
int[] endValues = { 2, 2, 2, 2 };
PrintCombinations(startValues, endValues, dimensions);
Console.ReadKey();
}
/// <summary>
/// Prints all combinations of numbers given inputs
/// </summary>
/// <param name="start">Inclusive stating integers</param>
/// <param name="end">Inclusive ending integers</param>
/// <param name="dimensions">The number of dimensions to iterate</param>
private static void PrintCombinations(int[] startValues, int[] endValues, int dimensions)
{
// Create new array to loop through without disturbing the original array
int[] loopArray = (int[])startValues.Clone();
// Loop through each value
while (!Enumerable.SequenceEqual(loopArray, endValues))
{
// Write array to console
Console.WriteLine($"{string.Join(", ", loopArray)}");
// Increment array
loopArray[0]++;
// Check if a dimension is larger than it's maximum, then set to min, and add +1 to next dimension
// Do not do this for last dimension, as loop will break once the final combination is met
for (int i = 0; i < dimensions - 1; i++)
if (loopArray[i] > endValues[i])
{
loopArray[i] = startValues[i];
loopArray[i + 1]++;
}
}
// Write final array combination to console
Console.WriteLine($"{string.Join(", ", loopArray)}");
}
}
This is a simple enough example to show how exactly I wanted to expand on the idea of "multiple dimensions" represented as an array.
If you look to the bottom of PrintCombinations, you will see the following code:
for (int i = 0; i < dimensions - 1; i++)
if (loopArray[i] > endValues[i])
{
loopArray[i] = startValues[i];
loopArray[i + 1]++;
}
This is the code I come up with the loop through multiple dimensions, removing the need to hard-code loops when you have user submitted dimensions and other information (as shown in the upper example).
Basically, this code stores the VALUE of each dimension in an array.
Let us do an example of 3 dimensions, (x, y, z).
We can say the point (x, y, z) = int[] { x, y, z }
If we say x, y, and z are the upper bound of the array, we can loop through this array by subtracting the array's first dimesnsion, until it reaches zero, then remove one from the following dimension until it reaches zero, etc, all while resetting the dimension to the upper bound when doing so, or as in this example, add from zero to an upper bound, then reset to zero, and increment the following dimension.
By using further arrays for upper and lower bounds, you can essentially make nested loops between two specific ranges. In the above example, I used an upper bound of { 2, 2, 2, 2 }.
I hope I have explained this well. Thanks
I'm new to this whole audio processing area and I'm wondering how to extract Bass, Mid and treble from an FFT output. I'm currently using this to get the data: https://stackoverflow.com/a/20414331/2714577 which uses Naudio.
But I'm using a fftlength of 1024 (require speed). I'm trying to get these 3 sections in a format such as 0-255 for colour purposes.
I currently have this:
double[] data = new double[512];
void FftCalculated(object sender, FftEventArgs e)
{
for (int j = 0; j < e.Result.Length / 2; j++)
{
double magnitude = Math.Sqrt(e.Result[j].X * e.Result[j].X + e.Result[j].Y * e.Result[j].Y);
double dbValue = 20 * Math.Log10(magnitude);
data[j] = dbValue;
}
double d = 0;
for (int i = 20; i < 89; i++)
{
d += data[i];
}
double m = 0;
for (int i = 150; i < 255; i++)
{
m += data[i];
}
double t = 0;
for (int i = 300; i < 512; i++)
{
t += data[i];
}
Debug.Message(""+d+" |||| "+m+" |||| "+t);
}
Which returns:
Is this right? How do I get this data to something more usable?
The coefficients you get out of a Fourier transform can be positive or negative - what you're interested in is the magnitude (ie. the amount of each frequency), so you will want to take the absolute value in your summation.
Also, I would recommend normalizing - at the end of your summation do this:
double total = data.Sum(x => Math.Abs(x));
d /= total;
m /= total;
t /= total;
This way, your numbers will be confined to the range [0-1) and you will get the same information out if the sound is quieter (unless you don't want that). Actually, the range will be somewhat less than that because each of your summations covers a smaller individual range. So you may want to scale them by the largest one of them:
double largest = Math.Max(d, m, t);
d /= largest;
m /= largest;
t /= largest;
Now the range of each should be between 0 and 1. You can then multiply by 255 or 256 and truncate the decimal if you like.
The downside of the last step is if the values are all zero (because the inputs were all zero) then you will divide by zero. Oops! At this point you need to decide exactly what you want.. If you don't do this scaling, then a sound which is entirely treble (according to your breakdown above) will have (0,0,1) for (d,m,t). But a sound which is an even mixture of the three will be (0.3333, 0.3333, 0.3333) for (d,m,t). And a sound which is completely quiet would be (0,0,0). If that's not what you want, well then you need to define exactly what you want before I could help you any further.
Your dbValue is already a very good number, maesuring the level in decibel relative to 1.0 which becomes 0.0 dB
You should average instead of sum the individual (dB-Values at various) frequencies.
Then map the dB Range of about -80db .. 0.0dB to your color range.
Also note: Speach and music tend to have an average pink noise spectrum. This means that low frequencies tend to have higher dB than high frequencies.
You should compensate for this effect (probably before averaging the frequencies) to get a "better" display.
I need some help with optimisation of my CCL algorithm implementation. I use it to detect black areas on the image. On a 2000x2000 it takes 11 seconds, which is pretty much. I need to reduce the running time to the lowest value possible to achieve. Also, I would be glad to know if there is any other algorithm out there which allows you to do the same thing, but faster than this one. So here is my code:
//The method returns a dictionary, where the key is the label
//and the list contains all the pixels with that label
public Dictionary<short, LinkedList<Point>> ProcessCCL()
{
Color backgroundColor = this.image.Palette.Entries[1];
//Matrix to store pixels' labels
short[,] labels = new short[this.image.Width, this.image.Height];
//I particulary don't like how I store the label equality table
//But I don't know how else can I store it
//I use LinkedList to add and remove items faster
Dictionary<short, LinkedList<short>> equalityTable = new Dictionary<short, LinkedList<short>>();
//Current label
short currentKey = 1;
for (int x = 1; x < this.bitmap.Width; x++)
{
for (int y = 1; y < this.bitmap.Height; y++)
{
if (!GetPixelColor(x, y).Equals(backgroundColor))
{
//Minumum label of the neighbours' labels
short label = Math.Min(labels[x - 1, y], labels[x, y - 1]);
//If there are no neighbours
if (label == 0)
{
//Create a new unique label
labels[x, y] = currentKey;
equalityTable.Add(currentKey, new LinkedList<short>());
equalityTable[currentKey].AddFirst(currentKey);
currentKey++;
}
else
{
labels[x, y] = label;
short west = labels[x - 1, y], north = labels[x, y - 1];
//A little trick:
//Because of those "ifs" the lowest label value
//will always be the first in the list
//but I'm afraid that because of them
//the running time also increases
if (!equalityTable[label].Contains(west))
if (west < equalityTable[label].First.Value)
equalityTable[label].AddFirst(west);
if (!equalityTable[label].Contains(north))
if (north < equalityTable[label].First.Value)
equalityTable[label].AddFirst(north);
}
}
}
}
//This dictionary will be returned as the result
//I'm not proud of using dictionary here too, I guess there
//is a better way to store the result
Dictionary<short, LinkedList<Point>> result = new Dictionary<short, LinkedList<Point>>();
//I define the variable outside the loops in order
//to reuse the memory address
short cellValue;
for (int x = 0; x < this.bitmap.Width; x++)
{
for (int y = 0; y < this.bitmap.Height; y++)
{
cellValue = labels[x, y];
//If the pixel is not a background
if (cellValue != 0)
{
//Take the minimum value from the label equality table
short value = equalityTable[cellValue].First.Value;
//I'd like to get rid of these lines
if (!result.ContainsKey(value))
result.Add(value, new LinkedList<Point>());
result[value].AddLast(new Point(x, y));
}
}
}
return result;
}
Thanks in advance!
You could split your picture in multiple sub-pictures and process them in parallel and then merge the results.
1 pass: 4 tasks, each processing a 1000x1000 sub-picture
2 pass: 2 tasks, each processing 2 of the sub-pictures from pass 1
3 pass: 1 task, processing the result of pass 2
For C# I recommend the Task Parallel Library (TPL), which allows to easily define tasks depending and waiting for each other. Following code project articel gives you a basic introduction into the TPL: The Basics of Task Parallelism via C#.
I would process one scan line at a time, keeping track of the beginning and end of each run of black pixels.
Then I would, on each scan line, compare it to the runs on the previous line. If there is a run on the current line that does not overlap a run on the previous line, it represents a new blob. If there is a run on the previous line that overlaps a run on the current line, it gets the same blob label as the previous. etc. etc. You get the idea.
I would try not to use dictionaries and such.
In my experience, randomly halting the program shows that those things may make programming incrementally easier, but they can exact a serious performance cost due to new-ing.
The problem is about GetPixelColor(x, y), it take very long time to access image data.
Set/GetPixel function are terribly slow in C#, so if you need to use them a lot, you should use Bitmap.lockBits instead.
private void ProcessUsingLockbits(Bitmap ProcessedBitmap)
{
BitmapData bitmapData = ProcessedBitmap.LockBits(new Rectangle(0, 0, ProcessedBitmap.Width, ProcessedBitmap.Height), ImageLockMode.ReadWrite, ProcessedBitmap.PixelFormat);
int BytesPerPixel = System.Drawing.Bitmap.GetPixelFormatSize(ProcessedBitmap.PixelFormat) / 8;
int ByteCount = bitmapData.Stride * ProcessedBitmap.Height;
byte[] Pixels = new byte[ByteCount];
IntPtr PtrFirstPixel = bitmapData.Scan0;
Marshal.Copy(PtrFirstPixel, Pixels, 0, Pixels.Length);
int HeightInPixels = bitmapData.Height;
int WidthInBytes = bitmapData.Width * BytesPerPixel;
for (int y = 0; y < HeightInPixels; y++)
{
int CurrentLine = y * bitmapData.Stride;
for (int x = 0; x < WidthInBytes; x = x + BytesPerPixel)
{
int OldBlue = Pixels[CurrentLine + x];
int OldGreen = Pixels[CurrentLine + x + 1];
int OldRed = Pixels[CurrentLine + x + 2];
// Transform blue and clip to 255:
Pixels[CurrentLine + x] = (byte)((OldBlue + BlueMagnitudeToAdd > 255) ? 255 : OldBlue + BlueMagnitudeToAdd);
// Transform green and clip to 255:
Pixels[CurrentLine + x + 1] = (byte)((OldGreen + GreenMagnitudeToAdd > 255) ? 255 : OldGreen + GreenMagnitudeToAdd);
// Transform red and clip to 255:
Pixels[CurrentLine + x + 2] = (byte)((OldRed + RedMagnitudeToAdd > 255) ? 255 : OldRed + RedMagnitudeToAdd);
}
}
// Copy modified bytes back:
Marshal.Copy(Pixels, 0, PtrFirstPixel, Pixels.Length);
ProcessedBitmap.UnlockBits(bitmapData);
}
Here is the basic code to access pixel data.
And I made a function to transform this into a 2D matrix, it's easier to manipulate (but little slower)
private void bitmap_to_matrix()
{
unsafe
{
bitmapData = ProcessedBitmap.LockBits(new Rectangle(0, 0, ProcessedBitmap.Width, ProcessedBitmap.Height), ImageLockMode.ReadWrite, ProcessedBitmap.PixelFormat);
int BytesPerPixel = System.Drawing.Bitmap.GetPixelFormatSize(ProcessedBitmap.PixelFormat) / 8;
int HeightInPixels = ProcessedBitmap.Height;
int WidthInPixels = ProcessedBitmap.Width;
int WidthInBytes = ProcessedBitmap.Width * BytesPerPixel;
byte* PtrFirstPixel = (byte*)bitmapData.Scan0;
Parallel.For(0, HeightInPixels, y =>
{
byte* CurrentLine = PtrFirstPixel + (y * bitmapData.Stride);
for (int x = 0; x < WidthInBytes; x = x + BytesPerPixel)
{
// Conversion in grey level
double rst = CurrentLine[x] * 0.0721 + CurrentLine[x + 1] * 0.7154 + CurrentLine[x + 2] * 0.2125;
// Fill the grey matix
TG[x / 3, y] = (int)rst;
}
});
}
}
And the website where the code comes
"High performance SystemDrawingBitmap"
Thanks to the author for his really good job !
Hope this will help !
Sorry I had no idea how set a topic which could express what help I need.
I have in an array of bytes, values for each pixel from a bitmap. It is a one dimensional array, from left to right. It takes each row and add it to the end of array's index.
I would like to split a bitmap to 225(=15*15) pieces. Each brick has for example dimension 34x34 and the length of array is then 260100(=225*34*34). So as you see now we will need 15 bricks on width and on height.
Few months ago I was using two loops starting from 0 - 14. I wrote own long code to get all that 34x34 bricks. However I didn't used any array which was storing all values.
Now I have a one dimensional array because marshal copy and bitmapdata with bitlocks were the best way to fast copy all pixels' values to array.
But I stand face to face with problem how to get 34 elements then one row lower and another one knowing that on 35 level will be another brick with its own starting value..
PS. edit my post if something is not good.
Few people could say "first make any your test code". I tried that but what I got was just trash and I really don't know how to do that.
This method was used to crop image to smaller images containing bricks. But I don't want store small images of brick. I need values storing in array of bytes.
Under, there is a proof.
private void OCropImage(int ii, int jj, int p, int p2)
{
////We took letter and save value to binnary, then we search in dictionary by value
this.rect = new Rectangle();
this.newBitmap = new Bitmap(this.bitmap);
for (ii = 0; ii < p; ii++)
{
for (jj = 0; jj < p2; jj++)
{
////New bitmap
this.newBitmap = new Bitmap(this.bitmap);
////Set rectangle working area with letters
this.rect = new Rectangle(jj * this.miniszerokosc, ii * this.miniwysokosc, this.miniszerokosc, this.miniwysokosc);
////Cut single rectangle with letter
this.newBitmap = this.newBitmap.Clone(this.rect, this.newBitmap.PixelFormat);
////Add frame to rectangle to delet bad noise
this.OAddFrameToCropImage(this.newBitmap, this.rect.Width, this.rect.Height);
this.frm1.SetIm3 = (System.Drawing.Image)this.newBitmap;
////Create image with letter which constains less background
this.newBitmap = this.newBitmap.Clone(this.GetAreaLetter(this.newBitmap), this.newBitmap.PixelFormat);
////Count pixels in bitmap
this.workingArea = this.GetBinnary(this.newBitmap);
var keysWithMatchingValues = this.alphabetLetters.Where(x => x.Value == this.workingArea).Select(x => x.Key);
foreach (var key in keysWithMatchingValues)
{
this.chesswords += key.ToString();
}
}
this.chesswords += Environment.NewLine;
var ordered = this.alphabetLetters.OrderBy(x => x.Value);
}
}
PS2. sorry for my English, please correct it if it is needed.
If I get you right, then if you have an image like this
p00|p01|p02|...
---+---+-------
p10|p11|p12|...
---+---+-------
p20|p21|p22|...
---+---+---+---
...|...|...|...
Which is stored in an array in left-to-right row scan like this:
p00,p01,...,p0n, p10,p11,...,p1n, p20,p21, ...
If I understand you correctly, what you want to be able to do, is to take a given rectangle (from a certain x and y with a certain width and height) from the image. Here is code to do this, with explanations:
byte[] crop_area (byte[] source_image, int image_width, int image_height,
int start_x, int start_y, int result_width, int result_height)
{
byte[] result = new byte[result_width * result_height];
int endX = x + result_width;
int endY = y + result_height;
int pos = 0;
for (int y = startY; y < endY; y++)
for (int x = startX; x < endX; x++)
{
/* To get to the pixel in the row I (starting from I=1), we need
* to skip I-1 rows. Since our y indexes start from row 0 (not 1),
* then we don't need to subtract 1.
*
* So, the offset of the pixel at (x,y) is:
*
* y * image_width + x
* |-----------------------| |-----------------|
* Skip pixels of y rows Offset inside row
*/
result[pos] = source_image[y * image_width + x];
/* Advance to the next pixel in the result image */
pos++;
}
return result;
}
Then, to take the block in the row I and column J (I,J=0,...,14) do:
crop_area (source_image, image_width, image_height, J*image_width/15, I*image_height/15, image_width/15, image_height/15)