I set the points and when the points of the same color form a square, I draw a polygon. But when a new square is formed, the old one disappears.
can you tell me how to make sure that when drawing a new polygon, the old one does not disappear?
in the checkpoint() function, I check whether there is a square of points of the same color and return e coordinates for drawing.
public partial class Form1 : Form
{
private Class1 Class1 = new Class1();
private CellState currentPlayer = CellState.Red;
public const int SIZE = 11;
public const int Icon_Size = 30;
public Form1()
{
InitializeComponent();
}
//ставит точки
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
var p = new Point((int)Math.Round(1f * e.X / Icon_Size), (int)Math.Round(1f * e.Y / Icon_Size));
if (Class1[p] == CellState.Empty)
{
Class1.SetPoint(p, currentPlayer);
currentPlayer = Class1.Inverse(currentPlayer);
Invalidate();
}
}
//рисуем
private void OnPaint(object sender, PaintEventArgs e)
{
e.Graphics.ScaleTransform(Icon_Size, Icon_Size);
//рисуем сеточку
using (var pen = new Pen(Color.Gainsboro, 0.1f))
{
for (int x = 1; x < SIZE; x++)
e.Graphics.DrawLine(pen, x, 1, x, SIZE - 1);
for (int y = 1; y < SIZE; y++)
e.Graphics.DrawLine(pen, 1, y, SIZE - 1, y);
}
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//рисуем точки
using (var brush = new SolidBrush(Color.White))
for (int x = 1; x < Form1.SIZE; x++)
for (int y = 1; y < Form1.SIZE; y++)
{
var p = new Point(x, y);
var cell = Class1[p];
if (cell != CellState.Empty)
{
brush.Color = StateToColor(cell);
e.Graphics.FillEllipse(brush, x - 0.2f, y - 0.2f, 0.4f, 0.4f);
}
}
using (var PenP = new Pen(Color.Black, 0.1f))
using (var brush = new SolidBrush(Color.White))
{
Class1.CheckPoint();
int i = Class1.CheckPoint()[0];
int j = Class1.CheckPoint()[1];
int cp = Class1.CheckPoint()[2];
if (cp == 1)
{
PenP.Color = Color.Red;
brush.Color = Color.IndianRed;
Point[] a = { new Point(i, j), new Point(i + 1, j), new Point(i + 1, j + 1), new Point(i, j + 1) };
e.Graphics.FillPolygon(brush, a);
e.Graphics.DrawPolygon(PenP, a);
}
if (cp == 2)
{
PenP.Color = Color.Blue;
brush.Color = Color.RoyalBlue;
Point[] a = { new Point(i, j), new Point(i + 1, j), new Point(i + 1, j + 1), new Point(i, j + 1) };
e.Graphics.FillPolygon(brush, a);
e.Graphics.DrawPolygon(PenP, a);
}
}
}
//условие смены цвета под ход игрока
Color StateToColor(CellState state, byte alpha = 255)
{
var res = state == CellState.Blue ? Color.Blue : Color.Red;
return Color.FromArgb(alpha, res);
}
}
I am trying to detect light from 2 LED lights (red and blue) I did that using Bernsen thresholding technique. However, I applied that to an image. Now I want to apply that same technique but to a live video from my webcam. Is there anyway I could simply edit the code for this technique on the image to make it work on a video from the webcam? I will add below the code I used for this thresholding technique.
private ArrayList getNeighbours(int xPos, int yPos, Bitmap bitmap)
{
//This goes around the image in windows of 5
ArrayList neighboursList = new ArrayList();
int xStart, yStart, xFinish, yFinish;
int pixel;
xStart = xPos - 5;
yStart = yPos - 5;
xFinish = xPos + 5;
yFinish = yPos + 5;
for (int y = yStart; y <= yFinish; y++)
{
for (int x = xStart; x <= xFinish; x++)
{
if (x < 0 || y < 0 || x > (bitmap.Width - 1) || y > (bitmap.Height - 1))
{
continue;
}
else
{
pixel = bitmap.GetPixel(x, y).R;
neighboursList.Add(pixel);
}
}
}
return neighboursList;
}
private void button5_Click_1(object sender, EventArgs e)
{
//The input image
Bitmap image = new Bitmap(pictureBox2.Image);
progressBar1.Minimum = 0;
progressBar1.Maximum = image.Height - 1;
progressBar1.Value = 0;
Bitmap result = new Bitmap(pictureBox2.Image);
int iMin, iMax, t, c, contrastThreshold, pixel;
contrastThreshold = 180;
ArrayList list = new ArrayList();
for (int y = 0; y < image.Height; y++)
{
for (int x = 0; x < image.Width; x++)
{
list.Clear();
pixel = image.GetPixel(x, y).R;
list = getNeighbours(x, y, image);
list.Sort();
iMin = Convert.ToByte(list[0]);
iMax = Convert.ToByte(list[list.Count - 1]);
// These are the calculations to test whether the
current pixel is light or dark
t = ((iMax + iMin) / 2);
c = (iMax - iMin);
if (c < contrastThreshold)
{
pixel = ((t >= 160) ? 0 : 255);
}
else
{
pixel = ((pixel >= t) ? 0 : 255);
}
result.SetPixel(x, y, Color.FromArgb(pixel, pixel, pixel));
}
progressBar1.Value = y;
}
pictureBox3.Image =result;
}
I have just wrote this method to crop transparent pixels from images.
It seems to work ok but it is very slow because of GetPixel - any ideas on how to make the algorithm logic quicker?
I know I can change the GetPixel for faster (but unsafe) access code and I might do so, however I am after ways to avoid doing a full scan. I want advice on how to make the logic behind this algorithm quicker.
public Bitmap CropTransparentPixels(Bitmap originalBitmap)
{
// Find the min/max transparent pixels
Point min = new Point(int.MaxValue, int.MaxValue);
Point max = new Point(int.MinValue, int.MinValue);
for (int x = 0; x < originalBitmap.Width; ++x)
{
for (int y = 0; y < originalBitmap.Height; ++y)
{
Color pixelColor = originalBitmap.GetPixel(x, y);
if (pixelColor.A == 255)
{
if (x < min.X) min.X = x;
if (y < min.Y) min.Y = y;
if (x > max.X) max.X = x;
if (y > max.Y) max.Y = y;
}
}
}
// Create a new bitmap from the crop rectangle
Rectangle cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X, max.Y - min.Y);
Bitmap newBitmap = new Bitmap(cropRectangle.Width, cropRectangle.Height);
using (Graphics g = Graphics.FromImage(newBitmap))
{
g.DrawImage(originalBitmap, 0, 0, cropRectangle, GraphicsUnit.Pixel);
}
return newBitmap;
}
This is the method I ended up writing and it is much faster.
public static Bitmap CropTransparentPixels(this Bitmap bmp)
{
BitmapData bmData = null;
try
{
bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int scanline = bmData.Stride;
IntPtr Scan0 = bmData.Scan0;
Point top = new Point(), left = new Point(), right = new Point(), bottom = new Point();
bool complete = false;
unsafe
{
byte* p = (byte*)(void*)Scan0;
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x++)
{
if (p[3] != 0)
{
top = new Point(x, y);
complete = true;
break;
}
p += 4;
}
if (complete)
break;
}
p = (byte*)(void*)Scan0;
complete = false;
for (int y = bmp.Height - 1; y >= 0; y--)
{
for (int x = 0; x < bmp.Width; x++)
{
if (p[x * 4 + y * scanline + 3] != 0)
{
bottom = new Point(x + 1, y + 1);
complete = true;
break;
}
}
if (complete)
break;
}
p = (byte*)(void*)Scan0;
complete = false;
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
if (p[x * 4 + y * scanline + 3] != 0)
{
left = new Point(x, y);
complete = true;
break;
}
}
if (complete)
break;
}
p = (byte*)(void*)Scan0;
complete = false;
for (int x = bmp.Width - 1; x >= 0; x--)
{
for (int y = 0; y < bmp.Height; y++)
{
if (p[x * 4 + y * scanline + 3] != 0)
{
right = new Point(x + 1, y + 1);
complete = true;
break;
}
}
if (complete)
break;
}
}
bmp.UnlockBits(bmData);
System.Drawing.Rectangle rectangle = new Rectangle(left.X, top.Y, right.X - left.X, bottom.Y - top.Y);
Bitmap b = new Bitmap(rectangle.Width, rectangle.Height);
Graphics g = Graphics.FromImage(b);
g.DrawImage(bmp, 0, 0, rectangle, GraphicsUnit.Pixel);
g.Dispose();
return b;
}
catch
{
try
{
bmp.UnlockBits(bmData);
}
catch { }
return null;
}
}
I have a block of product images we received from a customer. Each product image is a picture of something and it was taken with a white background. I would like to crop all the surrounding parts of the image but leave only the product in the middle. Is this possible?
As an example: [http://www.5dnet.de/media/catalog/product/d/r/dress_shoes_5.jpg][1]
I don't want all white pixels removed, however I do want the image cropped so that the top-most row of pixels contains one non-white pixel, the left-most vertical row of pixels contains one non-white pixel, bottom-most horizontal row of pixels contains one non-white pixel, etc.
Code in C# or VB.net would be appreciated.
I found I had to adjust Dmitri's answer to ensure it works with images that don't actually need cropping (either horizontally, vertically or both)...
public static Bitmap Crop(Bitmap bmp)
{
int w = bmp.Width;
int h = bmp.Height;
Func<int, bool> allWhiteRow = row =>
{
for (int i = 0; i < w; ++i)
if (bmp.GetPixel(i, row).R != 255)
return false;
return true;
};
Func<int, bool> allWhiteColumn = col =>
{
for (int i = 0; i < h; ++i)
if (bmp.GetPixel(col, i).R != 255)
return false;
return true;
};
int topmost = 0;
for (int row = 0; row < h; ++row)
{
if (allWhiteRow(row))
topmost = row;
else break;
}
int bottommost = 0;
for (int row = h - 1; row >= 0; --row)
{
if (allWhiteRow(row))
bottommost = row;
else break;
}
int leftmost = 0, rightmost = 0;
for (int col = 0; col < w; ++col)
{
if (allWhiteColumn(col))
leftmost = col;
else
break;
}
for (int col = w - 1; col >= 0; --col)
{
if (allWhiteColumn(col))
rightmost = col;
else
break;
}
if (rightmost == 0) rightmost = w; // As reached left
if (bottommost == 0) bottommost = h; // As reached top.
int croppedWidth = rightmost - leftmost;
int croppedHeight = bottommost - topmost;
if (croppedWidth == 0) // No border on left or right
{
leftmost = 0;
croppedWidth = w;
}
if (croppedHeight == 0) // No border on top or bottom
{
topmost = 0;
croppedHeight = h;
}
try
{
var target = new Bitmap(croppedWidth, croppedHeight);
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(bmp,
new RectangleF(0, 0, croppedWidth, croppedHeight),
new RectangleF(leftmost, topmost, croppedWidth, croppedHeight),
GraphicsUnit.Pixel);
}
return target;
}
catch (Exception ex)
{
throw new Exception(
string.Format("Values are topmost={0} btm={1} left={2} right={3} croppedWidth={4} croppedHeight={5}", topmost, bottommost, leftmost, rightmost, croppedWidth, croppedHeight),
ex);
}
}
Here's my (rather lengthy) solution:
public Bitmap Crop(Bitmap bmp)
{
int w = bmp.Width, h = bmp.Height;
Func<int, bool> allWhiteRow = row =>
{
for (int i = 0; i < w; ++i)
if (bmp.GetPixel(i, row).R != 255)
return false;
return true;
};
Func<int, bool> allWhiteColumn = col =>
{
for (int i = 0; i < h; ++i)
if (bmp.GetPixel(col, i).R != 255)
return false;
return true;
};
int topmost = 0;
for (int row = 0; row < h; ++row)
{
if (allWhiteRow(row))
topmost = row;
else break;
}
int bottommost = 0;
for (int row = h - 1; row >= 0; --row)
{
if (allWhiteRow(row))
bottommost = row;
else break;
}
int leftmost = 0, rightmost = 0;
for (int col = 0; col < w; ++col)
{
if (allWhiteColumn(col))
leftmost = col;
else
break;
}
for (int col = w-1; col >= 0; --col)
{
if (allWhiteColumn(col))
rightmost = col;
else
break;
}
int croppedWidth = rightmost - leftmost;
int croppedHeight = bottommost - topmost;
try
{
Bitmap target = new Bitmap(croppedWidth, croppedHeight);
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(bmp,
new RectangleF(0, 0, croppedWidth, croppedHeight),
new RectangleF(leftmost, topmost, croppedWidth, croppedHeight),
GraphicsUnit.Pixel);
}
return target;
}
catch (Exception ex)
{
throw new Exception(
string.Format("Values are topmost={0} btm={1} left={2} right={3}", topmost, bottommost, leftmost, rightmost),
ex);
}
}
I needed a solution that worked on large images (GetPixel is slow), so I wrote the extension method below. It seems to work well in my limited testing. The drawback is that "Allow Unsafe Code" has to be checked in your project.
public static Image AutoCrop(this Bitmap bmp)
{
if (Image.GetPixelFormatSize(bmp.PixelFormat) != 32)
throw new InvalidOperationException("Autocrop currently only supports 32 bits per pixel images.");
// Initialize variables
var cropColor = Color.White;
var bottom = 0;
var left = bmp.Width; // Set the left crop point to the width so that the logic below will set the left value to the first non crop color pixel it comes across.
var right = 0;
var top = bmp.Height; // Set the top crop point to the height so that the logic below will set the top value to the first non crop color pixel it comes across.
var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
unsafe
{
var dataPtr = (byte*)bmpData.Scan0;
for (var y = 0; y < bmp.Height; y++)
{
for (var x = 0; x < bmp.Width; x++)
{
var rgbPtr = dataPtr + (x * 4);
var b = rgbPtr[0];
var g = rgbPtr[1];
var r = rgbPtr[2];
var a = rgbPtr[3];
// If any of the pixel RGBA values don't match and the crop color is not transparent, or if the crop color is transparent and the pixel A value is not transparent
if ((cropColor.A > 0 && (b != cropColor.B || g != cropColor.G || r != cropColor.R || a != cropColor.A)) || (cropColor.A == 0 && a != 0))
{
if (x < left)
left = x;
if (x >= right)
right = x + 1;
if (y < top)
top = y;
if (y >= bottom)
bottom = y + 1;
}
}
dataPtr += bmpData.Stride;
}
}
bmp.UnlockBits(bmpData);
if (left < right && top < bottom)
return bmp.Clone(new Rectangle(left, top, right - left, bottom - top), bmp.PixelFormat);
return null; // Entire image should be cropped, so just return null
}
I've written code to do this myself - it's not too difficult to get the basics going.
Essentially, you need to scan pixel rows/columns to check for non-white pixels and isolate the bounds of the product image, then create a new bitmap with just that region.
Note that while the Bitmap.GetPixel() method works, it's relatively slow. If processing time is important, you'll need to use Bitmap.LockBits() to lock the bitmap in memory, and then some simple pointer use inside an unsafe { } block to access the pixels directly.
This article on CodeProject gives some more details that you'll probably find useful.
fix remaining 1px white space at the top and left
public Bitmap Crop(Bitmap bitmap)
{
int w = bitmap.Width;
int h = bitmap.Height;
Func<int, bool> IsAllWhiteRow = row =>
{
for (int i = 0; i < w; i++)
{
if (bitmap.GetPixel(i, row).R != 255)
{
return false;
}
}
return true;
};
Func<int, bool> IsAllWhiteColumn = col =>
{
for (int i = 0; i < h; i++)
{
if (bitmap.GetPixel(col, i).R != 255)
{
return false;
}
}
return true;
};
int leftMost = 0;
for (int col = 0; col < w; col++)
{
if (IsAllWhiteColumn(col)) leftMost = col + 1;
else break;
}
int rightMost = w - 1;
for (int col = rightMost; col > 0; col--)
{
if (IsAllWhiteColumn(col)) rightMost = col - 1;
else break;
}
int topMost = 0;
for (int row = 0; row < h; row++)
{
if (IsAllWhiteRow(row)) topMost = row + 1;
else break;
}
int bottomMost = h - 1;
for (int row = bottomMost; row > 0; row--)
{
if (IsAllWhiteRow(row)) bottomMost = row - 1;
else break;
}
if (rightMost == 0 && bottomMost == 0 && leftMost == w && topMost == h)
{
return bitmap;
}
int croppedWidth = rightMost - leftMost + 1;
int croppedHeight = bottomMost - topMost + 1;
try
{
Bitmap target = new Bitmap(croppedWidth, croppedHeight);
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(bitmap,
new RectangleF(0, 0, croppedWidth, croppedHeight),
new RectangleF(leftMost, topMost, croppedWidth, croppedHeight),
GraphicsUnit.Pixel);
}
return target;
}
catch (Exception ex)
{
throw new Exception(string.Format("Values are top={0} bottom={1} left={2} right={3}", topMost, bottomMost, leftMost, rightMost), ex);
}
}
It's certainly possible. In pseudocode:
topmost = 0
for row from 0 to numRows:
if allWhiteRow(row):
topmost = row
else:
# found first non-white row from top
break
botmost = 0
for row from numRows-1 to 0:
if allWhiteRow(row):
botmost = row
else:
# found first non-white row from bottom
break
And similarly for left and right.
The code for allWhiteRow would involve looking at the pixels in that row and making sure they're all close to 255,255,255.
public void TrimImage() {
int threshhold = 250;
int topOffset = 0;
int bottomOffset = 0;
int leftOffset = 0;
int rightOffset = 0;
Bitmap img = new Bitmap(#"e:\Temp\Trim_Blank_Image.png");
bool foundColor = false;
// Get left bounds to crop
for (int x = 1; x < img.Width && foundColor == false; x++)
{
for (int y = 1; y < img.Height && foundColor == false; y++)
{
Color color = img.GetPixel(x, y);
if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
foundColor = true;
}
leftOffset += 1;
}
foundColor = false;
// Get top bounds to crop
for (int y = 1; y < img.Height && foundColor == false; y++)
{
for (int x = 1; x < img.Width && foundColor == false; x++)
{
Color color = img.GetPixel(x, y);
if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
foundColor = true;
}
topOffset += 1;
}
foundColor = false;
// Get right bounds to crop
for (int x = img.Width - 1; x >= 1 && foundColor == false; x--)
{
for (int y = 1; y < img.Height && foundColor == false; y++)
{
Color color = img.GetPixel(x, y);
if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
foundColor = true;
}
rightOffset += 1;
}
foundColor = false;
// Get bottom bounds to crop
for (int y = img.Height - 1; y >= 1 && foundColor == false; y--)
{
for (int x = 1; x < img.Width && foundColor == false; x++)
{
Color color = img.GetPixel(x, y);
if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
foundColor = true;
}
bottomOffset += 1;
}
// Create a new image set to the size of the original minus the white space
//Bitmap newImg = new Bitmap(img.Width - leftOffset - rightOffset, img.Height - topOffset - bottomOffset);
Bitmap croppedBitmap = new Bitmap(img);
croppedBitmap = croppedBitmap.Clone(
new Rectangle(leftOffset - 3, topOffset - 3, img.Width - leftOffset - rightOffset + 6, img.Height - topOffset - bottomOffset + 6),
System.Drawing.Imaging.PixelFormat.DontCare);
// Get a graphics object for the new bitmap, and draw the original bitmap onto it, offsetting it do remove the whitespace
//Graphics g = Graphics.FromImage(croppedBitmap);
//g.DrawImage(img, 1 - leftOffset, 1 - rightOffset);
croppedBitmap.Save(#"e:\Temp\Trim_Blank_Image-crop.png", ImageFormat.Png);
}
I have got code from other post in ms, but that has bugs, I have changed something, now it works good.
The post from http://msm2020-sc.blogspot.com/2013/07/c-crop-white-space-from-around-image.html
The pnmcrop utility from the netpbm graphics utilities library does exactly that.
I suggest looking at their code, available from http://netpbm.sourceforge.net/
#Jonesie works great, but you have a bug with AllWhiteColumn
pixel was wrong calculated var px = i * w + col; is correct.
Also isTransparent should include white color SKColors.White or better compare it using rgb with offset r,g,b >200
I copied to a version that works with SkiaSharp.
using SkiaSharp;
using System;
//
// Based on the original stackoverflow post: https://stackoverflow.com/questions/248141/remove-surrounding-whitespace-from-an-image
//
namespace BlahBlah
{
public static class BitmapExtensions
{
public static SKBitmap TrimWhitespace(this SKBitmap bmp)
{
int w = bmp.Width;
int h = bmp.Height;
// get all the pixels here - this can take a while so dont want it in the loops below
// maybe theres a more efficient way? loading all the pixels could be greedy
var pixels = bmp.Pixels;
bool IsTransparent(SKColor color)
{
return (color.Red == 0 && color.Green == 0 && color.Blue == 0 && color.Alpha == 0) ||
(color == SKColors.Transparent);
}
Func<int, bool> allWhiteRow = row =>
{
for (int i = 0; i < w; ++i)
{
var px = row * w + i;
if (!IsTransparent(pixels[px]))
return false;
}
return true;
};
Func<int, bool> allWhiteColumn = col =>
{
for (int i = 0; i < h; ++i)
{
var px = col * h + i;
if (!IsTransparent(pixels[px]))
return false;
}
return true;
};
int topmost = 0;
for (int row = 0; row < h; ++row)
{
if (allWhiteRow(row))
topmost = row;
else break;
}
int bottommost = 0;
for (int row = h - 1; row >= 0; --row)
{
if (allWhiteRow(row))
bottommost = row;
else break;
}
int leftmost = 0, rightmost = 0;
for (int col = 0; col < w; ++col)
{
if (allWhiteColumn(col))
leftmost = col;
else
break;
}
for (int col = w - 1; col >= 0; --col)
{
if (allWhiteColumn(col))
rightmost = col;
else
break;
}
if (rightmost == 0) rightmost = w; // As reached left
if (bottommost == 0) bottommost = h; // As reached top.
int croppedWidth = rightmost - leftmost;
int croppedHeight = bottommost - topmost;
if (croppedWidth == 0) // No border on left or right
{
leftmost = 0;
croppedWidth = w;
}
if (croppedHeight == 0) // No border on top or bottom
{
topmost = 0;
croppedHeight = h;
}
try
{
var target = new SKBitmap(croppedWidth, croppedHeight);
using var canvas = new SKCanvas(target);
using var img = SKImage.FromBitmap(bmp);
canvas.DrawImage(img,
new SKRect(leftmost, topmost, rightmost, bottommost),
new SKRect(0, 0, croppedWidth, croppedHeight));
return target;
}
catch (Exception ex)
{
throw new Exception(
string.Format("Values are topmost={0} btm={1} left={2} right={3} croppedWidth={4} croppedHeight={5}", topmost, bottommost, leftmost, rightmost, croppedWidth, croppedHeight),
ex);
}
}
}
}