DrawImage fails to position sliced images correctly - c#

For a couple of days now I've tried to figure out why my nine-slice code does not work as expected. As far as I can see, there seems to be an issue with the Graphics.DrawImage method which handles my nine slice images incorrectly. So my problem is how to compensate for the incorrect scaling that is performed when running my code on the compact framework. I might add that this code of course works perfectly when running in the full framework environment. The problem only occurs when scaling the image to a larger image not the other way around. Here is the snippet:
public class NineSliceBitmapSnippet
{
private Bitmap m_OriginalBitmap;
public int CornerLength { get; set; }
/// <summary>
/// Initializes a new instance of the NineSliceBitmapSnippet class.
/// </summary>
public NineSliceBitmapSnippet(Bitmap bitmap)
{
CornerLength = 5;
m_OriginalBitmap = bitmap;
}
public Bitmap ScaleSingleBitmap(Size size)
{
Bitmap scaledBitmap = new Bitmap(size.Width, size.Height);
int[] horizontalTargetSlices = Slice(size.Width);
int[] verticalTargetSlices = Slice(size.Height);
int[] horizontalSourceSlices = Slice(m_OriginalBitmap.Width);
int[] verticalSourceSlices = Slice(m_OriginalBitmap.Height);
using (Graphics graphics = Graphics.FromImage(scaledBitmap))
{
using (Brush brush = new SolidBrush(Color.Fuchsia))
{
graphics.FillRectangle(brush, new Rectangle(0, 0, size.Width, size.Height));
}
int horizontalTargetOffset = 0;
int verticalTargetOffset = 0;
int horizontalSourceOffset = 0;
int verticalSourceOffset = 0;
for (int x = 0; x < horizontalTargetSlices.Length; x++)
{
verticalTargetOffset = 0;
verticalSourceOffset = 0;
for (int y = 0; y < verticalTargetSlices.Length; y++)
{
Rectangle destination = new Rectangle(horizontalTargetOffset, verticalTargetOffset, horizontalTargetSlices[x], verticalTargetSlices[y]);
Rectangle source = new Rectangle(horizontalSourceOffset, verticalSourceOffset, horizontalSourceSlices[x], verticalSourceSlices[y]);
graphics.DrawImage(m_OriginalBitmap, destination, source, GraphicsUnit.Pixel);
verticalTargetOffset += verticalTargetSlices[y];
verticalSourceOffset += verticalSourceSlices[y];
}
horizontalTargetOffset += horizontalTargetSlices[x];
horizontalSourceOffset += horizontalSourceSlices[x];
}
}
return scaledBitmap;
}
public int[] Slice(int length)
{
int cornerLength = CornerLength;
if (length <= (cornerLength * 2))
throw new Exception("Image to small for sliceing up");
int[] slices = new int[3];
slices[0] = cornerLength;
slices[1] = length - (2 * cornerLength);
slices[2] = cornerLength;
return slices;
}
}
So, my question is, does anybody now how I could compensate the incorrect scaling?
/Dan

After some more trial and error I've finally found a solution to my problem. The scaling problems has always been to the top-center, right-center, bottom-center and left-center slices since they're always stretched in only one direction according to the logic of nine slice scaling. If I apply a temporarely square stretch to those slices before applying the correct stretch the final bitmap will be correct. Once again the problem is only visible in the .Net Compact Framework of a Windows CE device (Smart Device). Here's a snippet with code adjusting for the bug in CF. My only concern now is that the slices that get square stretched will take much more memory due to the correction code. On the other hand this step is only a short period of time so I might get away with it. ;)
public class NineSliceBitmapSnippet
{
private Bitmap m_OriginalBitmap;
public int CornerLength { get; set; }
public NineSliceBitmapSnippet(Bitmap bitmap)
{
CornerLength = 5;
m_OriginalBitmap = bitmap;
}
public Bitmap Scale(Size size)
{
if (m_OriginalBitmap != null)
{
return ScaleSingleBitmap(size);
}
return null;
}
public Bitmap ScaleSingleBitmap(Size size)
{
Bitmap scaledBitmap = new Bitmap(size.Width, size.Height);
int[] horizontalTargetSlices = Slice(size.Width);
int[] verticalTargetSlices = Slice(size.Height);
int[] horizontalSourceSlices = Slice(m_OriginalBitmap.Width);
int[] verticalSourceSlices = Slice(m_OriginalBitmap.Height);
using (Graphics graphics = Graphics.FromImage(scaledBitmap))
{
using (Brush brush = new SolidBrush(Color.Fuchsia))
{
graphics.FillRectangle(brush, new Rectangle(0, 0, size.Width, size.Height));
}
int horizontalTargetOffset = 0;
int verticalTargetOffset = 0;
int horizontalSourceOffset = 0;
int verticalSourceOffset = 0;
for (int x = 0; x < horizontalTargetSlices.Length; x++)
{
verticalTargetOffset = 0;
verticalSourceOffset = 0;
for (int y = 0; y < verticalTargetSlices.Length; y++)
{
Rectangle destination = new Rectangle(horizontalTargetOffset, verticalTargetOffset, horizontalTargetSlices[x], verticalTargetSlices[y]);
Rectangle source = new Rectangle(horizontalSourceOffset, verticalSourceOffset, horizontalSourceSlices[x], verticalSourceSlices[y]);
bool isWidthAffectedByVerticalStretch = (y == 1 && (x == 0 || x == 2) && destination.Height > source.Height);
bool isHeightAffectedByHorizontalStretch = (x == 1 && (y == 0 || y == 2) && destination.Width > source.Width);
if (isHeightAffectedByHorizontalStretch)
{
BypassDrawImageError(graphics, destination, source, Orientation.Horizontal);
}
else if (isWidthAffectedByVerticalStretch)
{
BypassDrawImageError(graphics, destination, source, Orientation.Vertical);
}
else
{
graphics.DrawImage(m_OriginalBitmap, destination, source, GraphicsUnit.Pixel);
}
verticalTargetOffset += verticalTargetSlices[y];
verticalSourceOffset += verticalSourceSlices[y];
}
horizontalTargetOffset += horizontalTargetSlices[x];
horizontalSourceOffset += horizontalSourceSlices[x];
}
}
return scaledBitmap;
}
private void BypassDrawImageError(Graphics graphics, Rectangle destination, Rectangle source, Orientation orientationAdjustment)
{
Size adjustedSize = Size.Empty;
switch (orientationAdjustment)
{
case Orientation.Horizontal:
adjustedSize = new Size(destination.Width, destination.Width);
break;
case Orientation.Vertical:
adjustedSize = new Size(destination.Height, destination.Height);
break;
default:
break;
}
using (Bitmap quadScaledBitmap = new Bitmap(adjustedSize.Width, adjustedSize.Height))
{
using (Graphics tempGraphics = Graphics.FromImage(quadScaledBitmap))
{
tempGraphics.Clear(Color.Fuchsia);
tempGraphics.DrawImage(m_OriginalBitmap, new Rectangle(0, 0, adjustedSize.Width, adjustedSize.Height), source, GraphicsUnit.Pixel);
}
graphics.DrawImage(quadScaledBitmap, destination, new Rectangle(0, 0, quadScaledBitmap.Width, quadScaledBitmap.Height), GraphicsUnit.Pixel);
}
}
public int[] Slice(int length)
{
int cornerLength = CornerLength;
if (length <= (cornerLength * 2))
throw new Exception("Image to small for sliceing up");
int[] slices = new int[3];
slices[0] = cornerLength;
slices[1] = length - (2 * cornerLength);
slices[2] = cornerLength;
return slices;
}
}

Related

How to compare colors on image and crop the diff?

In order to perform visual tests under Selenium, I performed the image comparison tests ( 2 images only). I use the file size to see if there is a difference or not. However, nothing tells me where I have this difference and I would like to be able to display the difference(s) shown on the image.
I was thinking of a comparison by color instead of size. It seems complex to me especially since I would like to have an image output showing the difference (with a crop that specifies the area) or by extracting the pixels impacted by this difference. Do you think it's possible to do it under selenium in C#? For the moment, i tried by size.
public static void TestComapre()
{
string imgPath1 = <//PATHNAME >
string imgPath2 = <//PATHNAME >
const int size = 1000;
var len = new FileInfo(imgPath1).Length;
if (len != new FileInfo(imgPath2).Length)
var s1 = File.OpenRead(imgPath1);
var s2 = File.OpenRead(imgPath2);
var buf1 = new byte[size];
var buf2 = new byte[size];
for (int i = 0; i < len / size; i++)
{
s1.Read(buf1, 0, size);
s2.Read(buf2, 0, size);
if (CompareBuffers(buf1, buf2) == false)
Assert.Fail();
}
Assert.True(true);
}
I have a custom made image comparer in C#.
It compares 2 images, ignores magenta pixels (you can use magenta as a MASK for areas to ignore when comparing) and marks different pixels as blue in a new image
////////////////////// VARIABLES //////////////////////////
private string pathReferenceImg;
private string pathTestImg;
private FileInfo fReferenceFile;
private FileInfo fTestFile;
private Bitmap referenceImage;
private Bitmap testImage;
private int areaToCompareWidth;
private int areaToCompareHeight;
public int xMinAreaToCompare = 0;
public int yMinAreaToCompare = 0;
public int pixelDifferenceQuantity = 0;
public List<Point> differentPixelsList = new List<Point>();
private int[] rgbArrayTestImgWithReferenceImgPink;
private int tolerance = 15;
public bool result = false;
////////////////////// CODE //////////////////////////
public void compareFiles(string pathReferenceImg, string pathTestImg)
{
fReferenceFile = new FileInfo(pathReferenceImg);
fTestFile = new FileInfo(pathTestImg);
referenceImage = new Bitmap(pathReferenceImg);
testImage = new Bitmap(pathTestImg);
areaToCompareWidth = referenceImage.Width;
areaToCompareHeight = referenceImage.Height;
while (xMinAreaToCompare < areaToCompareWidth)
{
Color colorRef = referenceImage.GetPixel(xMinAreaToCompare, yMinAreaToCompare);
Color colorTest = testImage.GetPixel(xMinAreaToCompare, yMinAreaToCompare);
//Magenta = 255R,255B,0G
if (colorRef.ToArgb() != Color.Magenta.ToArgb())
{
if (colorRef != colorTest)
{
pixelDifferenceQuantity++;
differentPixelsList.Add(new Point(xMinAreaToCompare, yMinAreaToCompare));
}
}
yMinAreaToCompare ++;
if (yMinAreaToCompare == areaToCompareHeight)
{
xMinAreaToCompare ++;
yMinAreaToCompare = 1;
}
}
if (pixelDifferenceQuantity >= tolerance)
{
Bitmap resultImage = new Bitmap(testImage);
foreach (Point pixel in differentPixelsList)
{
resultImage.SetPixel(pixel.X, pixel.Y, Color.Blue);
}
resultImage.Save(pathTestImg.Replace("TestFolder", "ResultFolder"));
}
else
{
result = true;
}
}
hope it helps.

Break cycles without leaks

I have simple code that trying to find sequence of pixel on the screen.
How two break two cycles without memory leaks in function FindPixelSequence
when if (isEqual) occures?
How to improve existing code?
namespace FindColor
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
PlayWithColor PWC = new PlayWithColor();
List<System.Drawing.Color> pixels { get; set; }
public MainWindow()
{
InitializeComponent();
pixels = new List<System.Drawing.Color>();
pixels.Add(System.Drawing.Color.FromArgb(0, 0, 0, 0));
pixels.Add(System.Drawing.Color.FromArgb(0, 153, 255, 0));
pixels.Add(System.Drawing.Color.FromArgb(0, 153, 255, 0));
pixels.Add(System.Drawing.Color.FromArgb(0, 128, 214, 0));
}
private void Button_Click(object sender, RoutedEventArgs e)
{
using (var bitmap = PWC.TakeScreen())
{
PWC.FindPixelSequence(bitmap, pixels);
}
}
}
class ColorEqualityComparer : IEqualityComparer<Color>
{
public bool Equals(Color b1, Color b2)
{
return b1.R == b2.R && b1.G == b2.G && b1.B == b2.B;
}
public int GetHashCode(Color obj)
{
throw new NotImplementedException();
}
}
public class PlayWithColor
{
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool SetCursorPos(int x, int y);
private Bitmap bmpScreenshot { get; set; }
public Bitmap TakeScreen()
{
bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height,
PixelFormat.Format32bppArgb);
var gfxScreenshot = Graphics.FromImage(bmpScreenshot);
int sourceX = Screen.PrimaryScreen.Bounds.X;
int sourceY = Screen.PrimaryScreen.Bounds.Y;
gfxScreenshot.CopyFromScreen(sourceX,
sourceY,
0,
0,
Screen.PrimaryScreen.Bounds.Size,
CopyPixelOperation.SourceCopy);
return bmpScreenshot;
}
public System.Drawing.Point? FindPixelSequence(Bitmap bitmap, List<Color> pixels)
{
Stopwatch watch = new Stopwatch();
List<Color> currentPixels;
watch.Start();
BitmapData data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
unsafe
{
byte* ptrSrc = (byte*)data.Scan0;
for (int y = 0; y < data.Height; y = y + 1)
{
for (int x = 0; x < data.Width; x = x + 1)
{
currentPixels = new List<Color>();
for (int i = 0; i < pixels.Count; i++)
{
byte r0 = ptrSrc[2];
byte g0 = ptrSrc[1];
byte b0 = ptrSrc[0];
Color currentPixel = Color.FromArgb(0, r0, g0, b0);
ptrSrc += 4;
currentPixels.Add(currentPixel);
}
ptrSrc -= (4 * (pixels.Count - 1));
bool isEqual = currentPixels.SequenceEqual(pixels, new ColorEqualityComparer());
if (isEqual)
{
SetCursorPos(x, y);
//how return coords of x and y from there?
}
}
}
}
bitmap.UnlockBits(data);
watch.Stop();
Debug.WriteLine(watch.ElapsedMilliseconds);
}
}
}
for the unsafe code surround it with a try finally , to always unlock the bitmap and release any memory you allocated internally
1.
unsafe
{
Stopwatch watch = new Stopwatch();
watch.Start();
try
{
....
return new Point(x,y); // To return x,y corrdinates
}
finally
{
bitmap.UnlockBits(data);
watch.Stop();
}
}
to simply the code I would use a 3rd party image tool software library for c# you have AForge.NET

Color detection C#

I want to make color_detection_bot and This bot provide me that when I click the button in this form, mousecursor changes position to facebook login button and click it.First of all I created a bitmap in Paint and save in System.Resources name as bmpLogin and this bitmap shows a 24 bit bitmap of facebook login button.
private void btn_login_Click(object sender, EventArgs e)
{
this.BackgroundImage = Screenshot(); //Method which is take screenshot
Point location;
bool success=FindBitmap(Properties.Resources.bmpLogin, Screenshot(), out location);
if (success == false)
{
MessageBox.Show("Couldn't find the login button");
return;
}
Cursor.Position = location; //bitmap location within screenshot
MouseCLick(); //Mouse click event works successfull
}
/// <summary>
/// Find the location of bitmap within another bitmap and return if it was succesfully found
/// </summary>
/// <param name="bmpNeedle"> The image we want to find </param>
/// <param name="bmpHaystack"> Where we want to search for the image </param>
/// <param name="location"> Where we found the image </param>
/// <returns> If the bmpNeedle was found succesfully </returns>
private Boolean FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location)
{
for (int outerX = 0; outerX < bmpHaystack.Width; outerX++)
{
for (int outerY = 0; outerY < bmpHaystack.Height ; outerY++)
{
for (int innerX = 0; innerX <bmpNeedle.Width; innerX++)
{
for (int innerY = 0; innerY <bmpNeedle.Height; innerY++)
{
Color cNeedle = bmpNeedle.GetPixel(innerX, innerY);
Color cHaystack = bmpHaystack.GetPixel(innerX + outerX, innerY + outerY);
if (cNeedle.R != cHaystack.R || cNeedle.G != cHaystack.G || cNeedle.B != cHaystack.B)
{
goto Notfound;
}
}
}
location = new Point(outerX, outerY);
return true;
Notfound:
continue;
}
}
location = Point.Empty;
return false;
}
This codes works perfectly But When I change if statemant as shown in below, Bot doesnt work.Where am I doing logical error
private Boolean FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location)
{
for (int outerX = 0; outerX < bmpHaystack.Width; outerX++)
{
for (int outerY = 0; outerY < bmpHaystack.Height; outerY++)
{
for (int innerX = 0; innerX <bmpNeedle.Width; innerX++)
{
for (int innerY = 0; innerY <bmpNeedle.Height; innerY++)
{
Color cNeedle = bmpNeedle.GetPixel(innerX, innerY);
Color cHaystack = bmpHaystack.GetPixel(innerX + outerX, innerY + outerY);
if (cNeedle.R == cHaystack.R && cNeedle.G == cHaystack.G && cNeedle.B == cHaystack.B)
{
location = new Point(outerX, outerY);
return true;
}
}
}
}
}
location = Point.Empty;
return false;
}
In your first code sample, the algorithm is matching all the pixels. In your second code sample, the algorithm is returning the location of the first matched pixel.
It's not enough just to match one pixel. For example, say your full image is:
1 2 3 4
1 a a b b
2 a a c a
3 a b a a
And you're searching for:
1 2
1 a b
2 a c
Your second block of code looks first at 1,1 and says, brilliant, they are both a, job done, and merrily returns the wrong answer. However your first block of code will look at 1,1, it matches so then it goes to 1,2 see that it is b whereas they haystack is a, and then the goto takes it to not found, and it searches the next position of the haystack.
As already pointed out, it's a good demonstration of what's wrong with goto. See here GOTO still considered harmful? for a full discussion on the topic.
Two other problems with this - one already mentioned, that if your search image is in the bottom right, you're going to go out of bounds when you compare it to the larger image - if we use the simple example above, if your search start point is at 4,3, then as you go to compare to the second column of the smaller image, you're looking at 5,3, which doesn't exist. Second problem, GetPixel is very slow, for a large search image this code is going to take a while - and it can't be multi-threaded. A faster but more complicated implementation is to use LockBits, which is much faster to access. Sample implementation is below.
static void Main(string[] args)
{
Bitmap haystack = (Bitmap)Image.FromFile(#"c:\temp\haystack.bmp");
Bitmap needle = (Bitmap)Image.FromFile(#"c:\temp\needle.bmp");
Point location;
if (FindBitmap(haystack, needle, out location) == false)
{
Console.WriteLine("Didn't find it");
}
else
{
using (Graphics g = Graphics.FromImage(haystack))
{
Brush b = new SolidBrush(Color.Red);
Pen pen = new Pen(b, 2);
g.DrawRectangle(pen, new Rectangle(location, new Size(needle.Width, needle.Height)));
}
haystack.Save(#"c:\temp\found.bmp");
Console.WriteLine("Found it # {0}, {1}", location.X, location.Y);
}
Console.ReadKey();
}
public class LockedBitmap : IDisposable
{
private BitmapData _data = null;
private Bitmap _sourceBitmap = null;
private byte[] _pixelData = null;
public readonly int BytesPerPixel, WidthInBytes, Height, Width, ScanWidth;
public LockedBitmap(Bitmap bitmap)
{
_sourceBitmap = bitmap;
_data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
BytesPerPixel = Bitmap.GetPixelFormatSize(bitmap.PixelFormat) / 8;
ScanWidth = _data.Stride;
int byteCount = _data.Stride * bitmap.Height;
_pixelData = new byte[byteCount];
Marshal.Copy(_data.Scan0, _pixelData, 0, byteCount);
WidthInBytes = bitmap.Width * BytesPerPixel;
Height = bitmap.Height;
Width = bitmap.Width;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void GetPixel(int x, int y, out int r, out int g, out int b)
{
int realY = y * ScanWidth;
int realX = x * BytesPerPixel;
b = (int)_pixelData[realY + realX];
g = (int)_pixelData[realY + realX + 1];
r = (int)_pixelData[realY + realX + 2];
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing) return;
if (_sourceBitmap != null && _data != null)
_sourceBitmap.UnlockBits(_data);
}
}
private static bool FindBitmap(Bitmap haystack, Bitmap needle, out Point location)
{
using (LockedBitmap haystackToSearch = new LockedBitmap(haystack))
using (LockedBitmap needleToSearch = new LockedBitmap(needle))
{
for (int outerY = 0; outerY <= (haystackToSearch.Height - needleToSearch.Height); outerY++)
{
for (int outerX = 0; outerX <= (haystackToSearch.Width - needleToSearch.Width); outerX++)
{
bool isMatched = true;
for (int innerY = 0; innerY < needleToSearch.Height; innerY++)
{
for (int innerX = 0; innerX < needleToSearch.Width; innerX++)
{
int needleR, needleG, needleB;
int haystackR, haystackG, haystackB;
haystackToSearch.GetPixel(outerX + innerX, outerY + innerY, out haystackR, out haystackG, out haystackB);
needleToSearch.GetPixel(innerX, innerY, out needleR, out needleG, out needleB);
isMatched = isMatched && needleR == haystackR && haystackG == needleG && haystackB == needleB;
if (!isMatched)
break;
}
if (!isMatched)
break;
}
if (isMatched)
{
location = new Point(outerX, outerY);
return true;
}
}
}
}
location = new Point();
return false;
}

OpenCv: Finding multiple matches

I have the following, but I can't figure out how to find ALL the matches in a source image.
static void Main()
{
using (var template = Cv.LoadImage(#"images\logo.png", LoadMode.GrayScale))
using (var source = Cv.LoadImage(#"images\manyLogos.png", LoadMode.GrayScale))
using (var sourceColour = Cv.LoadImage(#"images\manyLogos.png", LoadMode.Color))
{
var width = source.Width - template.Width + 1;
var height = source.Height - template.Height + 1;
using (var result = Cv.CreateImage(Cv.Size(width, height), BitDepth.F32, 1))
{
Cv.MatchTemplate(source, template, result, MatchTemplateMethod.SqDiff);
var THRESHOLD = 0.08D;
double minVal, maxVal;
CvPoint minLoc, maxLoc;
Cv.MinMaxLoc(result, out minVal, out maxVal, out minLoc, out maxLoc);
var outlineColor = (minVal > THRESHOLD) ? CvColor.Green : CvColor.Red;
Cv.Rectangle(sourceColour, Cv.Point(minLoc.X, minLoc.Y), Cv.Point(minLoc.X + template.Width, minLoc.Y + template.Height), outlineColor, 1, 0, 0);
}
using (var window = new CvWindow("Test"))
{
while (CvWindow.WaitKey(10) < 0)
{
window.Image = sourceColour;
}
}
}
}
I can outline the best match, just not all the matches. I need to get all the matches somehow.
Using matchTemplate method, your output image will give you pixels values which represents how well your template is matched at this specific location. In you case, the lower the value, the best the match, since you used MatchTemplateMethod.SqDiff.
You problem is that when you use the minMaxLoc function, you get what you asks for, which is the best match in this case, the min).
All matches are the pixels whose value are under the threshold that you set up.
Since I'm not used to csharp, here is how it would go in C++, you can do the translation:
// after your call to MatchTemplate
float threshold = 0.08;
cv::Mat thresholdedImage;
cv::threshold(result, thresholdedImage, threshold, 255, CV_THRESH_BINARY);
// the above will set pixels to 0 in thresholdedImage if their value in result is lower than the threshold, to 255 if it is larger.
// in C++ it could also be written cv::Mat thresholdedImage = result < threshold;
// Now loop over pixels of thresholdedImage, and draw your matches
for (int r = 0; r < thresholdedImage.rows; ++r) {
for (int c = 0; c < thresholdedImage.cols; ++c) {
if (!thresholdedImage.at<unsigned char>(r, c)) // = thresholdedImage(r,c) == 0
cv::circle(sourceColor, cv::Point(c, r), template.cols/2, CV_RGB(0,255,0), 1);
}
}
Translating from C++ and using OpenCvSharp wrapper, the above code, replacing minMaxLoc lines, worked for me:
double threshold=0.9
var thresholdImage=Cv.CreateImage(newImageSize, BitDepth.F32,1);
Cv.Threshold(result, thresholdImage, threshold, 255, ThresholdType.Binary);
for (int r = 0; r < thresholdImage.GetSize().Height; r++)
{
for (int c = 0; c < thresholdImage.GetSize().Width; c++)
{
if (thresholdImage.GetRow(r)[c].Val0 > 0)
{
Cv.Rectangle(soruceColour, Cv.Point(c, r), Cv.Point(c + template.Width, r + template.Height), CvColor.Red, 1, 0, 0);
}
}
}
here is the solution using Min_Max and Match_Template methods. hope it will help.
public void multipleTemplateMatch(string SourceImages, string tempImage)
{
Image<Bgr, byte> image_source = new Image<Bgr, byte>(SourceImages);
Image<Bgr, byte> image_partial1 = new Image<Bgr, byte>(tempImage);
double threshold = 0.9;
ImageFinder imageFinder = new ImageFinder(image_source, image_partial1, threshold);
imageFinder.FindThenShow();
}
and here is the class which will help.
class ImageFinder
{
private List<Rectangle> rectangles;
public Image<Bgr, byte> BaseImage { get; set; }
public Image<Bgr, byte> SubImage { get; set; }
public Image<Bgr, byte> ResultImage { get; set; }
public double Threashold { get; set; }
public List<Rectangle> Rectangles
{
get { return rectangles; }
}
public ImageFinder(Image<Bgr, byte> baseImage, Image<Bgr, byte> subImage, double threashold)
{
rectangles = new List<Rectangle>();
BaseImage = baseImage;
SubImage = subImage;
Threashold = threashold;
}
public void FindThenShow()
{
FindImage();
DrawRectanglesOnImage();
ShowImage();
}
public void DrawRectanglesOnImage()
{
ResultImage = BaseImage.Copy();
foreach (var rectangle in this.rectangles)
{
ResultImage.Draw(rectangle, new Bgr(Color.Blue), 1);
}
}
public void FindImage()
{
rectangles = new List<Rectangle>();
using (Image<Bgr, byte> imgSrc = BaseImage.Copy())
{
while (true)
{
using (Image<Gray, float> result = imgSrc.MatchTemplate(SubImage, TemplateMatchingType.CcoeffNormed))
{
double[] minValues, maxValues;
Point[] minLocations, maxLocations;
result.MinMax(out minValues, out maxValues, out minLocations, out maxLocations);
if (maxValues[0] > Threashold)
{
Rectangle match = new Rectangle(maxLocations[0], SubImage.Size);
imgSrc.Draw(match, new Bgr(Color.Blue), -1);
rectangles.Add(match);
}
else
{
break;
}
}
}
}
}
public void ShowImage()
{
Random rNo = new Random();
string outFilename = "matched Templates" + rNo.Next();
CvInvoke.Imshow(outFilename, ResultImage);
}
}
if you find this helpful please vote is as useful.
thanks

C# Very Simple Image Resizer

I am in need of a very simple c# image resizer. By simple, I mean simple. This is just a program that loops through a single directory and changes all the pictures in that directory to the same resolution. Here's what I have so far.
private void Form1_Load(object sender, EventArgs e)
{
string[] files = null;
int count = 0;
files = System.IO.Directory.GetFiles(#"C:\Users\..\..\ChristmasPicsResized");
foreach (string file in files)
{
System.Drawing.Bitmap bmp = System.Drawing.Bipmap.FromFile(file);
ResizeBitmap(bmp, 807, 605);
bmp.Save(file);
count++;
lblCount.Text = count.ToString();
}
}
public Bitmap ResizeBitmap(Bitmap b, int nWidth, int nHeight)
{
Bitmap result = new Bitmap(nWidth, nHeight);
using (Graphics g = Graphics.FromImage((Image)result))
g.DrawImage(b, 0, 0, nWidth, nHeight);
return result;
}
The problem I ran into is that the picture cannot be saved while it is open. I am unsure how to make this into a file stream. What should be a very simple app doesn't seem so simple to me. Any help please?
Try saving to a temp file, then delete the original file and rename the temp file to the original file name.
Have a look at C# Image to Byte Array and Byte Array to Image Converter Class
public byte[] imageToByteArray(System.Drawing.Image imageIn)
{
MemoryStream ms = new MemoryStream();
imageIn.Save(ms,System.Drawing.Imaging.ImageFormat.Gif);
return ms.ToArray();
}
and
public Image byteArrayToImage(byte[] byteArrayIn)
{
MemoryStream ms = new MemoryStream(byteArrayIn);
Image returnImage = Image.FromStream(ms);
return returnImage;
}
This way you can close the image after you have read it in, and can then save it over the existing one.
You could also render the resized images into a different folder to preserve the original, high-resolution images. Maybe you'll need them one day (i did that mistake once...).
I created this nuget package which does resizing for you:
http://nuget.org/packages/Simple.ImageResizer
Source and howto here:
https://github.com/terjetyl/Simple.ImageResizer
My C# Image Extension class:
namespace MycImageExtension
{
public static class Helper
{
public static Image Clip(this Image imgPhoto, int width, int height)
{
return Clip(imgPhoto, width, height, false);
}
public static Image ToImage(this byte[] ba)
{
var ms = new MemoryStream(ba);
return Image.FromStream(ms);
}
public static byte[] ToArray(this Image imgPhoto)
{
var ms = new MemoryStream();
imgPhoto.Save(ms, ImageFormat.Jpeg);
return ms.ToArray();
}
static ImageCodecInfo GetImageCodec(string mimetype)
{
foreach (ImageCodecInfo ici in ImageCodecInfo.GetImageEncoders())
{
if (ici.MimeType == mimetype) return ici;
}
return null;
}
public static Image Clip(this Image imgPhoto, int width, int height, bool stretch)
{
if (!stretch && (imgPhoto.Width <= width && imgPhoto.Height <= height))
return imgPhoto;
// detect if portrait
if (imgPhoto.Height > imgPhoto.Width)
{
// swap
int a = width;
width = height;
height = a;
}
var d = new Dimension(imgPhoto.Width, imgPhoto.Height);
double scale = d.NewSizeScaleFactor(new Dimension(width, height), stretch);
var newD = scale * d;
if (stretch)
{
if (!(newD.Width == width || newD.Height == height))
throw new Exception("Stretching algo has some error");
}
var bmPhoto = new Bitmap(imgPhoto, new Size(newD.Width, newD.Height));
return bmPhoto;
}
// using for crystal report
public static Image PadImage(this Image imgPhoto, int width, int height)
{
// detect if portrait
if (imgPhoto.Height > imgPhoto.Width)
{
// swap
int a = width;
width = height;
height = a;
}
var d = new Dimension(imgPhoto.Width, imgPhoto.Height);
double scale = d.NewSizeScaleFactor(new Dimension(width, height), true);
Dimension newSize = scale * d;
PadAt padAt;
int padNeeded;
newSize.GetPadNeeded(new Dimension(width, height), out padAt, out padNeeded);
int padLeft = 0, padTop = 0;
if (padAt == PadAt.Width)
padLeft = padNeeded;
else if (padAt == PadAt.Height)
padTop = padNeeded;
var bmPhoto = new Bitmap(width, height, PixelFormat.Format24bppRgb);
var grPhoto = Graphics.FromImage(bmPhoto);
grPhoto.Clear(Color.White);
grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
grPhoto.DrawImage(imgPhoto,
new Rectangle(padLeft, padTop, newSize.Width, newSize.Height),
new Rectangle(0, 0, imgPhoto.Width, imgPhoto.Height),
GraphicsUnit.Pixel);
grPhoto.Dispose();
return bmPhoto;
}
}
public enum PadAt { None = 0, Width = 1, Height }
public struct Dimension
{
public int Width { set; get; }
public int Height { set; get; }
public Dimension(int width, int height)
: this()
{
this.Width = width;
this.Height = height;
}
public override string ToString()
{
return string.Format("Width: {0} Height: {1}", Width, Height);
}
public static Dimension operator *(Dimension src, double scale)
{
return new Dimension((int)Math.Ceiling((scale * src.Width)), (int)Math.Ceiling((scale * src.Height)));
}
public static Dimension operator *(double scale, Dimension src)
{
return new Dimension((int)Math.Ceiling((scale * src.Width)), (int)Math.Ceiling((scale * src.Height)));
}
public double NewSizeScaleFactor(Dimension newSize, bool stretch)
{
if (!stretch
&&
(this.Width <= newSize.Width && this.Height <= newSize.Height))
return 1;
double widthScaleFactor = (double)newSize.Width / this.Width;
double heightScaleFactor = (double)newSize.Height / this.Height;
// return the lowest scale factor
if (widthScaleFactor < heightScaleFactor)
return widthScaleFactor;
else if (heightScaleFactor < widthScaleFactor)
return heightScaleFactor;
else
return widthScaleFactor; // can even use heightscalefactor
}
public Dimension Clip(Dimension newSize, bool stretch)
{
if (!stretch
&&
(this.Width <= newSize.Width && this.Height <= newSize.Height))
return new Dimension(this.Width, this.Height);
double smallestScaleFactor = NewSizeScaleFactor(newSize, stretch);
return new Dimension((int)(this.Width * smallestScaleFactor), (int)(this.Height * smallestScaleFactor));
}
// so crystal report images would have exact dimension
public void GetPadNeeded(Dimension newSize, out PadAt padAt, out int padNeeded)
{
if (this.Width == newSize.Width && this.Height == newSize.Height)
{
padAt = PadAt.None;
padNeeded = 0;
return;
}
if (this.Width > newSize.Width || this.Height > newSize.Height)
throw new Exception("Source cannot be bigger than the new size");
if (this.Width != newSize.Width && this.Height != newSize.Height)
throw new Exception("At least one side should be equal");
if (newSize.Width != this.Width)
{
padAt = PadAt.Width;
padNeeded = (newSize.Width - this.Width) / 2;
}
else if (newSize.Height != this.Width)
{
padAt = PadAt.Height;
padNeeded = (newSize.Height - this.Height) / 2;
}
else
{
throw new Exception("Some anomaly occured, contact the programmer");
}
}
// test the logic
static void X()
{
var ls = new Dimension[]
{
new Dimension(400, 400), // as is
new Dimension(640, 480), // as is
new Dimension(600, 480), // as is
new Dimension(800, 600), // as is
new Dimension(800, 400), // as is
new Dimension(1280, 960), // as is
new Dimension(1280, 961), // as is
new Dimension(1281, 960), // as is
new Dimension(1280, 900), // as is
new Dimension(1000, 960), // as is
new Dimension(1000, 970), // as is
new Dimension(1000, 900), // as is
new Dimension(1380, 960), // clip
new Dimension(1280, 1000), // clip
new Dimension(1380, 1300), // clip
new Dimension(1600, 1200), // clip
new Dimension(1600, 1000), // clip
new Dimension(1800, 1200), // clip
new Dimension(1800, 1000), // clip
new Dimension(1400, 1200), // clip
new Dimension(1400, 1000), // clip
new Dimension(960, 1280), // clip
new Dimension(960, 1300), // clip
new Dimension(970, 1280) // clip
};
foreach (var l in ls)
{
// saving to database...
double scale = l.NewSizeScaleFactor(new Dimension(1280, 960), false);
Dimension newSize = scale * l;
// ...saving to database
// display to crystal report...
double scaleA = l.NewSizeScaleFactor(new Dimension(800, 600), true);
Dimension newSizeA = scaleA * l;
PadAt padAt;
int padNeeded;
newSizeA.GetPadNeeded(new Dimension(800, 600), out padAt, out padNeeded);
// ...display to crystal report
Console.WriteLine("Pad {0} {1}", padAt, padNeeded);
Console.WriteLine();
}
Console.ReadLine();
}
}
}

Categories

Resources