How to compare Image objects with C# .NET? [closed] - c#

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
Can we compare two Image objects with C#? For example, check whether they are equal, or even better check how similar are their pixels?
if possible, how?

You can use a set of tools called TestApi, which is an open-source library to aid unit testing. One of such API is called Visual Verification API, and it does exactly what you need - it can compare two images and tell you if they are equal:
// 1. Capture the actual pixels from a given window
Snapshot actual = Snapshot.FromRectangle(new Rectangle(0, 0, 100, 100));
// 2. Load the reference/master data from a previously saved file
Snapshot expected = Snapshot.FromFile("Expected.png"));
// 3. Compare the actual image with the master image
// This operation creates a difference image. Any regions which are identical in
// the actual and master images appear as black. Areas with significant
// differences are shown in other colors.
Snapshot difference = actual.CompareTo(expected);
// 4. Configure the snapshot verifier - It expects a black image with zero tolerances
SnapshotVerifier v = new SnapshotColorVerifier(Color.Black, new ColorDifference());
// 5. Evaluate the difference image
if (v.Verify(difference) == VerificationResult.Fail)
{
// Log failure, and save the diff file for investigation
actual.ToFile("Actual.png", ImageFormat.Png);
difference.ToFile("Difference.png", ImageFormat.Png);
}

The simplest place to start would be dimensions. If the dimensions are not equal, you may be able to declare them false.
If you need to go through them pixel-by-pixel, you'll need two for loops. Something along these lines:
Bitmap ImageA...
Bitmap ImageB...
for ( Int64 x = 0; x < ImageA.Width; x++ )
{
for ( Int64 y = 0; y < ImageA.Height; y++ )
{
if ( ImageA.GetPixel(x, y) != ImageB.GetPixel(x, y) )
{
return false;
}
}
}
It's pseudo-code (the functions exist in C#, although I can't recall them at the moment) and very simplistic, but is how you'd want to perform a basic pixel-to-pixel check.
Note, however, for that loop to work the images must be of the same dimensions. If they aren't, you're likely to get exceptions if you try to sample a pixel outside of the smaller one's area. It also won't be terribly fast to compare the pixels, so you may want to find another way of discarding possible duplicates first.
Edit: I'm not sure how to do this on an Image, but it is quite simple for Bitmaps. There isn't a visible way of getting Image pixel data out of the class. It appears the Bitmaps inherit from Images, though, so this may still work. Given that Images are an abstract class for both Bitmaps and Metafiles, they may not have a simple internal pixel list.

I had the same question this very day, my workaround was to take image1 and image2 converted to 256x256 or 128x128 both translated AND then generate an image3 with the difference between them, then scan image3 checking the differences and returning the difference amount, I found out that the LOWER difference amount in % more equal the images are and more likely for them to be equal. This way you can identify if images are equal even if they're differently sized. here is the code.
double CompareImages(Bitmap InputImage1, Bitmap InputImage2, int Tollerance)
{
Bitmap Image1 = new Bitmap(InputImage1, new Size(128, 128));
Bitmap Image2 = new Bitmap(InputImage2, new Size(128, 128));
int Image1Size = Image1.Width * Image1.Height;
int Image2Size = Image2.Width * Image2.Height;
Bitmap Image3;
if (Image1Size > Image2Size)
{
Image1 = new Bitmap(Image1, Image2.Size);
Image3 = new Bitmap(Image2.Width, Image2.Height);
}
else
{
Image1 = new Bitmap(Image1, Image2.Size);
Image3 = new Bitmap(Image2.Width, Image2.Height);
}
for (int x = 0; x < Image1.Width; x++)
{
for (int y = 0; y < Image1.Height; y++)
{
Color Color1 = Image1.GetPixel(x, y);
Color Color2 = Image2.GetPixel(x, y);
int r = Color1.R > Color2.R ? Color1.R - Color2.R : Color2.R - Color1.R;
int g = Color1.G > Color2.G ? Color1.G - Color2.G : Color2.G - Color1.G;
int b = Color1.B > Color2.B ? Color1.B - Color2.B : Color2.B - Color1.B;
Image3.SetPixel(x, y, Color.FromArgb(r,g,b));
}
}
int Difference = 0;
for (int x = 0; x < Image1.Width; x++)
{
for (int y = 0; y < Image1.Height; y++)
{
Color Color1 = Image3.GetPixel(x, y);
int Media = (Color1.R + Color1.G + Color1.B) / 3;
if (Media > Tollerance)
Difference++;
}
}
double UsedSize = Image1Size > Image2Size ? Image2Size : Image1Size;
double result = Difference*100/UsedSize;
return Difference*100/UsedSize;
}
Tested here with over 900 images and it works like a charm x)

Related

Get most similar image [duplicate]

This question already has answers here:
How can I measure the similarity between two images? [closed]
(17 answers)
Closed 5 years ago.
I have one Bitmap A and one array of Bitmap, in the array there is a Bitmap that looks the same as Bitmap A. I'm using the code below but it sometimes doesnt work, it iterates the entire array without finding it, it seems there are some minor differences, is there a way to change the function to return true if its 90% similar or pick the most similar image in the array? The array has only 6 images.
for(int i = 0; i < list.Count;i++)
{
if(ImageCompareString(image,list[i])
{
answerIndex = i;
break;
}
}
private static bool ImageCompareString(Bitmap firstImage, Bitmap secondImage)
{
MemoryStream ms = new MemoryStream();
firstImage.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
String firstBitmap = Convert.ToBase64String(ms.ToArray());
ms.Position = 0;
secondImage.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
String secondBitmap = Convert.ToBase64String(ms.ToArray());
if (firstBitmap.Equals(secondBitmap))
{
return true;
}
else
{
return false;
}
}
Of course there is such way... But you have to code it yourself.
First you shoud not compare the base64 data... You'll loose direct pixel value access and increase the size of the data to compare by more then 150% (Originaly 200% but corrected thanks to PeterDuniho's comment) in C# due to UTF16.
Second I assume that all pictures have the same fixed size. Before comparing, reduce the image size to something really small, but keep the width/height aspect. This will speed up the comparsion and also eliminates noise.
Third Iterate both pictures and compare their grayscaled pixel values. I Assume that you have resized the picture to 16x16. Since we're comparing their grayscale-values the value of one pixel is between 0 and 255. So the maximum distance between both pictures will be 16 * 16 * 256 = 65536. If both pictures are black, the distance between the pictures will be zero (100% similarity). If one picture is black and the other is white the distance will be 65535 (0% similarity).
To compare the images iterate the picture-pixels and subtract the grayscale-pixel-value-from-picture-a from the grayscale-pixel-value-of-picture-b at the point x,y and add the absolute difference value to the counter. This counter will be the total distance between both pictures.
Lets assume this counter has a value of 1000 after the comparison loop, you get the percentage-similarity by 1000 / 65535 ~ 1.5% difference (or 98.5% similarity) between both pictures.
pseudo-compare-code
long counter = 0;
long total = image.Width * image.Height * (Color.White - Color.Black);
for(int x = 0; x < image.Width; x++)
{
for(int y = 0; y < image.Height; y++)
{
var p1 = image.GetPixel(x, y);
var p2 = otherImage.GetPixel(x, y);
var g1 = ((p1.R + p1.G + p1.B) / 3);
var g2 = ((p2.R + p2.G + p2.B) / 3);
var distance = Math.Abs(g1 - g2);
counter += distance;
}
}
var similarity = 100 - ((counter / total) * 100);
This is an more or less easy approach, but you have to test this with you scenario/images. Instead of comparing grayscale-values you could also compare rgb-values. Look for distance definitions like the euclidean distance... Start and keep reading :)
EDIT
This is just a really basic approach that should explain how you can start comparing images. It does not take into account that there might be different image formats (jpeg, png, gif), color formats (indexed, 16bit, 24bit, 32bit) or images with different resolutions.

C# WinForms Chart Control: get Size,Region,Position of Bar

is there a way to get the rectangles of the stackcolumn chart bar?
this code snippet is how it can be works but it's very ugly:
var points = new List<Point>();
for (int x = 0; x < chart.Size.Width; x++)
{
for (int y = 0; y < chart.Size.Height; y++)
{
var hp = chart.HitTest(x, y, false, ChartElementType.DataPoint);
var result = hp.Where(h => h.Series?.Name == "Cats");
if (result.Count() > 0)
{
points.Add(new Point(x, y));
}
}
}
var bottomright = points.First();
var topleft = points.Last();
I will try to describe my purpose:
I would like to create a chart from various testresults and make this available as a HTML file. This generated Chart is inserted as an image file in the HTML document. Now, I would like to link each part of a Bar area from the Chart to an external document. Since the graphics is static, I have only the possibility to use the "MAP Area" element to make any area as a link from HTML. The "map" element requires a "rectangle", or these coordinates. That's the reason why I need the coordinator of each part of a Bar.
I have to mention that I am not really familiar with the Chart control yet.
The graphics is generated testweise.
[SOLVED]
i got the solution:
var stackedColumns = new List<Tuple<string,string,Rectangle>>();
for (int p = 0; p < chart.Series.Select(sm => sm.Points.Count).Max(); p++)
{
var totalPoints = 0;
foreach (var series in chart.Series)
{
var width = int.Parse(series.GetCustomProperty("PixelPointWidth"));
var x = (int)area.AxisX.ValueToPixelPosition(p + 1) - (width / 2);
int y = (int)area.AxisY.ValueToPixelPosition(totalPoints);
totalPoints += series.Points.Count > p ? (int)series.Points[p].YValues[0] : 0;
int y_total = (int)area.AxisY.ValueToPixelPosition(totalPoints);
var rect = new Rectangle(x, y_total, width, Math.Abs(y - y_total));
stackedColumns.Add(new Tuple<string, string, Rectangle>(series.Name, series.Points.ElementAtOrDefault(p)?.AxisLabel, rect));
}
}
this workaround works for stackedcolumn and points starts at x-axis=0.
just the PixelPointWidth property has to be set manualy to get the right width. i have not yet found a way to get the default bar width..
This is extremely tricky and I really wish I knew how to get the bounds from some chart functionionality!
You code snippet is actulally a good start for a workaround. I agree though that it has issues:
It is ugly
It doesn't always work
It has terrible performance
Let's tackle these issues one by one:
Yes it is ugly, but then that's the way of workarounds. My solution is even uglier ;-)
There are two things I found don't work:
You can't call a HitTest during a Pre/PostPaint event or terrible things will happen, like some Series go missing, SO exceptions or other crashes..
The result for the widths of the last Series are off by 1-2 pixels.
The performance of testing each pixel in the chart will be terrible even for small charts, but gets worse and worse when you enlarge the chart. This is relatively easy to prevent, though..:
What we are searching are bounding rectangles for each DataPoint of each Series.
A rectangle is defined by left and right or width plus top and bottom or height.
We can get precise values for top and bottom by using the axis function ValueToPixelPosition feeding in the y-value and 0 for each point. This is simple and cheap.
With that out of the way we still need to find the left and right edges of the points. To do so all we need to do it test along the zero-line. (All points will either start or end there!)
This greatly reduces the number of tests.
I have decided to do the testing for each series separately, restaring at 0 each time. For even better performance one could do it all in one go.
Here is a function that returns a List<Rectangle> for a given Series:
List<Rectangle> GetColumnSeriesRectangles(Series s, Chart chart, ChartArea ca)
{
ca.RecalculateAxesScale();
List<Rectangle> rex = new List<Rectangle>();
int loff = s == chart.Series.Last() ? 2 : 0; ;
int y0 = (int)ca.AxisY.ValueToPixelPosition(0);
int left = -1;
int right = -1;
foreach (var dp in s.Points)
{
left = -1;
int delta = 0;
int off = dp.YValues[0] > 0 ? delta : -delta;
for (int x = 0; x < chart.Width; x++)
{
var hitt = chart.HitTest(x, y0 +off );
if (hitt.ChartElementType == ChartElementType.DataPoint &&
((DataPoint)hitt.Object) == dp)
{
if (left < 0) left = x;
right = x;
}
else if (left > 0 && right > left) break;
}
int y = (int)ca.AxisY.ValueToPixelPosition(dp.YValues[0]);
rex.Add(new Rectangle(left, Math.Min(y0, y),
right - left + 1 - loff, Math.Abs(y - y0)));
left = -1;
}
return rex;
}
A few notes:
I start by doing a RecalculateAxesScale because we can't Hittest before the current layout has been calculated.
I use a helper variable loff to hold the offset for the width in the last Series.
I start searching at the last x coordinate as the points should all lie in sequence. If they don't because you have used funny x-values or inserted points you may need to start at 0 instead..
I use y0 as the baseline of the zero values for both the hittesting y and also the points' base.
I use a little Math to get the bounds right for both positive and negative y-values.
Here is a structure to hold those rectangles for all Series and code to collect them:
Dictionary<string, List<Rectangle>> ChartColumnRectangles = null;
Dictionary<string, List<Rectangle>> GetChartColumnRectangles(Chart chart, ChartArea ca)
{
Dictionary<string, List<Rectangle>> allrex = new Dictionary<string, List<Rectangle>>();
foreach (var s in chart.Series)
{
allrex.Add(s.Name, GetColumnSeriesRectangles(s, chart, ca));
}
return allrex;
}
We need to re-calculate the rectangles whenever we add points or resize the chart; also whenever the axis view changes. The common code for AxisViewChanged, ClientSizeChanged, Resize and any spot you add or remove points could look like this:
Chart chart= sender as Chart;
GetChartColumnRectangles(chart, chart.ChartAreas[0]);
Let's test the result with a Paint event:
private void chart1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
chart1.ApplyPaletteColors();
foreach (var kv in ChartColumnRectangles)
{
{
foreach (var r in kv.Value)
g.DrawRectangle(Pens.Black, r);
}
}
}
Here it is in action:
Well, I've been down this path and the BIG issue for me is that the custom property of 'PixelPointWidth' is just that - it is custom. You cannot retrieve it unless you've set it. I needed the width of the item - had to scwag/calculate it myself. Keep in mind that many charts can be panned/zoomed, so once you go down this path, then you need to recalculate it and set it for the chart prepaint events.
Here is a crude little function I made (is more verbose than needed - for educational purposes and has no error handling :)):
private int CalculateChartPixelPointWidth(Chart chart, ChartArea chartArea, Series series)
{
// Get right side - takes some goofy stuff - as the pixel location isn't available
var areaRightX = Math.Round(GetChartAreaRightPositionX(chart, chartArea));
var xValue = series.Points[0].XValue;
var xPixelValue = chartArea.AxisX.ValueToPixelPosition(xValue);
var seriesLeftX = chart.Location.X + xPixelValue;
var viewPointWidth = Math.Round((areaRightX - seriesLeftX - (series.Points.Count * 2)) / series.Points.Count, 0);
return Convert.ToInt32(viewPointWidth);
}
And this as well:
private double GetChartAreaRightPositionX(Chart chart, ChartArea area)
{
var xLoc = chart.Location.X;
return xLoc + (area.Position.Width + area.Position.X) / 100 * chart.Size.Width;
}
The reason I'm calculating this is because I need to draw some graphical overlays on top of the normal chart item objects (my own rendering for my own purposes).
In the 'prepaint' event for the chart, I need to calculate the 'PixelPointWidth' that matches the current chart view (might be panned/zoomed). I then use that value to SET the chart custom property to match . . . such that the normal chart entities and MINE are correctly aligned/scaled (ensures we're in exactly the right 'x' axis position):
In my prepaint event, I do the following - just prior to drawing my graphical entities:
// Pretty close scwag . . .
var viewPointWidth = CalculateChartPixelPointWidth(e.Chart, e.Chart.ChartAreas[0], e.Chart.Series[0]);
// Set the custom property and use the same point width for my own entities . .
chart1.Series[0].SetCustomProperty("PixelPointWidth", viewPointWidth.ToString("D"));
// . . . now draw my entities below . . .

Find / replace colors in (i.e. recolor) a picture

I am trying to recreate the Recolor Picture dialog that Microsoft unfortunately discontinued in the transition from Office 2003 to 2007. This was very useful for replacing colors in a picture (see http://www.indezine.com/products/powerpoint/learn/picturesandvisuals/recolor-pictures-ppt2003.html for full description of dialog).
I am mostly interested in doing this for images in the metafile format (EMF or WMF), which tend to have fewer colors than other picture formats, in my experience. The picture below is an example of an enhanced metafile picture pasted from Excel into PowerPoint that appears to contain just 6 colors:
If I was able to use the legacy Office dialog pictured above, I would see my 6 colors on the left in the "Original" column, and I could easily change the blue font (and border) color to black. The problem here is that if I use GetPixel() to programmatically inventory the colors in the image, I get dozens of colors due to anti-aliasing of the fonts, and it isn't practical to show the user all these recoloring options (which would effectively require the user to manually recreate the proper anti-aliasing effect). The snippet of code below illustrates how I have tried to inventory the colors:
Dim listColors as New List(Of Color)
Dim shp as PowerPoint.Shape = [a metafile picture in PowerPoint]
Dim strTemp as String = Path.Combine(Environ("temp"), "temp_img.emf")
shp.Export(strTemp, PowerPoint.PpShapeFormat.ppShapeFormatEMF, 0, 0)
Using bmp As New Bitmap(strTemp)
For x As Integer = 0 To bmp.Width - 1
For y As Integer = 0 To bmp.Height - 1
listColors.Add(bmp.GetPixel(x, y))
Next
Next
End Using
I see that there is an optional Palette property for metafiles, which I thought could provide an answer, but an exception is thrown when I try to access it, so that was a dead end. I also see that there are headers for metafile images, but I cannot decipher what to do with them from the limited documentation on the Internet, and I am not even sure that that would get me to the right answer (i.e. 6 colors).
To summarize, part 1 of the question is how to inventory (i.e. identify) the 6 "core" colors in the image above, and part 2 is how to replace one of these 6 colors with another. VB.NET solutions are preferred, although I can probably translate C# code if not too complex.
If needed, you can download the EMF version of the image above at https://www.dropbox.com/s/n03ys3dh9pcd0xu/temp_img.emf?dl=0.
EDIT: To be clear, I am not interested in "computing" the six "core" colors in the image above. I believe, perhaps incorrectly, that these six colors are explicit properties of the image, and my first objective is to figure out how to access them. Indeed, if you simply ungroup the metafile picture twice in PowerPoint, you can loop through the resulting shapes to get these six colors. That would address part 1 of the question, although it seems a bit sloppy, works only for metafiles (which may be fine, actually), and I doubt that is how the legacy Recolor Picture dialog worked. To address part 2, I could regroup the metafile picture shapes after swapping colors but, again, that seems sloppy and modifies the picture in ways other than what is intended. So, how can I explicitly retrieve / modify / set these "core" colors in a [metafile] picture?
So I've had a quick stab at implementing one of my ideas to use a dictionary of colours and what not. The code isn't working properly just yet but I figured I show it here so that you can have a quick look into how it works and develop on it from there.
using (Bitmap bitmap = new Bitmap(#"InputPath"))
{
Dictionary<Color, List<Color>> colourDictionary = new Dictionary<Color, List<Color>>();
int nTolerance = 60;
int nBytesPerPixel = Bitmap.GetPixelFormatSize(bitmap.PixelFormat) / 8;
System.Drawing.Imaging.BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat);
try
{
int nByteCount = bitmapData.Stride * bitmap.Height;
byte[] _baPixels = new byte[nByteCount];
System.Runtime.InteropServices.Marshal.Copy(bitmapData.Scan0, _baPixels, 0, _baPixels.Length);
int _nStride = bitmapData.Stride;
for (int h = 0; h < bitmap.Height; h++)
{
int nCurrentLine = h * _nStride;
for (int w = 0; w < (bitmap.Width * nBytesPerPixel); w += nBytesPerPixel)
{
int nBlue = _baPixels[nCurrentLine + w];
int nGreen = _baPixels[nCurrentLine + w + 1];
int nRed = _baPixels[nCurrentLine + w + 2];
if (colourDictionary.Keys.Count > 0)
{
Color[] caNearbyColours = colourDictionary.Keys.Select(c => c)
.Where(c => (int)c.B <= (nBlue + nTolerance) && (int)c.B >= (nBlue - nTolerance)
&& (int)c.G <= (nGreen + nTolerance) && (int)c.G >= (nGreen - nTolerance)
&& (int)c.R <= (nRed + nTolerance) && (int)c.R >= (nRed - nTolerance)).ToArray();
if (caNearbyColours.Length > 0)
{
if (!colourDictionary[caNearbyColours.FirstOrDefault()].Any(c => c.R == nRed && c.G == nGreen && c.B == nBlue))
colourDictionary[caNearbyColours.FirstOrDefault()].Add(Color.FromArgb(255, nRed, nGreen, nBlue));
}
else
colourDictionary.Add(Color.FromArgb(255, nRed, nGreen, nBlue), new List<Color>());
}
else
colourDictionary.Add(Color.FromArgb(255, nRed, nGreen, nBlue), new List<Color>());
}
}
}
finally
{
bitmap.UnlockBits(bitmapData);
}
using (Bitmap colourBitmap = new Bitmap(bitmap.Width, bitmap.Height, bitmap.PixelFormat))
{
using (Graphics g = Graphics.FromImage(colourBitmap))
{
for (int h = 0; h < colourBitmap.Height; h++)
{
for (int w = 0; w < colourBitmap.Width; w++)
{
Color colour = bitmap.GetPixel(w, h);
if (!colourDictionary.ContainsKey(colour))
{
Color keyColour = colourDictionary.Keys.FirstOrDefault(k => colourDictionary[k].Any(v => v == colour));
colourBitmap.SetPixel(w, h, keyColour);
}
else
colourBitmap.SetPixel(w, h, colour);
}
}
}
colourBitmap.Save(#"OutputPath", System.Drawing.Imaging.ImageFormat.Png);
}
}
Notice how the top section uses Lockbits for better performance. This can easily be transferred onto the bottom section. Also note that the lock bits code is set to work for images with a bytes per pixel of 3 or 4.
Now onto how the code is attempting to work:
It starts off by looping over the initial image and finding colours. If the colour is found already it skips it, however if the colour is not in the dictionary it will add it and also if the colour has a similar colour in the dictionaries keys (will need changing to look at values too) it will add it to it's values.
This then loops through the pixels in the output image setting them according to the keys in the dictionary, thus creating a 'blocked' image.
Now as I said it isn't working just yet, so here are some improvements needed to be made:
As said above checking the colour to those in the values
Adding lock bits to the bottom code for better performance
Tweaking the nTolerance value for better results
Keep track of counts of colours and at the end of looping set the key to that with the largest count
And of course anything else that I have not thought of

How to measure width of character precisely?

Maybe I've got something wrong, but... I want to simulate character spacing.
I break the word (text) into the list of single characters, measure their widths, and then painting them one after another on the bitmap. I supposed, that overall width of the rendered text will be the same as the width of the whole not splitted string, but there is something wrong. Rendering characters in a loop show wider result. Is there any way to get common (expected) results?
here is a code snippet:
private struct CharWidths
{
public char Char;
public float Width;
}
private List<CharWidths> CharacterWidths = new List<CharWidths>();
...
private void GetCharacterWidths(string Text, Bitmap BMP)
{
int i;
int l = Text.Length;
CharacterWidths.Clear();
Graphics g = Graphics.FromImage(BMP);
CharWidths cw = new CharWidths();
for (i = 0; i < l; i++)
{
Size textSize = TextRenderer.MeasureText(Text[i].ToString(), Font);
cw.Char = Text[i];
cw.Width = textSize.Width;
CharacterWidths.Add(cw);
}
}
...
public void RenderToBitmap(Bitmap BMP)
{
//MessageBox.Show("color");
Graphics g = Graphics.FromImage(BMP);
GetCharacterWidths("Lyborko", BMP);
int i;
float X = 0;
PointF P = new PointF();
for (i = 0; i < CharacterWidths.Count; i++)
{
P.X = X;
P.Y = 0;
g.DrawString(CharacterWidths[i].Char.ToString(), Font, Brushes.White, P);
X = X+CharacterWidths[i].Width;
}
P.X = 0;
P.Y = 30;
g.DrawString("Lyborko", Font, Brushes.White, P);
// see the difference
}
Thanx a lot
First of all should say that don't have a silver bullet solution for this, but have a couple of suggessions on subject:
Considering that you by calling TextRenderer.MeasureText do not pass current device context (the same one you use to draw a string after) and knowing a simple fact that MeasureText simply in case of lack of that parameter creates a new one compatible with desktop and calls DrawTextEx WindowsSDK function, I would say first use an overload of MeasureText where you specify like a first argument device context which you use to render a text after. Could make a difference.
If it fails, I would try to use Control.GetPreferredSize method to guess most presize possible rendering dimension of the control on the screen, so actually the dimension of you future string's bitmap. To do that you can create some temporary control, assign a string, render and after call this function. It's clear to me that this solution may hardly fit in your app architecture, but can possibly produce a better results.
Hope this helps.

c# getPixel not sellecting all pixels

I have a problem with getting the pixels from an image. I load a image, select a pixel from the image and retrieve it's color and then i generate a matrix indexMatrix[bitmap_height][bitmap_width] which contains 1 or 0 depending if the [x,y] color of the bitmap is the same as the color selected. The problem is that the program doesn't select all the pixels although it should. It only retrieves a part of them ( i am sure the pixels 'forgotten' are the same color as the selected color )
The wierd thing is that if i run my program for the new image ( the one constructed from the matrix ) it returns the same image ( as it should ) but i can't figure out how to fix the problem.
Please Help!!!
Regards,
Alex Badescu
and some code from my project :
bitmap declaration:
m_Bitmap = (Bitmap)Bitmap.FromFile(openFileDialog.FileName, false);
Here i calculate the matrix:
int bitmapWidth = m_Bitmap.Width;
int bitmapHeight = m_Bitmap.Height;
indexMatrix = new int[bitmapHeight][];
if (imageIsLoaded && colorIsSelected)
{
for (int i = 0; i < bitmapHeight; i++)
{
indexMatrix[i] = new int[bitmapWidth];
for (int j = 0; j < bitmapWidth; j++)
{
Color temp = m_Bitmap.GetPixel(j, i);
if (temp == selectedColor)
indexMatrix[i][j] = 1;
else indexMatrix[i][j] = 0;
}
}
MessageBox.Show("matrix generated succesfully");
}
matrixIsCalculated = true;
}
There is no obvious failure mode here. Other than that the pixel isn't actually a match with the color. Being off by, say, only one in the Color.B value for example. You cannot see this with the unaided eye.
These kind of very subtle color changes are quite common when the image was resized. An interpolation filter alters the color subtly, even if not strictly needed. Another failure mode is using a compressed image format like JPEG, the compression algorithm changes colors.

Categories

Resources