Why isn't my image loading? - c#

private void LoadImageList()
{
string filepath = Application.StartupPath + #"\Content\Textures\Tiles\PlatformTiles.png";
Bitmap tileSheet = new Bitmap(filepath);
int tilecount = 0;
for (int y = 0; y < tileSheet.Height / TileGrid.TileHeight; y++)
{
for (int x = 0; x < tileSheet.Width / TileGrid.TileWidth; x++)
{
Bitmap newBitmap = tileSheet.Clone(
new System.Drawing.Rectangle(
x * TileGrid.TileWidth,
y * TileGrid.TileHeight,
TileGrid.TileWidth,
TileGrid.TileHeight),
System.Drawing.Imaging.PixelFormat.DontCare);
imgListTiles.Images.Add(newBitmap);
string itemName = "";
if (tilecount == 0)
{
itemName = "Empty";
}
if (tilecount == 1)
{
itemName = "White";
}
listTiles.Items.Add(new ListViewItem(itemName, tilecount++));
}
}
}
All I did was update PlatformTiles.png with a newer one and the next time I ran the program it doesn't load. I placed a breakpoint at int tilecount = 0; and it doesn't ever reach it. Every thing after it doesn't load either. Any ideas?

Is that Bitmap referring to System.Drawing.Bitmap? If so your code has a memory leak. You're creating hundreds (thousands?) of Bitmap objects without disposing them. Each Bitmap object encapsulates a GDI Bitmap surface which must be explicitly disposed. Use a C# using(Bitmap bmp) { } block to ensure they're disposed when they fall out of scope, or ensure that you dispose of the bitmaps when the parent class object is disposed.
This may be related to your problem (generally it's a bad idea to create too many bitmaps in case you hit the GDI Object limit).

I got rid of the string and replaced filepatth, Bitmap tileSheet = new Bitmap(filepath);, with the path of the image on my hard-drive and it worked. Solution, but could use some explaining.

If the file is part of your content project and it's Build Action is set to Compile, the file that's in that folder at run time does not have a .png extension so the Bitmap will never be created. Content assets have a .xnb extension as they've been compiled to a binary format. Change the Build Action to None and the Copy to Output Direction to Copy if newer

Related

searching image in another image, only return a partial result

The error with the following code is, it only find, partially, the first image itself.
Let me be more clear, I took a screenshot of an image, cropped it and putted it on the desktop. Then I saved the actual original image in full size (not cropped) on desktop as well.
When I run the code, it correctly detects all the coordinates of the cropped image on the desktop, however, when I open the full size image and hover it, it won't detect anything and return null, while it should be able to detect the pixels location inside the full size image as well.
I'm gonna post a video of what I mean, to be more clear:
https://www.youtube.com/watch?v=Ha3eGxWcAF8
As you see, it does not detect the pixels on the main image.
What's wrong on there?
Why it detects only the loaded cropped image, instead of a position on the screenshot based on the actual full fize image?
Code below:
public static Point? Find(Bitmap first, Bitmap second)
{
if (null == first || null == second)
{
return null;
}
if (first.Width < second.Width || first.Height < second.Height)
{
return null;
}
var firstArray = GetPixelArray(first);
var secondArray = GetPixelArray(second);
foreach (var firstLineMatchPoint in FindMatch(firstArray.Take(first.Height - second.Height), secondArray[0]))
{
if (IssecondPresentAtLocation(firstArray, secondArray, firstLineMatchPoint, 1))
{
return firstLineMatchPoint;
}
}
return null;
}
private static int[][] GetPixelArray(Bitmap bitmap)
{
var result = new int[bitmap.Height][];
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
for (int y = 0; y < bitmap.Height; ++y)
{
result[y] = new int[bitmap.Width];
Marshal.Copy(bitmapData.Scan0 + y * bitmapData.Stride, result[y], 0, result[y].Length);
}
bitmap.UnlockBits(bitmapData);
return result;
}
private static IEnumerable<Point> FindMatch(IEnumerable<int[]> firstLines, int[] secondLine)
{
var y = 0;
foreach (var firstLine in firstLines)
{
for (int x = 0, n = firstLine.Length - secondLine.Length; x < n; ++x)
{
if (ContainSameElements(firstLine, x, secondLine, 0, secondLine.Length))
{
yield return new Point(x, y);
}
}
y += 1;
}
}
private static bool ContainSameElements(int[] first, int firstStart, int[] second, int secondStart, int length)
{
for (int i = 0; i < length; ++i)
{
if (first[i + firstStart] != second[i + secondStart])
{
return false;
}
}
return true;
}
private static bool IssecondPresentAtLocation(int[][] first, int[][] second, Point point, int alreadyVerified)
{
//we already know that "alreadyVerified" lines already match, so skip them
for (int y = alreadyVerified; y < second.Length; ++y)
{
if (!ContainSameElements(first[y + point.Y], point.X, second[y], 0, second.Length))
{
return false;
}
}
return true;
}
static void Main(string[] args)
{
int counter = 1;
while (true)
{
Console.WriteLine("Executing pixel search");
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
Bitmap bitmap2 = (Bitmap)Image.FromFile(#"path_to_desktop\cropped.jpg", true);
if (Find(bitmap, bitmap2) == null)
{
Console.WriteLine("Not found.");
}
else {
Console.WriteLine($"{Find(bitmap, bitmap2)}.");
}
System.Threading.Thread.Sleep(500); // loop to detect new position
counter++;
}
}
Edit 7.11.2020: I published a Sample Project and Video as a result of this question:
Sample:
https://github.com/DataJuggler/SubImageCreator
Could be called SubImageSearcher also.
Video:
How To Search Images Using C# And DataJuggler.PixelDatabase
https://youtu.be/JKc7QtdaxWY
Search for a sub image in a larger image using C#.
Nuget Updates
DataJuggler.PixelDatabase .Net Core
DataJuggler.PixelDatabase.Net .Net Framework
// Return a sub image after a PixelDatabase is loaded
Bitmap subImage = CreateSubImage(Point topLeft, Rectangle size);
// Search for a sub image inside a larger image
SearchResult result = PixelDatabase.SearchForSubImage(bitmap, searchDepth);
Search Result contains a Point of the top left and a score. A score of zero is a perfect match.
The sample project draws a little yellow rectangle on the Top Left hand corner if the sub image is found inside the larger image.
--
End Edit
Are the images something you can share? If not a sample image might give something to work with. If yes, tell me what you are trying to find and maybe I can find "where the pixels are you are searching for".
Another thing to watch out for is if either of the images were stretched, any difference like that could distort exact pixel colors and locations.
Are you using .Net Framework or .Net Core?
I have a Nuget package that might help you with this:
Nuget:
DataJuggler.PixelDatabase .Net Core
DataJuggler.PixelDatabase.Net .Net Framework
Both packages were updated yesterday.
Showing a .Net Core example
using DataJuggler.PixelDatabase;
// local
DataJuggler.PixelDatabase.PixelDatabase pixelDatabase = null;
// Load the pixelDatabase (you can also pass in a Bitmap or an Image also)
pixelDatabase = PixelDatabaseLoader.LoadPixelDatabase(FullImagePath, null);
// get information about a pixel at the coordinates given
PixelInformation pixel = PixelDatabase.GetPixel(x, y);
The pixel information object contains the Red Green and Blue colors, plus the X, Y and a lot of read only properties such as Total, BlueRed, MinMaxDifference, etc., that make querying and updating Bitmaps pretty simple.
Another thing you could do is if you know of a certain color you are looking for:
// color
Color color = Color.FromArgb(128, 55, 92);
// get the pixels that match a certain color
List<PixelInformation> pixels = PixelDatabase.GetPixels(color);
That might help you locate certain groups of pixels in the image.
The same code that powers the Nuget packages is also used on my site:
https://PixelDatabase.Net
The site and the Nuget packages use a language called Bitmap Query Language, or BQL for short. This is very similar to SQL for databases if you have learned that yet.
There are lots of help and videos, but if you do any Pixel querying, I think you will find it worth the price of free.
The full source code is on Git Hub:
https://github.com/DataJuggler/PixelDatabase.Net .Net Framework
https://github.com/DataJuggler/PixelDatabase .Net Core
And Help is located here:
https://pixeldatabase.net/Help
And lots of videos here:
https://www.youtube.com/playlist?list=PLKrW5tXCPiX2PxrLPszDzlcEZwQG-Qb8r

Application fails visualizing more than 600 Images

I wrote a WPF app that should swap (fast) between a large set of images (600+, 190Kb average size), but I'm finding some difficulties.
private int appendImages(Canvas c, int start, int end)
{
int tot = 0;
for (int i = start; i < end; i++)
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
//bi.CacheOption = BitmapCacheOption.OnLoad;
bi.UriSource = new Uri(appFolder+#"/"+imgFolder+"/"+filename(i)+".jpg");
bi.EndInit();
Image img = new Image
{
Width = imgWidth,
Height = imgHeight,
Source = bi,
Name = name(i),
Visibility = i == startImg ? Visibility.Visible : Visibility.Hidden
};
c.Children.Add(img);
tot++;
}
}
Apparently the inizialization is fine, but if I try to swap the images like this:
private void changeImageTo(int n)
{
Image img = findImage(n);
Image old = findImage(prevImg);
if (img != null)
{
img.Visibility = Visibility.Visible;
if (old != null && old != img)
old.Visibility = Visibility.Hidden;
prevImg = n;
}
}
..then the app shows the first 200/300 images (depending on the sources I use), and the others are just empty/blank (i can see the canvas underneath).
I suspect it's a memory issue, but I'm not really sure what causes it.
By the way, if I uncomment the commented line (BitmapCacheOption.OnLoad) sometimes I get a vshost error when launching the app.
Any help would be MUCH appreciated, since I couldn't find anything useful browsing around.
Thanks in advance!
It looks like you're loading all the images at once, and putting them into WinForms/WPF controls. That is a very bad idea with that many images, as each one takes resources even if it's not shown.
Rough back of the envelope calculation, assuming 640x480 images, 24bpp being the native GDI+ format, shows a bit over 2gb for loading all the images at once, and that would, of course, increase exponentially with image size.
What I would do instead, is have only one Image. Move the actual image loading code into your changeImageTo function, build the file name based on n, and set the loaded image to the Image there.

How to save a bitmap as an png in console application of c#

I am doing a console application in c#. In this app, I have to create a bitmap of png type and it has to to be stored in some defined path (say C: or D: drive).
In a windows application I have the below code to create a bitmap and it will be shown in a picture box.
void CreateBitmap()
{
System.Drawing.Bitmap flag = new System.Drawing.Bitmap(10, 10);
for( int x = 0; x < flag.Height; ++x )
for( int y = 0; y < flag.Width; ++y )
flag.SetPixel(x, y, Color.White);
for( int x = 0; x < flag.Height; ++x )
flag.SetPixel(x, x, Color.Red);
pictureBox1.Image = flag;
}
How can I create and store this in a specified path using a console application?
I have changed my code as below but still error exists:
static void CreatePng(string[] binvalues)
{
String aName = System.Reflection.Assembly.GetExecutingAssembly().Location;
String aPath = System.IO.Path.GetDirectoryName(aName);
string[] ExecDirectories = System.IO.Directory.GetDirectories(aPath);
System.Drawing.Bitmap flag = new System.Drawing.Bitmap(10, 10);
for (int x = 0; x < flag.Height; ++x)
for (int y = 0; y < flag.Width; ++y)
flag.SetPixel(x, y, Color.White);
for (int x = 0; x < flag.Height; ++x)
flag.SetPixel(x, x, Color.Red);
flag.Save(aPath, System.Drawing.Imaging.ImageFormat.Png);
}
It is showing run-time error in last line where flag.save seems something wrong?
Use the same code, and instead of assigning to a PictureBox, call the Save() method on the bitmap:
flag.Save("yourpath", System.Drawing.Imaging.ImageFormat.Png);
Note that you may have to add a reference to System.Drawing in your console application, as it is not there by default.
Now, I've never done this myself using C# so someone more inclined could possibly help but here's what I was able to find out.
1) You need to have your image data stored in a Bitmap. From the looks of things you're doing this already with flag
2) You need to call the save() function on the Bitmap:
flag.Save(filename, ImageFormat.Png);
3) filename would be a String that you define. This is fairly easy as you could simply have the application prompt the user to enter a path and save it.
Questions?
Disclaimer: I received my info from this page. There is a lot of discussion on the "proper" way to save a png you can dig from.
Use this:
flag.Save(filename, System.Drawing.Imaging.ImageFormat.Png);
You don't need to add the image to a PictureBox. You have a console application and PictureBox is a visual control.
For future questions it would be nice if you spent a minimum amount of time of searching SO or other sources for an answer first
The question is not tagged correctly. If you want to save a System.Drawing.Bitmap it can not be tagged BitmapImage as this is for WPF only. I changed this.
You can try below code-:
public bool ResizeImage(string OriginalFilepath, string NewFilepath)
{
Bitmap original = (Bitmap)Image.FromFile(OriginalFilepath);
Bitmap resized = new Bitmap(original, new Size(Width,Height));
resized.Save(NewFilepath.png);
}

Working with Diff Image (Deltas) in Picturebox Control

I create a picture box and on the first request to my server retrieve the full desktop and display it. On every subsequent request, I'm only getting the diff'ed image which is transparent everywhere except where there are changes.
How can I "overlay" these two images together? Or otherwise combine them in my picture box?
Thank you!
Try this:
for (var x = 0; x < sourceBitmap.Width; x++)
{
for (var y = 0; y < sourceBitmap.Height; y++)
{
var pixelColor = sourceBitmap.GetPixel(x, y);
// copy all non-transparent pixels
if (pixelColor.A != Byte.MaxValue)
{
destinationBitmap.SetPixel(x, y, pixelColor);
}
}
}
I'm assuming both sourceBitmap and destinationBitmap are System.Drawing.Bitmap objects. Source bitmap would be the one you're displaying in the picture box, but don't use the one that is returned by pictureBox.Image property, create a copy with Clone() method and then do a swap, or you'll get an exception.
If this method is too slow, you can try using direct memory access to manipulate bitmap data, using LockBits and UnlockBits methods on Bitmap objects.

Out of memory with multi images in one picturebox

I have a problem with out of memory when I'm trying load a few images into one picturebox.
public void button2_Click(object sender, EventArgs e)
{
FolderBrowserDialog dialog = new FolderBrowserDialog();
dialog.ShowDialog();
string selected = dialog.SelectedPath;
string[] imageFileList = Directory.GetFiles(selected);
int iCtr = 0,zCtr = 0;
foreach(string imageFile in imageFileList)
{
if (Image.FromFile(imageFile) != null)
{
Image.FromFile(imageFile).Dispose();
}
PictureBox eachPictureBox = new PictureBox();
eachPictureBox.Size = new Size(100,100);
// if (iCtr % 8 == 0)
//{
// zCtr++;
// iCtr = 0;
//}
eachPictureBox.Location = new Point(iCtr * 100 + 1, 1);
eachPictureBox.Image = Image.FromFile(imageFile);
iCtr++;
panel1.Controls.Add(eachPictureBox);
}
}`enter code here`
if (Image.FromFile(imageFile) != null)
{
Image.FromFile(imageFile).Dispose();
}
Bad. You're loading the image from the file, checking to see if the result is null...then loading it again into a new result so that you can dispose it. While the latter portion is silly, it isn't harmful. The first portion is, however, as the resulting Image is never properly disposed of (if/when the GC collects it, the finalizer on the Image type should dispose of the unmanaged resources, but this is not a wise thing to rely on).
Incidentally, Image.FromFile will never return null. If it cannot read the image, then it will throw an OutOfMemoryException.
The code also appears to do nothing, since there's no else block and nothing meaningful is done in the if block.
My guess is that your OutOfMemoryException is coming from the fact that one or more of the files in that directory is stored in a corrupted or unsupported image format, or isn't an image at all.
Try replacing the code in your foreach with this:
try
{
Image image = Image.FromFile(imageFile);
PictureBox eachPictureBox = new PictureBox();
eachPictureBox.Size = new Size(100,100);
eachPictureBox.Location = new Point(iCtr * 100 + 1, 1);
eachPictureBox.Image = Image.FromFile(imageFile);
iCtr++;
panel1.Controls.Add(eachPictureBox);
}
catch(OutOfMemoryException) { } // skip the file
The picture box internally holds a reference to the bitmap that you place in it. Unless you get rid of the picture box, it's holding a reference to every bitmap you load into it.
Something you have to consider that regardless of the type of picture stored on disk, when you open it for display the picture will become a bitmap and require 4 bytes per displayed pixel.
Your code seems to suggest an attempt at a thumbnail operation. You are in fact loading 70 files into memory and regardless of the display size, in memory they will be very large.
For example let's say you have 70 jpegs at 32bit color depth and say 1920x1080 pixels in size. Your memory requirement to load that many images all at once then is:
70 pics x 1920 pixels x 1080 pixels x 4 bytes/pixel = 580,608,000 bytes!
And that's a fairly low estimate.
You could consider loading many fewer pictures or trying for a real thumbnailing solution.

Categories