I have a black and white System.Drawing.Bitmap I need to auto-crop it so that it is only as big as needed to fit the image. This image always starts at the top left (0,0) position but I'm not sure how much height and width is will require. If there any way to auto-crop it to size?
The following is the code I used:
// Figure out the final size
int maxX = 0;
int maxY = 0;
for (int x = 0; x < bitmap.Width; x++)
{
for (int y = 0; y < bitmap.Height; y++)
{
System.Drawing.Color c = bitmap.GetPixel(x, y);
System.Drawing.Color w = System.Drawing.Color.White;
if (c.R != w.R || c.G != w.G || c.B != w.B)
{
if (x > maxX)
maxX = x;
if (y > maxY)
maxY = y;
}
}
}
maxX += 2;
If your image does not start at 0,0 like mine, here is a variant that gets you the auto-crop bounds:
public static Rectangle GetAutoCropBounds(Bitmap bitmap)
{
int maxX = 0;
int maxY = 0;
int minX = bitmap.Width;
int minY = bitmap.Height;
for (int x = 0; x < bitmap.Width; x++)
{
for (int y = 0; y < bitmap.Height; y++)
{
var c = bitmap.GetPixel(x, y);
var w = Color.White;
if (c.R != w.R || c.G != w.G || c.B != w.B)
{
if (x > maxX)
maxX = x;
if (x < minX)
minX = x;
if (y > maxY)
maxY = y;
if (y < minY)
minY = y;
}
}
}
maxX += 2;
return new Rectangle(minX, minY, maxX - minX, maxY - minY);
}
Note: if you are processing any amount of images in quantity or high-resolution then you'll want to look into faster alternatives than the GetPixel method.
Related
The updated image:
thanks you very much man but i wanted to do somthing little different, to cut each rectangle here as a sperate image.Lets try first to find the blue block Bounds. Sounds hard but its actually simple.
look when i have done so far:
private unsafe Bitmap CodeImage(Bitmap bmp)
{
Bitmap bmpRes = new Bitmap(bmp.Width, bmp.Height);
BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
IntPtr scan0 = bmData.Scan0;
int stride = bmData.Stride;
int nWidth = bmp.Width;
int nHeight = bmp.Height;
int minX = 10000 ;
int maxX = -10000;
int minY = 10000;
var maxY = -10000;
bool found = false;
for (int y = 0; y < nHeight; y++)
{
byte* p = (byte*)scan0.ToPointer();
p += y * stride;
for (int x = 0; x < nWidth; x++)
{
if (p[3]!=0) //Check if pixel is transpert;
{
found = true;
if (x < minX)
minX = x;
if (y < minY)
minY = y;
if (x > maxX)
maxX = x;
if (y > maxY)
maxY = y;
}
else
{
if (found)
{
Rectangle temp = new Rectangle(minX, minY, maxX - minX, maxY-minY);
return bmp.Clone(temp, bmp.PixelFormat);
}
}
p += 4;
}
}
return null;
}
you actually was write and i should calculate the width like this :
int width = maxX - minX; and it actually works.. but the height is 0....
try this out man its outputing almost correct rectangle with these bounds:
(200,800,400, and 0 on the height).
i just used parts of your code in my algorithm and yea you were right but now there is a little problem with the height i' would very appreciate if you will have a look
I'll write this in pseudo-ish-code since you should be able then to apply it to any type of image or language...
var minX = 10000
var maxX = -10000
var minY = 10000
var maxY = -10000
for (var y = 0 to height)
{
for (var x = 0 to width)
{
if (pixel(x,y).Color != transparent)
{
if (x < minX) minX = x
if (y < minY) minY = y
if (x > maxX) maxX = x
if (y > maxY) maxY = y
}
}
}
var cropRectangle = (minX, minY, maxX, maxY)
You can now use standard functions on the bitmap to get that area, which should be the area bounded by the non-transparent pixels.
I'm attempting to get the bounds of the edges of interest in a binary image using the following method. Sadly my maths seems to be letting me down and I'm only getting a rectangle 2px smaller in each dimension than the original image.
Can someone show me where I have gone wrong?
Note. FastBitmap is a class that allows fast access to pixel data.
private Rectangle FindBox(Bitmap bitmap, byte indexToRemove)
{
int width = bitmap.Width;
int height = bitmap.Height;
int minX = width;
int minY = height;
int maxX = 0;
int maxY = 0;
using (FastBitmap fastBitmap = new FastBitmap(bitmap))
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (fastBitmap.GetPixel(x, y).B == indexToRemove)
{
if (x < minX)
{
minX = x;
}
if (x > maxX)
{
maxX = x;
}
if (y < minY)
{
minY = y;
}
if (y > maxY)
{
maxY = y;
}
}
}
}
}
// check
if ((minX == width) && (minY == height) && (maxX == 0) && (maxY == 0))
{
minX = minY = 0;
}
return new Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1);
}
The image I'm testing.
You appear to be checking EVERY pixel to see if a match exists in any x and y. Instead, what you want to do is check the minx, maxx, miny, and maxy separately.
For the minY, you want start at the top and check each row down until you hit a y row that has a matching pixel.
For the maxY, you want start at the bottom and check each row up until you hit a y row that has a matching pixel.
For the minX, you want start at the left and check each column until you hit a x column that has a matching pixel.
For the maxX, you want start at the right and check each column until you hit a x column that has a matching pixel.
Something like this:
minY = getMinY(fastBitmap, indexToRemove);
maxY = getMinY(fastBitmap, indexToRemove);
minX = getMinY(fastBitmap, indexToRemove);
maxX = getMinY(fastBitmap, indexToRemove);
int getMinY(Bitmap bitmap, byte indexToRemove)
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (fastBitmap.GetPixel(x, y).B == indexToRemove)
{
return y;
}
}
}
return 0;
}
int getMaxY(Bitmap bitmap, byte indexToRemove)
{
for (int y = height; y > 0; y--)
{
for (int x = 0; x < width; x++)
{
if (fastBitmap.GetPixel(x, y).B == indexToRemove)
{
return y;
}
}
}
return height;
}
etc...
You should be able to write the getMinX and getMaxY yourself.
I have the following code for looping pixels in an image:
Bitmap myBitmap = new Bitmap(pictureBox1.Image);
for (x = 0; x < pictureBox1.Image.Width; x += 1)
{
for (y = 0; y < pictureBox1.Image.Height; y += 1)
{
Color pixelColor = myBitmap.GetPixel(x, y);
aws = pixelColor.GetBrightness();
}
}
The above works. What if i want to exclude an area of the image. The area is an area selected by the user,for example:
from x=200&y=30 to x=250&y=30
from x=200&y=35 to x=250&y=35
from x=200&y=40 to x=250&y=40
from x=200&y=45 to x=250&y=45
How i loop all pixels except the one i wrote above?
Thank you!
I think you can do something like the following
Rectangle rectToExclude = new Rectangle(200, 30, 250, 30);//rectangle to be excluded
Bitmap myBitmap = new Bitmap(pictureBox1.Image);
for (x = 0; x < pictureBox1.Image.Width; x += 1)
{
for (y = 0; y < pictureBox1.Image.Height; y += 1)
{
if (rectToExclude.Contains(x, y)) continue; // if in the exclude rect then continue without doing any effects
Color pixelColor = myBitmap.GetPixel(x, y);
aws = pixelColor.GetBrightness();
}
}
Maybe write a function to test x and y like this:
public static bool IsInArea(int testx, int testy, int x1, int y1, int x2, int y2)
{
if (testx > x1 && testx < x2 && testy > y1 && testy < y2)
{
return true;
}
else
{
return false;
}
}
and then use it like this:
BitmapData bmd = myBitmap.LockBits(new Rectangle(0, 0, myBitmap.Width, myBitmap.Height),
System.Drawing.Imaging.ImageLockMode.ReadWrite,
myBitmap.PixelFormat);
int PixelSize = 4;
unsafe
{
for (int y = 0; y < bmd.Height; y++)
{
byte* row = (byte*)bmd.Scan0 + (y * bmd.Stride);
for (int x = 0; x < bmd.Width; x++)
{
if (!IsInArea(x, y, 200, 30, 250, 30))
{
int b = row[x * PixelSize]; //Blue
int g = row[x * PixelSize + 1]; //Green
int r = row[x * PixelSize + 2]; //Red
int a = row[x * PixelSize + 3]; //Alpha
Color c = Color.FromArgb(a, r, g, b);
float brightness = c.GetBrightness();
}
}
}
}
myBitmap.UnlockBits(bmd);
I'm trying to add texture coordinates to each of the vertices so that a grass texture is added to each triangle. The code I have stretches the texture across the entire area which works but doesn't scale up very well. How do I correctly add (0,0), (0,1), (1,1), etc to the vertices?
Currently they're added in the SetUpVertices() method, should they be added in the SetUpIndices() method when the code can distinguish whether it's top left, bottom left, bottom right, etc. Any help would be greatly appreciated. The relevant methods are below and the full Game1.cs code is here http://pastebin.com/REd8QDZA
private void SetUpVertices()
{
vertices = new VertexPositionNormalTexture[terrainWidth * terrainHeight];
for (int x = 0; x < terrainWidth; x++)
{
for (int y = 0; y < terrainHeight; y++)
{
vertices[x + y * terrainWidth].Position = new Vector3(x, -y, heightData[x, y]);
vertices[x + y * terrainWidth].TextureCoordinate.X = x / (terrainWidth - 1.0);
vertices[x + y * terrainWidth].TextureCoordinate.Y = y / (terrainHeight - 1.0);
}
}
}
private void SetUpIndices()
{
indices = new short[(terrainWidth - 1) * (terrainHeight - 1) * 6];
int counter = 0;
for (int y = 0; y < terrainHeight - 1; y++)
{
for (int x = 0; x < terrainWidth - 1; x++)
{
int lowerLeft = x + y * terrainWidth;
int lowerRight = (x + 1) + y * terrainWidth;
int topLeft = x + (y + 1) * terrainWidth;
int topRight = (x + 1) + (y + 1) * terrainWidth;
indices[counter++] = (short)topLeft;
indices[counter++] = (short)lowerRight;
indices[counter++] = (short)lowerLeft;
indices[counter++] = (short)topLeft;
indices[counter++] = (short)topRight;
indices[counter++] = (short)lowerRight;
}
}
}
Just specify
vertices[x + y * terrainWidth].TextureCoordinate.X = x;
vertices[x + y * terrainWidth].TextureCoordinate.Y = y;
By default, texture coordinates greater than 1 will be wrapped and the texture is repeated.
In an assignment for school do we need to do some image recognizing, where we have to find a path for a robot.
So far have we been able to find all the polygons in the image, but now we need to generate a pixel map, that be used for an astar algorithm later. We have found a way to do this, show below, but the problem is that is very slow, as we go though each pixel and test if it is inside the polygon. So my question is, are there a way that we can generate this pixel map faster?
We have a list of coordinates for the polygon
private List<IntPoint> hull;
The function "getMap" is called to get the pixel map
public Point[] getMap()
{
List<Point> points = new List<Point>();
lock (hull)
{
Rectangle rect = getRectangle();
for (int x = rect.X; x <= rect.X + rect.Width; x++)
{
for (int y = rect.Y; y <= rect.Y + rect.Height; y++)
{
if (inPoly(x, y))
points.Add(new Point(x, y));
}
}
}
return points.ToArray();
}
Get Rectangle is used to limit the search, se we don't have to go thoug the whole image
public Rectangle getRectangle()
{
int x = -1, y = -1, width = -1, height = -1;
foreach (IntPoint item in hull)
{
if (item.X < x || x == -1)
x = item.X;
if (item.Y < y || y == -1)
y = item.Y;
if (item.X > width || width == -1)
width = item.X;
if (item.Y > height || height == -1)
height = item.Y;
}
return new Rectangle(x, y, width-x, height-y);
}
And atlast this is how we check to see if a pixel is inside the polygon
public bool inPoly(int x, int y)
{
int i, j = hull.Count - 1;
bool oddNodes = false;
for (i = 0; i < hull.Count; i++)
{
if (hull[i].Y < y && hull[j].Y >= y
|| hull[j].Y < y && hull[i].Y >= y)
{
try
{
if (hull[i].X + (y - hull[i].X) / (hull[j].X - hull[i].X) * (hull[j].X - hull[i].X) < x)
{
oddNodes = !oddNodes;
}
}
catch (DivideByZeroException e)
{
if (0 < x)
{
oddNodes = !oddNodes;
}
}
}
j = i;
}
return oddNodes;
}
There are some interesting discussion here on polygon hit tests, but it sounds to me as if you might be better off with a polygon fill.
You may want to look for a Plygon Triangulation algorithm.
Also, note that catching an exception is far more time-consuming that checking the right condition. So i suggest you to convert your existing code in:
public bool inPoly(int x, int y)
{
int i, j = hull.Count - 1;
var oddNodes = false;
for (i = 0; i < hull.Count; i++)
{
if (hull[i].Y < y && hull[j].Y >= y
|| hull[j].Y < y && hull[i].Y >= y)
{
var delta = (hull[j].X - hull[i].X);
if (delta == 0)
{
if (0 < x) oddNodes = !oddNodes;
}
else if (hull[i].X + (y - hull[i].X) / delta * delta < x)
{
oddNodes = !oddNodes;
}
}
j = i;
}
return oddNodes;
}