quantization (Reduction of colors of image) - c#

I am trying to quantize an image into 10 colors in C# and I have a problem in draw the quantized image, I have made the mapping table and it is correct, I have made a copy of the original image and I am changing the color of pixels based on the mapping table , I am using the below code:
bm = new Bitmap(pictureBox1.Image);
Dictionary<Color, int> histo = new Dictionary<Color, int>();
for (int x = 0; x < bm.Size.Width; x++)
for (int y = 0; y < bm.Size.Height; y++)
{
Color c = bm.GetPixel(x, y);
if (histo.ContainsKey(c))
histo[c] = histo[c] + 1;
else
histo.Add(c, 1);
}
var result1 = histo.OrderByDescending(a => a.Value);
int ind = 0;
List<Color> mostusedcolor = new List<Color>();
foreach (var entry in result1)
{
if (ind < 10)
{
mostusedcolor.Add(entry.Key);
ind++;
}
else
break;
}
Double temp_red,temp_green,temp_blue,temp;
Dictionary<Color, Double> dist = new Dictionary<Color, double>();
Dictionary<Color, Color> mapping = new Dictionary<Color, Color>();
foreach (var p in result1)
{
dist.Clear();
foreach (Color pp in mostusedcolor)
{
temp_red = Math.Pow((Convert.ToDouble(p.Key.R) - Convert.ToDouble(pp.R)), 2.0);
temp_green = Math.Pow((Convert.ToDouble(p.Key.G) - Convert.ToDouble(pp.G)), 2.0);
temp_blue = Math.Pow((Convert.ToDouble(p.Key.B) - Convert.ToDouble(pp.B)), 2.0);
temp = Math.Sqrt((temp_red + temp_green + temp_blue));
dist.Add(pp, temp);
}
var min = dist.OrderBy(k=>k.Value).FirstOrDefault();
mapping.Add(p.Key, min.Key);
}
Bitmap copy = new Bitmap(bm);
for (int x = 0; x < copy.Size.Width; x++)
for (int y = 0; y < copy.Size.Height; y++)
{
Color c = copy.GetPixel(x, y);
Boolean flag = false;
foreach (var entry3 in mapping)
{
if (c.R == entry3.Key.R && c.G == entry3.Key.G && c.B == entry3.Key.B)
{
copy.SetPixel(x, y, entry3.Value);
flag = true;
}
if (flag == true)
break;
}
}
pictureBox2.Image=copy;

Your code has two problems:
it is terribly slow
the quantization is not what I would expect.
Here is an original image, the result of your code and what Photoshop does when asked to reduce to 10 colors:
Speeding up the code can be done in two steps:
Get rid of the most obnoxious time wasters
Turn the GetPixel and the SetPixel loops into Lockbits loops.
Here is a solution for step one, that speeds up the code by at least 100x:
Bitmap bm = (Bitmap)Bitmap.FromFile("d:\\ImgA_VGA.png");
pictureBox1.Image = bm;
Dictionary<Color, int> histo = new Dictionary<Color, int>();
for (int x = 0; x < bm.Size.Width; x++)
for (int y = 0; y < bm.Size.Height; y++)
{
Color c = bm.GetPixel(x, y); // **1**
if (histo.ContainsKey(c)) histo[c] = histo[c] + 1;
else histo.Add(c, 1);
}
var result1 = histo.OrderByDescending(a => a.Value);
int number = 10;
var mostusedcolor = result1.Select(x => x.Key).Take(number).ToList();
Double temp;
Dictionary<Color, Double> dist = new Dictionary<Color, double>();
Dictionary<Color, Color> mapping = new Dictionary<Color, Color>();
foreach (var p in result1)
{
dist.Clear();
foreach (Color pp in mostusedcolor)
{
temp = Math.Abs(p.Key.R - pp.R) +
Math.Abs(p.Key.R - pp.R) +
Math.Abs(p.Key.R - pp.R);
dist.Add(pp, temp);
}
var min = dist.OrderBy(k => k.Value).FirstOrDefault();
mapping.Add(p.Key, min.Key);
}
Bitmap copy = new Bitmap(bm);
for (int x = 0; x < copy.Size.Width; x++)
for (int y = 0; y < copy.Size.Height; y++)
{
Color c = copy.GetPixel(x, y); // **2**
copy.SetPixel(x, y, mapping[c]);
}
pictureBox2.Image = copy;
Note that there is no need to calculate the distances with the full force of Pythagoras if all we want is to order the colors. The Manhattan distance will do just fine.
Also note that we already have the lookup dictionary mapping, which contains every color in the image as its key, so we can access the values directly. (This was by far the worst waste of time..)
The test image is processed in ~1s, so I don't even go for the LockBits modifications..
But correcting the quantization is not so simple, I'm afraid and imo goes beyond the scope of a good SO question.
But let's look at what goes wrong: Looking at the result we can see it pretty much at the first glance: There is a lot of sky and all those many many blues pixels have more than 10 hues and so all colors on your top-10 list are blue.
So there are no other hues left for the whole image!
To work around that you best study the common quantization algorithms..
One simplistic approach at repairing the code would be to discard/map together all colors from the most-used-list that are too close to any one of those you already have. But finding the best minimum distance would require soma data analysis..
Update Another very simple way to improve on the code is to mask the real colors by a few of its lower bits to map similar colors together. Picking only 10 colors will still be too few, but the improvement is quite visible, even for this test image:
Color cutOff(Color c, byte mask)
{ return Color.FromArgb(255, c.R & mask, c.G & mask, c.B & mask ); }
Insert this here (1) :
byte mask = (byte)255 << 5 & 0xff; // values of 3-5 worked best
Color c = cutOff(bm.GetPixel(x, y), mask);
and here (2) :
Color c = cutOff(copy.GetPixel(x, y), mask);
And we get:
Still all yellow, orange or brown hues are missing, but a nice improvement with only one extra line..

Related

Why is my C# nested for loop slow?

I'm creating a Game as a school project and thought it would be a good idea to use colormapping for level creation, however the method I'm using is very slow for some reason.
public List<Entity> LoadLevel(Level level)
{
List<Entity> ents = new List<Entity>();
Color[] clrs = new Color[level.getColorMap.Height*level.getColorMap.Width];
level.getColorMap.GetData(clrs);
for (int x = 0; x < level.getColorMap.Width; x++)
{
for (int y = 0; y < level.getColorMap.Height; y++)
{
if (clrs[x + y * level.getColorMap.Width] == new Color(0, 0, 0))
{
ents.Add(new Terrain(new Vector2(x, y)));
ents.Last().Animation.setImageIndex(0);
ents.Last().Animation.Play();
}
if (clrs[x + y * level.getColorMap.Width] == new Color(6, 6, 6))
{
ents.Add(new Terrain(new Vector2(x, y)));
ents.Last().Animation.setImageIndex(6);
ents.Last().Animation.setSpeed(69);
ents.Last().Animation.Play();
}
if (clrs[x + y * level.getColorMap.Width] == new Color(9, 9, 9))
{
ents.Add(new Terrain(new Vector2(x, y)));
ents.Last().Animation.setImageIndex(9);
ents.Last().Animation.setSpeed(69);
ents.Last().Animation.Play();
}
}
}
return ents;
}
I call this function in LoadContent() and it takes about half a minute to execute, why is it so slow?
(Posted on behalf of the OP).
The problem was actually in the constructor of the terrainobject, the function for creating its collider was appearantly very poorly optimized, thanks for the help!
I have some notes to the coding style. Most probably it is not cause of your problems, but better know sooner than later. Generally speaking, you should avoid doing duplicated and unnecessary work. I would not even call it optimization, I would call it a rule. It should be the way you always code.
What if level.getColorMap takes a while? You call it over and over again even when you do not need to do that. You should generally not rely on the fact, that property is cheap. Call it once and remember its result.
ents.Last() is quite fast, but not for free. Do not call it if you do not need it. Build new terrain and remember pointer to it.
The new Color(0, 0, 0) in every loop is bad. Do not take loop hoisting as granted. Most probably you would construct 12000000 color object even when You actually need three.
Your code also has a lot of duplicity. Generally, think twice before you copy and paste code. DRY
The loop order is bad. You jump from row to row ruining the cache locality. You should process the whole row and then go to next.
Avoid multiplication when You need just addition.
Example of improvement:
public void AddTerrain(List<Entity> ents, int selector, int x, int y)
{
Terrain newT = new Terrain(new Vector2(x, y));
ents.Add(newT);
var animation = newT.Animation;
animation.setImageIndex(selector);
if (selector > 0)
{
animation.setSpeed(69);
}
animation.Play();
}
public List<Entity> LoadLevel(Level level)
{
List<Entity> ents = new List<Entity>();
var colorMap = level.getColorMap;
int colorMapWidth = colorMap.Width;
int colorMapHeight = colorMap.Height;
Color[] clrs = new Color[colorMapWidth * colorMapHeight];
Color[] colors = new Color[] { new Color(0, 0, 0), new Color(6, 6, 6), new Color(9, 9, 9) };
colorMap.GetData(clrs);
int ci = 0;
for (int y = 0; y < colorMapHeight; y++)
{
for (int x = 0; x < colorMapWidth; x++)
{
Color c = clrs[ci++];
for (int i = 0; i < colors.Length; ++i)
{
if (c == colors[i])
{
AddTerrain(ents, c.R, x, y);
break;
}
}
}
}
return ents;
}

Convert RGB array to image in C#

I know the rgb value of every pixel, and how can I create the picture by these values in C#? I've seen some examples like this:
public Bitmap GetDataPicture(int w, int h, byte[] data)
{
Bitmap pic = new Bitmap(this.width, this.height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Color c;
for (int i = 0; i < data.length; i++)
{
c = Color.FromArgb(data[i]);
pic.SetPixel(i%w, i/w, c);
}
return pic;
}
But it does not works.
I have a two-dimensional array like this:
1 3 1 2 4 1 3 ...2 3 4 2 4 1 3 ...4 3 1 2 4 1 3 ......
Each number correspond to a rgb value, for example, 1 => {244,166,89}
2=>{54,68,125}.
I'd try the following code, which uses an array of 256 Color entries for the palette (you have to create and fill this in advance):
public Bitmap GetDataPicture(int w, int h, byte[] data)
{
Bitmap pic = new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
int arrayIndex = y * w + x;
Color c = Color.FromArgb(
data[arrayIndex],
data[arrayIndex + 1],
data[arrayIndex + 2],
data[arrayIndex + 3]
);
pic.SetPixel(x, y, c);
}
}
return pic;
}
I tend to iterate over the pixels, not the array, as I find it easier to read the double loop than the single loop and the modulo/division operation.
Your solution very near to working code. Just you need the "palette" - i.e. set of 3-elements byte array, where each 3-bytes element contains {R, G, B} values.
//palette is a 256x3 table
public static Bitmap GetPictureFromData(int w, int h, byte[] data, byte[][] palette)
{
Bitmap pic = new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Color c;
for (int i = 0; i < data.Length; i++)
{
byte[] color_bytes = palette[data[i]];
c = Color.FromArgb(color_bytes[0], color_bytes[1], color_bytes[2]);
pic.SetPixel(i % w, i / w, c);
}
return pic;
}
This code works for me, but it very slow.
If you create in-memory "image" of BMP-file and then use Image.FromStream(MemoryStream("image")), it code will be more faster, but it more complex solution.

Randomly generate blocks on a flat map

I'm trying to randomly generate blocks on a flat map and make it so that they don't overlap each other.
I have made a matrix (c# array) of the size of the map (500x500), the blocks have a scale between 1 and 5.
The code works but if a generated block overlaps another one, it is destroyed and not regenerated somewhere else.
Only around 80 of the 1000 blocks I try to generate don't overlap another block.
Here is a picture of the map with around 80 blocks generated, the green squares are blocks
void generateElement(int ratio, int minScale, int maxScale, GameObject g) {
bool elementFound = false;
for (int i = 0; i < ratio * generationDefault; i++) {
GameObject el;
// Randomly generate block size and position
int size = Random.Range(minScale, maxScale + 1);
int x = Random.Range(0, mapSizex + 1 - size);
int y = Random.Range(0, mapSizey + 1 - size);
// Check if there is already an element
for (int j = x; j < x + size; j++)
for (int k = y; k < y + size; k++)
if (map[j][k] != null)
elementFound = true;
if (elementFound)
continue;
else {
el = (GameObject)Instantiate(g, new Vector3(x + (float)size / 2, (float)size / 2, y + (float)size / 2), Quaternion.Euler(0, 0, 0));
el.transform.localScale *= size;
}
// Create element on map array
for (int j = x; j < x + size; j++)
for (int k = y; k < y + size; k++)
if (map[j][k] == null) {
map[j][k] = el.GetComponent<ObjectInterface>();
}
}
}
I thought of 3 possible fixes
I should set the size of the block depending of the place it has.
I should use another randomization algorithm.
I'm not doing this right.
What do you think is the best idea ?
UPDATE
I got the code working much better. I now try to instantiate the blocks multiple times if needed (maximum 5 for the moment) and I fixed the bugs. If there are already many elements on the map, they will not always be instantiated and that's what I wanted, I just have to find the right amount of times it will try to instantiate the block.
I tried instantiating 1280 elements on a 500x500 map. It takes only about 1.5 second and it instantiated 1278/1280 blocks (99.843%).
void generateElement(int ratio, int minScale, int maxScale, GameObject g) {
bool elementFound = false;
int cnt = 0;
// Generate every block
for (int i = 0; i < ratio * generationDefault; i++) {
GameObject el = null;
// Randomly generate block size and position
int size, x, y, tryCnt = 0;
// Try maximum 5 times to generate the block
do {
elementFound = false;
// Randomly set block size and position
size = Random.Range(minScale, maxScale + 1);
x = Random.Range(0, mapSizex + 1 - size);
y = Random.Range(0, mapSizey + 1 - size);
// Check if there is already an element
for (int j = x; j < x + size; j++)
for (int k = y; k < y + size; k++)
if (map[j][k] != null)
elementFound = true;
tryCnt++;
} while (elementFound && tryCnt < 5);
if (tryCnt >= 5 && elementFound) continue;
// Instantiate the block
el = (GameObject)Instantiate(g, new Vector3(x + (float)size / 2, (float)size / 2, y + (float)size / 2), Quaternion.Euler(0, 0, 0));
el.transform.localScale *= size;
// Create element on map array
for (int j = x; j < x + size; j++)
for (int k = y; k < y + size; k++)
if (map[j][k] == null) {
map[j][k] = el.GetComponent<ObjectInterface>();
}
cnt++;
}
print("Instantiated " + cnt + "/" + ratio * generationDefault);
}
This is incredibly difficult to do well.
Here's a quick solution you'll maybe like ... depending on your scene.
actualWidth = 500 //or whatever. assume here is square
// your blocks are up to 5 size
chunkWidth = actualWidth / 5
// it goes without saying, everything here is an int
kChunks = chunkWidth*chunkWidth
List<int> shuf = Enumerable.Range(1,kChunks).OrderBy(r=>Random.value).ToList();
howManyWanted = 1000
shuf = shuf.Take(howManyWanted)
foreach( i in shuf )
x = i % actualWidth
y = i / actualWidth
make block at x y
put block in list allBlocks
HOWEVER ............
...... you'll see that this looks kind of "regular", so do this:
Just randomly perturb all the blocks. Remember, video game programming is about clever tricks!
Ideally, you have to start from the middle and work your way out; in any event you can't just do them in a line. Shuffling is OK. So, do this ..
harmonic = 3 //for example. TRY DIFFERENT VALUES
function rh = Random.Range(1,harmonic) (that's 1 not 0)
function rhPosNeg
n = rh
n = either +n or -n
return n
function onePerturbation
{
allBlocks = allBlocks.OrderBy(r => Random.value) //essential
foreach b in allBlocks
newPotentialPosition = Vector2(rhPosNeg,rhPosNeg)
possible = your function to check if it is possible
to have a block at newPotentialPosition,
however be careful not to check "yourself"
if possible, move block to newPotentialPosition
}
The simplest approach is just run onePerturbation, say, three times. Have a look at it between each run. Also try different values of the harmonic tuning factor.
There are many ways to perturb fields of differently-sized blocks, above is a KISS solution that hopefully looks good for your situation.
Coding note...
How to get sets of unique random numbers.
Just to explain this line of code...
List<int> shuf = Enumerable.Range(1,kChunks).OrderBy(r=>Random.value).ToList();
If you are new to coding: say you want to do this: "get a hundred random numbers, from 1 to million, but with no repeats".
Fortunately, this is a very well known problem with a very simple solution.
The way you get numbers with no repeats, is simply shuffle all the numbers, and then take how many you want off the top.
For example, say you need a random couple of numbers from 1-10 but with no repeats.
So, here's the numbers 1-10 shuffled: 3,8,6,1,2,7,10,9,4,5
Simply take what you need off the front: so, 3, 8, 6 etc.
So to make an example let's say you want twelve numbers, no repeats, from 1 through 75. So the first problem is, you want a List with all the numbers up to 75, but shuffled. In fact you do that like this ..
List<int> shuf = Enumerable.Range(1,75).OrderBy(r=>Random.value).ToList();
So that list is 75 items long. You can check it by saying foreach(int r in shuf) Debug.Log(r);. Next in the example you only want 12 of those numbers. Fortunately there's a List call that does this:
shuf = shuf.Take(12)
So, that's it - you now have 12 numbers, no repeats, all random between 1 and 75. Again you can check with foreach(int r in shuf) Debug.Log(r);
In short, when you want "n" numbers, no repeats, between 1 and Max, all you have to so is this:
List<int> shuf = Enumerable.Range(1,Max).OrderBy(r=>Random.value).ToList();
shuf = shuf.Take(n);
et voilĂ , you can check the result with foreach(int r in shuf) Debug.Log(r);
I just explain this at length because the question is often asked "how to get random numbers that are unique". This is an "age-old" programming trick and the answer is simply that you shuffle an array of all the integers involved.
Interestingly, if you google this question ("how to get random numbers that are unique") it's one of those rare occasions where google is not much help, because: whenever this question is asked, you get a plethora of keen new programmers (who have not heard the simple trick to do it properly!!) writing out huge long complicated ideas, leading to further confusion and complication.
So that's how you make random numbers with no repeats, fortunately it is trivial.
if (elementFound) continue; will skip out this current loop iteration. You need to wrap the int x=Random..; int y=Random()..; part in a while loop with the condition being while(/* position x/y already occupued*/) { /* generate new valid point */} like this for example:
void generateElement(int ratio, int minScale, int maxScale, GameObject g) {
for (int i = 0; i < ratio * generationDefault; i++) {
GameObject el;
// Randomly generate block size and position
bool elementFound = false;
int size, x, y;
do
{
elementFound = false;
size = Random.Range(minScale, maxScale + 1);
x = Random.Range(0, mapSizex + 1 - size);
y = Random.Range(0, mapSizey + 1 - size);
// Check if there is already an element
for (int j = x; j < x + size; j++)
for (int k = y; k < y + size; k++)
if (map[j][k] != null)
elementFound = true;
} while(elementFound);
el = (GameObject)Instantiate(g, new Vector3(x + (float)size / 2, (float)size / 2, y + (float)size / 2), Quaternion.Euler(0, 0, 0));
el.transform.localScale *= size;
// Create element on map array
for (int j = x; j < x + size; j++)
for (int k = y; k < y + size; k++)
if (map[j][k] == null) {
map[j][k] = el.GetComponent<ObjectInterface>();
}
}
}
You shouldn't be getting that many collisions.
Assuming your blocks were ALL 5 units wide and you're trying to fit them into a grid of 500,500 you would have 100*100 spaces for them at minimum, which gives 10,000 spaces into which to fit 1,000 blocks.
Try playing around with this code:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
var result = PlaceNonOverlappingBlocks(1000, 5, 500, 500);
}
static List<Block> PlaceNonOverlappingBlocks(int count, int maxBlockSize, int mapX, int mapY)
{
var map = new bool[mapY, mapX];
var rng = new Random();
var result = new List<Block>(count);
int collisions = 0;
while (count > 0)
{
int size = rng.Next(1, maxBlockSize + 1);
int x = rng.Next(0, mapX - size);
int y = rng.Next(0, mapY - size);
if (fits(map, x, y, size))
{
result.Add(new Block(x, y, size));
addToMap(map, x, y, size);
--count;
}
else
{
if (++collisions> 100000)
throw new InvalidOperationException("Hell has frozen over");
}
}
// This is just for diagnostics, and can be removed.
Console.WriteLine($"There were {collisions} collisions.");
return result;
}
static void addToMap(bool[,] map, int px, int py, int size)
{
for (int x = px; x < px+size; ++x)
for (int y = py; y < py + size; ++y)
map[y, x] = true;
}
static bool fits(bool[,] map, int px, int py, int size)
{
for (int x = px; x < px + size; ++x)
for (int y = py; y < py + size; ++y)
if (map[y, x])
return false;
return true;
}
internal class Block
{
public int X { get; }
public int Y { get; }
public int Size { get; }
public Block(int x, int y, int size)
{
X = x;
Y = y;
Size = size;
}
}
}
}

How to group near coordinates

I'm having my thesis "Multiple Choice Examination Checker" and I'm having a big issue about what to do with my problem. I got a picture image (a bitmap specifically) here it is, so you can see:
This is the image with the detectedbox, I will describe this:
This is an examination paper, 1-50 items. each number has a corresponding box(right side of the number, that serves as a container for the answer)
This pictures is just a sample, the number of detected boxes may vary. My approximation is it contains 150-200 detected boxes.
Each detectedboxes are stored in a List(MCvBOX2D) which holds the detectedboxes' size, center, etc.
I transferred those center coordinates in a new list List(PointF) center;
Each box from the image, may have 3-5 detectedboxes. as you can see there are more than one detectedboxes in each of the boxes from the image.
I sorted all the detectedboxes in ascending order, so I would know which will possibly be the number1, number2, and so on..
Here is some of my code, which contains the sorting of boxes.
List<PointF> center = new List<PointF>();
List<PointF> centernew = new List<PointF>();
foreach (MCvBox2D box in lstRectangles)
{
// this code transfers every center-coordinates of detected boxes
// to a new list which is center
center.Add(new PointF(box.center.X, box.center.Y));
}
// and this one sorts the coordinates in ascending order.
centernew = center.OrderBy(p => p.Y).ThenBy(p => p.X).ToList();
I'm done with the sorting part, now my problem is, since there many detected boxes in every box from the image, I would like to group the sortedlist of center-coordinates, so I could eliminate the other detectedboxes and get only one detectedbox for each number.
I know it's hard to understand so I'll explain more.
Let's say my sortedlist of detectedboxes contains first five center-coordinates which are:
let's say this are the center-coordinates of each of the detectedboxes from first box of the image.
center[0] = [ 45.39, 47.6]
center[1] = [ 65.39, 47.6]
center[2] = [ 45.40, 47.10]
center[3] = [ 65.45, 47.25]
center[4] = [ 46.01, 47.50]
and the 2nd are:
center[5] = [ 200.39, 47.2]
center[6] = [ 45.39, 47.2]
center[7] = [ 45.39, 47.3]
center[8] = [ 45.39, 47.55]
My goal is to organize all the sorted detectedboxes inside the list, I must be able to group all the center-coordinates that have close value with the other center, specifically their Y-coordinates.
var rand = new Random();
var threshold = 1;
var points = new List<PointF>();
for (int i = 0; i < 20; i++)
{
points.Add(new PointF((float) rand.NextDouble()*10, (float) rand.NextDouble()*10));
}
Console.WriteLine(points.Count);
for (int i = 0; i < points.Count(); i++)
{
for (int j = i + 1; j < points.Count(); )
{
var pointHere = points[i];
var pointThere = points[j];
var vectorX = pointThere.X - pointHere.X;
var vectorY = pointThere.Y - pointHere.Y;
var length = Math.Sqrt(Math.Pow(vectorX, 2) + Math.Pow(vectorY, 2));
if (length <= threshold)
{
points.RemoveAt(j);
}
else
{
j += 1;
}
}
}
Console.WriteLine(points.Count);
You can calculate the distance between a given point and any other point in the list. If the distance is less than half the width of a box, you can be pretty sure that it's part of the same box.
double threshold = 3.0; // Make this whatever is appropriate
for (int i = center.Count - 1; i >= 0; --i)
if (center.Any(p => p != center[i] && Distance(center[i], p) < threshold))
center.Remove(center[i]);
And you could use this for your Distance() method:
private double Distance(PointF p1, PointF p2)
{
double deltaX = Math.Abs(p1.X - p2.X);
double deltaY = Math.Abs(p1.Y - p2.Y);
return Math.Sqrt((deltaX * deltaX) + (deltaY * deltaY));
}
You could use Distinct with an custom IEqualityComparer (see MSDN).
As an example, define a class:
class BoxEqualityComparer : IEqualityComparer<MCvBox2D>
{
private static Double Tolerance = 0.01; //set your tolerance here
public Boolean Equals(MCvBox2D b1, MCvBox2D b2)
{
if (CentersAreCloseEnough(b1.Center, b2.Center))
{
return true;
}
else
{
return false;
}
}
private Boolean CentersAreCloseEnough(PointF c1, PointF c2)
{
return Math.Abs(c1.X - c2.X) < Tolerance && Math.Abs(c1.Y - c2.Y) < Tolerance;
}
}
then use the method in your code like so:
var distinctRectangles = lstRectangles.Distinct(new BoxEqualityComparer());
You are free to implement CentersAreCloseEnough(PointF c1, PointF c2) however you would like; you could use vector distance, absolute distance in x and y, etc.
If you are just concerned about positions with Y coordinates, just sort by that number. If you want to sort both you can add both X and Y and use that number to sort them. Here is an example of what I meen.
for(int i = 0; i < points.length - 1; i++)
{
int temp = points[i].x + points[i].y;
for(int j = i+1; j < points.length; i++)
{
int temp2 = point[j].x + points[j].y;
if(temp2 < temp)
{
Point jugglePoint = points[i];
points[i] = points[j];
points[j] = jugglePoint;
}
}
}

Image Comparing and return Percentage

int DiferentPixels = 0;
Bitmap first = new Bitmap("First.jpg");
Bitmap second = new Bitmap("Second.jpg");
Bitmap container = new Bitmap(first.Width, first.Height);
for (int i = 0; i < first.Width; i++)
{
for (int j = 0; j < first.Height; j++)
{
int r1 = second.GetPixel(i, j).R;
int g1 = second.GetPixel(i, j).G;
int b1 = second.GetPixel(i, j).B;
int r2 = first.GetPixel(i, j).R;
int g2 = first.GetPixel(i, j).G;
int b2 = first.GetPixel(i, j).B;
if (r1 != r2 && g1 != g2 && b1 != b2)
{
DiferentPixels++;
container.SetPixel(i, j, Color.Red);
}
else
container.SetPixel(i, j, first.GetPixel(i, j));
}
}
int TotalPixels = first.Width * first.Height;
float dierence = (float)((float)DiferentPixels / (float)TotalPixels);
float percentage = dierence * 100;
With this portion of Code im comparing 2 Images foreach Pixels and yes it work's it return's Percentage of difference ,so it compares each Pixel of First Image with pixel in same index of Second Image .But what is wrong here ,i have a huge precision maybe it should not work like that ,the comparison ,and maybe there are some better algorithms which are faster and more flexible .
So anyone has an idea how can i transform the comparison ,should i continue with that or should i compare Colors of Each Pixels or ....
PS : If anyone has a solution how to make Parallel this code ,i would also accept it ! Like expanding this to 4 Threads would they do it faster right in a Quad Core?
One obvious change would be call GetPixel only once per Bitmap, and then work with the returned Color structs directly:
for (int i = 0; i < first.Width; ++i)
{
for (int j = 0; j < first.Height; ++j)
{
Color secondColor = second.GetPixel(i, j);
Color firstColor = first.GetPixel(i, j);
if (firstColor != secondColor)
{
DiferentPixels++;
container.SetPixel(i, j, Color.Red);
}
else
{
container.SetPixel(i, j, firstColor);
}
}
}
For speed, resize the images to something very small (16x12, for example) and do the pixel comparison. If it is a near match, then try it at higher resolution.

Categories

Resources