无论图像颜色如何,在图像上绘制可见文本
Draw visible text on image regardless of image color
我想使用 DrawString() 在 24bpp 图像上打印文本。问题是如果我选择白色作为文本颜色,文本在较亮的图像区域中几乎看不见。如果我选择红色作为文本颜色,文本在包含更多红色的图像区域中几乎不可见。等等。
我要实现的是,文字在任何情况下都可见。我尝试的是用粗体绘制文本,然后使用相同的字体再次绘制文本。但是粗体字稍宽,所以这不是解决方案。
但是解决办法是什么?有吗?
谢谢!
如果图像差别不大,以至于可以假设您将在一个区域中用纯色绘制字符串,则可以使用以下解决方案。
您可以先使用算法计算出与另一种颜色最不同的颜色,如下所示:
public static byte MostDifferent (byte original) {
if(original < 0x80) {
return 0xff;
} else {
return 0x00;
}
}
public static Color MostDifferent (Color original) {
byte r = MostDifferent(original.R);
byte g = MostDifferent(original.G);
byte b = MostDifferent(original.B);
return Color.FromArgb(r,g,b);
}
现在我们已经完成了,我们必须计算 string
将被绘制的区域内的平均颜色。您可以在 Bitmap
级别上执行此操作:
public static unsafe Color AverageColor (Bitmap bmp, Rectangle r) {
BitmapData bmd = bmp.LockBits (r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int s = bmd.Stride;
int cr = 0;
int cg = 0;
int cb = 0;
int* clr = (int*)(void*)bmd.Scan0;
int tmp;
int* row = clr;
for (int i = 0; i < r.Height; i++) {
int* col = row;
for (int j = 0; j < r.Width; j++) {
tmp = *col;
cr += (tmp >> 0x10) & 0xff;
cg += (tmp >> 0x08) & 0xff;
cb += tmp & 0xff;
col++;
}
row += s>>0x02;
}
int div = r.Width * r.Height;
int d2 = div >> 0x01;
cr = (cr + d2) / div;
cg = (cg + d2) / div;
cb = (cb + d2) / div;
bmp.UnlockBits (bmd);
return Color.FromArgb (cr, cg, cb);
}
最后,算法首先测量要绘制字符串的矩形,然后确定最不同的颜色,最后用该颜色绘制字符串:
public static void DrawColorString (this Graphics g, Bitmap bmp, string text, Font font, PointF point) {
SizeF sf = g.MeasureString (text, font);
Rectangle r = new Rectangle (Point.Truncate (point), Size.Ceiling (sf));
r.Intersect (new Rectangle(0,0,bmp.Width,bmp.Height));
Color brsh = MostDifferent (AverageColor (bmp, r));
g.DrawString (text, font, new SolidBrush (brsh), point);
}
现在您可以调用该方法,例如:
Bitmap bmp = new Bitmap("Foo.png");
Graphics g = Graphics.FromImage(bmp);
g.DrawColorString (bmp, "Sky", new Font ("Arial", 72.0f), new PointF (600.0f, 150.0f));
g.DrawColorString (bmp, "Sand", new Font ("Arial", 72.0f), new PointF (600.0f, 450.0f));
bmp.Save ("result.jpg");
这个结果例如:
我建议将文本绘制两次(或三次):一次在深色中,一次在亮色中,根据字体大小,向上和向左留出 1 或 2 个像素。
如果您的字体非常小并且在许多笔划上只有 1 像素宽,那么使用三重绘图会有所帮助:两个外部的一种颜色(深色)和中间的第三个(明亮的)
Random R = new Random();
private void pictureBox2_Paint(object sender, PaintEventArgs e)
{
// using a loop for create random locations..:
for (int i=0; i< 33; i++)
{
Point pt = new Point(R.Next(pictureBox2.ClientSize.Width),
R.Next(pictureBox2.ClientSize.Width));
e.Graphics.DrawString("Hello World", Font, Brushes.Black, pt.X - 1, pt.Y - 1);
e.Graphics.DrawString("Hello World", Font, Brushes.Black, pt.X + 1, pt.Y + 1);
e.Graphics.DrawString("Hello World", Font, Brushes.White, pt.X , pt.Y);
}
}
这里有一个三重绘图的例子
:
(请注意,原版看起来比我在浏览器中看到的要清晰得多。您可能需要下载它来检查..)
如果您的图像可能包含很多噪音并且字体必须相当小,那么在文本后面添加纯色背景会更好。
我想使用 DrawString() 在 24bpp 图像上打印文本。问题是如果我选择白色作为文本颜色,文本在较亮的图像区域中几乎看不见。如果我选择红色作为文本颜色,文本在包含更多红色的图像区域中几乎不可见。等等。
我要实现的是,文字在任何情况下都可见。我尝试的是用粗体绘制文本,然后使用相同的字体再次绘制文本。但是粗体字稍宽,所以这不是解决方案。
但是解决办法是什么?有吗?
谢谢!
如果图像差别不大,以至于可以假设您将在一个区域中用纯色绘制字符串,则可以使用以下解决方案。
您可以先使用算法计算出与另一种颜色最不同的颜色,如下所示:
public static byte MostDifferent (byte original) {
if(original < 0x80) {
return 0xff;
} else {
return 0x00;
}
}
public static Color MostDifferent (Color original) {
byte r = MostDifferent(original.R);
byte g = MostDifferent(original.G);
byte b = MostDifferent(original.B);
return Color.FromArgb(r,g,b);
}
现在我们已经完成了,我们必须计算 string
将被绘制的区域内的平均颜色。您可以在 Bitmap
级别上执行此操作:
public static unsafe Color AverageColor (Bitmap bmp, Rectangle r) {
BitmapData bmd = bmp.LockBits (r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int s = bmd.Stride;
int cr = 0;
int cg = 0;
int cb = 0;
int* clr = (int*)(void*)bmd.Scan0;
int tmp;
int* row = clr;
for (int i = 0; i < r.Height; i++) {
int* col = row;
for (int j = 0; j < r.Width; j++) {
tmp = *col;
cr += (tmp >> 0x10) & 0xff;
cg += (tmp >> 0x08) & 0xff;
cb += tmp & 0xff;
col++;
}
row += s>>0x02;
}
int div = r.Width * r.Height;
int d2 = div >> 0x01;
cr = (cr + d2) / div;
cg = (cg + d2) / div;
cb = (cb + d2) / div;
bmp.UnlockBits (bmd);
return Color.FromArgb (cr, cg, cb);
}
最后,算法首先测量要绘制字符串的矩形,然后确定最不同的颜色,最后用该颜色绘制字符串:
public static void DrawColorString (this Graphics g, Bitmap bmp, string text, Font font, PointF point) {
SizeF sf = g.MeasureString (text, font);
Rectangle r = new Rectangle (Point.Truncate (point), Size.Ceiling (sf));
r.Intersect (new Rectangle(0,0,bmp.Width,bmp.Height));
Color brsh = MostDifferent (AverageColor (bmp, r));
g.DrawString (text, font, new SolidBrush (brsh), point);
}
现在您可以调用该方法,例如:
Bitmap bmp = new Bitmap("Foo.png");
Graphics g = Graphics.FromImage(bmp);
g.DrawColorString (bmp, "Sky", new Font ("Arial", 72.0f), new PointF (600.0f, 150.0f));
g.DrawColorString (bmp, "Sand", new Font ("Arial", 72.0f), new PointF (600.0f, 450.0f));
bmp.Save ("result.jpg");
这个结果例如:
我建议将文本绘制两次(或三次):一次在深色中,一次在亮色中,根据字体大小,向上和向左留出 1 或 2 个像素。
如果您的字体非常小并且在许多笔划上只有 1 像素宽,那么使用三重绘图会有所帮助:两个外部的一种颜色(深色)和中间的第三个(明亮的)
Random R = new Random();
private void pictureBox2_Paint(object sender, PaintEventArgs e)
{
// using a loop for create random locations..:
for (int i=0; i< 33; i++)
{
Point pt = new Point(R.Next(pictureBox2.ClientSize.Width),
R.Next(pictureBox2.ClientSize.Width));
e.Graphics.DrawString("Hello World", Font, Brushes.Black, pt.X - 1, pt.Y - 1);
e.Graphics.DrawString("Hello World", Font, Brushes.Black, pt.X + 1, pt.Y + 1);
e.Graphics.DrawString("Hello World", Font, Brushes.White, pt.X , pt.Y);
}
}
这里有一个三重绘图的例子
:
(请注意,原版看起来比我在浏览器中看到的要清晰得多。您可能需要下载它来检查..)
如果您的图像可能包含很多噪音并且字体必须相当小,那么在文本后面添加纯色背景会更好。