I have a ttf-file. How do I draw a "J" using the ttf-files font and put it into a picturebox of 100 pixels height, cutting it out, so the top of the J hits the top of the picturebox and the bottom of the J hits the bottom of the picturebox? So the J is exactly 100 pixels tall, see this:
So in summary:
Draw a "J" using the ttf-files font
Cut it out, so from top to bottom of the J it is 100 pixels.
Put it into a picturebox of 100 pixels height
This is my approach, which works but is not performant at all:
void gv()
{
Bitmap bitmap = new Bitmap(1000,1000);
Rectangle re = new Rectangle();
using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap))
{
graphics.Clear(Color.White);
Font font1 = new Font("Arial", 600, FontStyle.Regular, GraphicsUnit.Pixel);
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixel;
graphics.DrawString(was, font1, Brushes.Black, 0, 0);
Color j = Color.FromArgb(0,0,0);
for (int y = 1; y < 999; y++)
{
for (int x = 1; x < 999; x++)
{
j = bitmap.GetPixel(x, y);
if (j.R == 0)
{
//MessageBox.Show("Oben y: "+y);
re.Y = y;
break;
}
}
if (j.R == 0)
break;
}
for (int y = 999; y > 1; y--)
{
for (int x = 999; x > 1; x--)
{
j = bitmap.GetPixel(x, y);
if (j.R == 0)
{
//MessageBox.Show("Unten y: " + y);
re.Height = y - re.Y;
break;
}
}
if (j.R == 0)
break;
}
for (int x = 1; x < 999; x++)
{
for (int y = 1; y < 999; y++)
{
j = bitmap.GetPixel(x, y);
if (j.R == 0)
{
//MessageBox.Show("Links x: " + x);
re.X = x;
break;
}
}
if (j.R == 0)
break;
}
for (int x = 999; x > 1; x--)
{
for (int y = 999; y > 1; y--)
{
j = bitmap.GetPixel(x, y);
if (j.R == 0)
{
//MessageBox.Show("Rechts x: " + x);
re.Width = x - re.X;
break;
}
}
if (j.R == 0)
break;
}
//graphics.DrawRectangle(new Pen(Brushes.Black), re);
}
pictureBox1.Image = ResizeImage(CropBitmap(bitmap, re.X, re.Y, re.Width, re.Height),85,100);
}
public static Bitmap ResizeImage(Image image, int width, int height)
{
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
public Bitmap CropBitmap(Bitmap bitmap, int cropX, int cropY, int cropWidth, int cropHeight)
{
Rectangle rect = new Rectangle(cropX, cropY, cropWidth, cropHeight);
Bitmap cropped = bitmap.Clone(rect, bitmap.PixelFormat);
return cropped;
}
Related
public void ReadSetPixels(Bitmap image1, Bitmap image2)
{
int tolerance = 64;
for (int x = 0; x < image1.Width; x++)
{
for (int y = 0; y < image1.Height; y++)
{
Color pixelColor = image1.GetPixel(x, y);
// just average R, G, and B values to get gray. Then invert by 255.
int invertedGrayValue = 255 - (int)((pixelColor.R + pixelColor.G + pixelColor.B) / 3);
if (invertedGrayValue > tolerance) { invertedGrayValue = 255; }
// this keeps the original pixel color but sets the alpha value
image1.SetPixel(x, y, Color.FromArgb(invertedGrayValue, pixelColor));
}
}
// composite image1 on top of image2
using (Graphics g = Graphics.FromImage(image2))
{
g.CompositingMode = CompositingMode.SourceOver;
g.CompositingQuality = CompositingQuality.HighQuality;
g.DrawImage(image1, new Point(0, 0));
}
image2.Save(#"d:\mynewbmp.bmp");
image1.Dispose();
image2.Dispose();
}
using it
RadarPixels rp = new RadarPixels();
rp.ReadSetPixels(new Bitmap(#"d:\myimage1.png"),
new Bitmap(#"d:\clean_radar_image123.bmp"));
I looked on the example in the docs at : https://learn.microsoft.com/en-us/dotnet/api/system.drawing.bitmap.lockbits?redirectedfrom=MSDN&view=dotnet-plat-ext-7.0#overloads
but not sure how to implement it with my method.
UPDATE :
This is what I have tried so far :
created a new method :
public unsafe void Test(Bitmap Image1, Bitmap Image2)
{
int tolerance = 64;
int width = Image1.Width;
int height = Image1.Height;
//TODO determine bytes per pixel
int bytesPerPixel = 4; // we assume that image is Format32bppArgb
int maxPointerLenght = width * height * bytesPerPixel;
int stride = width * bytesPerPixel;
byte R, G, B, A;
BitmapData bData = Image1.LockBits(
new System.Drawing.Rectangle(0, 0, Image1.Width, Image1.Height),
ImageLockMode.ReadWrite, Image1.PixelFormat);
byte* scan0 = (byte*)bData.Scan0.ToPointer();
IntPtr ptr = bData.Scan0;
int bytes = Math.Abs(bData.Stride) * Image1.Height;
byte[] rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
for (int i = 0; i < maxPointerLenght; i += 4)
{
B = scan0[i + 0];
G = scan0[i + 1];
R = scan0[i + 2];
A = scan0[i + 3];
int invertedGrayValue = 255 - (int)((R + G + B) / 3);
if (invertedGrayValue > tolerance) { invertedGrayValue = 255; }
rgbValues[i] = (byte)invertedGrayValue;
}
Image1.UnlockBits(bData);
using (Graphics g = Graphics.FromImage(Image2))
{
g.CompositingMode = CompositingMode.SourceOver;
g.CompositingQuality = CompositingQuality.HighQuality;
g.DrawImage(Image1, new Point(0, 0));
}
Image2.Save(#"d:\mynewbmp.bmp");
Image2.Dispose();
}
but the result image is not as before with the method at the top with the get/set pixel. why it's not making the overlay like the method before ?
the result image :
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;
}
}
Suppose I have a System.Drawing.Bitmap in 32bpp ARGB mode. It's a large bitmap, but it's mostly fully transparent pixels with a relatively small image somewhere in the middle.
What is a fast algorithm to detect the borders of the "real" image, so I can crop away all the transparent pixels from around it?
Alternatively, is there a function already in .Net that I can use for this?
The basic idea is to check every pixel of the image to find the top, left, right and bottom bounds of the image. To do this efficiently, don't use the GetPixel method, which is pretty slow. Use LockBits instead.
Here's the implementation I came up with:
static Bitmap TrimBitmap(Bitmap source)
{
Rectangle srcRect = default(Rectangle);
BitmapData data = null;
try
{
data = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] buffer = new byte[data.Height * data.Stride];
Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);
int xMin = int.MaxValue;
int xMax = 0;
int yMin = int.MaxValue;
int yMax = 0;
for (int y = 0; y < data.Height; y++)
{
for (int x = 0; x < data.Width; x++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
if (x < xMin) xMin = x;
if (x > xMax) xMax = x;
if (y < yMin) yMin = y;
if (y > yMax) yMax = y;
}
}
}
if (xMax < xMin || yMax < yMin)
{
// Image is empty...
return null;
}
srcRect = Rectangle.FromLTRB(xMin, yMin, xMax, yMax);
}
finally
{
if (data != null)
source.UnlockBits(data);
}
Bitmap dest = new Bitmap(srcRect.Width, srcRect.Height);
Rectangle destRect = new Rectangle(0, 0, srcRect.Width, srcRect.Height);
using (Graphics graphics = Graphics.FromImage(dest))
{
graphics.DrawImage(source, destRect, srcRect, GraphicsUnit.Pixel);
}
return dest;
}
It can probably be optimized, but I'm not a GDI+ expert, so it's the best I can do without further research...
EDIT: actually, there's a simple way to optimize it, by not scanning some parts of the image :
scan left to right until you find a non-transparent pixel; store (x, y) into (xMin, yMin)
scan top to bottom until you find a non-transparent pixel (only for x >= xMin); store y into yMin
scan right to left until you find a non-transparent pixel (only for y >= yMin); store x into xMax
scan bottom to top until you find a non-transparent pixel (only for xMin <= x <= xMax); store y into yMax
EDIT2: here's an implementation of the approach above:
static Bitmap TrimBitmap(Bitmap source)
{
Rectangle srcRect = default(Rectangle);
BitmapData data = null;
try
{
data = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] buffer = new byte[data.Height * data.Stride];
Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);
int xMin = int.MaxValue,
xMax = int.MinValue,
yMin = int.MaxValue,
yMax = int.MinValue;
bool foundPixel = false;
// Find xMin
for (int x = 0; x < data.Width; x++)
{
bool stop = false;
for (int y = 0; y < data.Height; y++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
xMin = x;
stop = true;
foundPixel = true;
break;
}
}
if (stop)
break;
}
// Image is empty...
if (!foundPixel)
return null;
// Find yMin
for (int y = 0; y < data.Height; y++)
{
bool stop = false;
for (int x = xMin; x < data.Width; x++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
yMin = y;
stop = true;
break;
}
}
if (stop)
break;
}
// Find xMax
for (int x = data.Width - 1; x >= xMin; x--)
{
bool stop = false;
for (int y = yMin; y < data.Height; y++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
xMax = x;
stop = true;
break;
}
}
if (stop)
break;
}
// Find yMax
for (int y = data.Height - 1; y >= yMin; y--)
{
bool stop = false;
for (int x = xMin; x <= xMax; x++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
yMax = y;
stop = true;
break;
}
}
if (stop)
break;
}
srcRect = Rectangle.FromLTRB(xMin, yMin, xMax, yMax);
}
finally
{
if (data != null)
source.UnlockBits(data);
}
Bitmap dest = new Bitmap(srcRect.Width, srcRect.Height);
Rectangle destRect = new Rectangle(0, 0, srcRect.Width, srcRect.Height);
using (Graphics graphics = Graphics.FromImage(dest))
{
graphics.DrawImage(source, destRect, srcRect, GraphicsUnit.Pixel);
}
return dest;
}
There won't be a significant gain if the non-transparent part is small of course, since it will still scan most of the pixels. But if it's big, only the rectangles around the non-transparent part will be scanned.
I would like to suggest a divide & conquer approach:
split the image in the middle (e.g. vertically)
check if there are non-transparent pixels on the cut line (if so, remember min/max for bounding box)
split left half again vertically
if cut line contains non-transparent pixels -> update bounding box
if not, you can probably discard the leftmost half (I don't know the pictures)
continue with the left-right half (you stated that the image is somewhere in the middle) until you find the leftmost bound of the image
do the same for the right half
My Code:
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
ngr.Dispose();
List<Image> list = new List<Image>();
Graphics g = Graphics.FromImage(pictureBox1.Image);
Brush redBrush = new SolidBrush(Color.Red);
Pen pen = new Pen(redBrush,3);
MessageBox.Show(pictureBox1.Image.Width + " " + pictureBox1.Image.Height);
for (int i = 0; i < pictureBox1.Image.Width; i = (pictureBox1.Image.Width / 3) + i)
{
for (int y = 0; y < pictureBox1.Image.Height; y = (pictureBox1.Image.Height / 3) + y)
{
Rectangle r = new Rectangle(i, y, pictureBox1.Image.Width / 3, pictureBox1.Image.Height / 3);
g.DrawRectangle(pen,r );
if (i > 0 && y > 0)
{
if (i + r.Width < pictureBox1.Image.Width && y + r.Height < pictureBox1.Image.Height)
{
list.Add(cropImage(pictureBox1.Image, r));
}
}
}
}
g.Dispose();
pictureBox1.Invalidate();
pictureBox1.Image = list[0];
}
private static Image cropImage(Image img, Rectangle cropArea)
{
Bitmap bmpImage = new Bitmap(img);
Bitmap bmpCrop = bmpImage.Clone(cropArea, System.Drawing.Imaging.PixelFormat.DontCare);
return (Image)(bmpCrop);
}
This Code adds only 2 pieces to the list but not the other 7 pieces.
Please Help!!
Change this part of code and try again:
for (int i = 0; i < 3; i++)
{
for (int y = 0; y < 3; y++)
{
Rectangle r = new Rectangle(i*(pictureBox1.Image.Width / 3),
y*(pictureBox1.Image.Height / 3),
pictureBox1.Image.Width / 3,
pictureBox1.Image.Height / 3);
g.DrawRectangle(pen,r );
list.Add(cropImage(pictureBox1.Image, r));
}
}
I'm looking for sample .NET code (System.Drawing.Image) that does the following:
Load a given image file.
Generate a new single image that repeats the orginal image for x times horizontally.
This creates a new bitmap and draws the source bitmap to it numTimes times.
Bitmap b = Bitmap.FromFile(sourceFilename);
Bitmap output = new Bitmap(b.Width * numTimes, b.Height);
Graphics g = Graphics.FromImage(output);
for (int i = 0; i < numTimes; i++) {
g.DrawImage(b, i * b.Width, 0);
}
// do whatever with the image, here we'll output it
output.Save(outputFilename);
// make sure to clean up too
g.Dispose();
b.Dispose();
output.Dispose();
Here a sample copying each source image pixel on destination bitmap
static void Main(string[] args)
{
ushort nbCopies = 2;
Bitmap srcBitmap = (Bitmap)Image.FromFile(#"C:\Users\Public\Pictures\Sample Pictures\Koala.jpg");
Bitmap dstBitmap = new Bitmap(srcBitmap.Width * nbCopies, srcBitmap.Height, srcBitmap.PixelFormat);
//Slow method
for (int curCopy = 0; curCopy < nbCopies; curCopy++)
{
for (int x = 0; x < srcBitmap.Width; x++)
{
for (int y = 0; y < srcBitmap.Height; y++)
{
Color c = srcBitmap.GetPixel(x, y);
dstBitmap.SetPixel(x + (curCopy * srcBitmap.Width), y, c);
}
}
}
//OR
//Fast method using unsafe code
BitmapData srcBd = srcBitmap.LockBits(new Rectangle(Point.Empty, srcBitmap.Size), ImageLockMode.ReadOnly, srcBitmap.PixelFormat);
BitmapData dstBd = dstBitmap.LockBits(new Rectangle(Point.Empty, dstBitmap.Size), ImageLockMode.WriteOnly, dstBitmap.PixelFormat);
unsafe
{
for (int curCopy = 0; curCopy < nbCopies; curCopy++)
{
for (int y = 0; y < srcBitmap.Height; y++)
{
byte* srcRow = (byte*)srcBd.Scan0 + (y * srcBd.Stride);
byte* dstRow = (byte*)dstBd.Scan0 + (y * dstBd.Stride) + (curCopy * srcBd.Stride);
for (int x = 0; x < srcBitmap.Width; x++)
{
//Copy each composant value (typically RGB)
for (int comp = 0; comp < (srcBd.Stride / srcBd.Width); comp++)
{
dstRow[x * 3 + comp] = srcRow[x * 3 + comp];
}
}
}
}
}
dstBitmap.UnlockBits(dstBd);
srcBitmap.UnlockBits(srcBd);
dstBitmap.Save(#"C:\Users\Public\Pictures\Sample Pictures\Koala_multiple.jpg");
dstBitmap.Dispose();
srcBitmap.Dispose();
}