这些年来,我多次遇到这个问题,我仍然希望有一种简单的方法可以做到这一点,而我已经错过了。我经常用条形码。它们通常是由白色背景上的黑点或线条组成的。条形码阅读器通常工作更快和更准确,当边缘是脆,然后大小的线条或点是精确的。
大多数条形码生成算法将给你一个紧凑的条形码,通常最小的元素大小是一个像素。一个典型的QR代码可以适用于21×21网格。如果在大多数打印机上打印像素到像素,这将太小,并且通常会被放大。放大的结果取决于所使用的方法,虽然有时您有选择,但通常没有使图像合适的选项。即使是直接打印,也常常会给你希望的灰色工艺品或抖动的形式。我发现最一致的方法是在其他地方(如Microsoft、lightburn和其他几个仍然让我头疼的地方)使用图片之前进行缩放。
下面我将介绍我尝试过的,并展示结果。我把它限制在位图上,只是因为在这里使用向量不是我当前项目所需要的东西。
我目前最好的分辨率不是很好,它是缓慢的,虽然我可以通过锁定位图中的位来提高速度,但我希望有人有一个非常简单的答案,这一次我在我的搜索中完全错过了。
下面是GIMP中一个简单的QR代码的图片。

问题是,如果它被放大了,它最终会看起来像这样:

下面,我创建了一个小测试程序,通过我所知道的所有不同的模式,然后生成一个图像矩阵,我已经在下面复制。我目前使用的版本是模式99,它涉及检查每个像素并绘制一个正方形。
有人有更好的主意吗?
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
namespace PSWpfCommon.Images
{
public partial class WPFImageHelper
{
public class Test
{
public int smode = 0;
public int imode = 0;
public int mode = 0;
public string title = "";
public Test(int s, int i, int m, string t)
{
smode = s;
imode = i;
mode = m;
title = t;
}
}
public static Bitmap TestImage()
{
byte[] img =
{
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44,
0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x15, 0x08, 0x06, 0x00, 0x00, 0x00, 0xA9,
0x17, 0xA5, 0x96, 0x00, 0x00, 0x00, 0xCF, 0x49, 0x44, 0x41, 0x54, 0x38, 0xCB, 0x9D, 0x54,
0x5B, 0x0E, 0xC3, 0x30, 0x08, 0xB3, 0xAB, 0xDC, 0xFF, 0xCA, 0xDE, 0xC7, 0xD4, 0x88, 0x7A,
0x3C, 0xD2, 0x21, 0x55, 0xAD, 0x48, 0xA0, 0xC6, 0x80, 0x09, 0x40, 0x28, 0x4C, 0x12, 0x48,
0xEE, 0x6F, 0x00, 0x20, 0xF9, 0xF0, 0x67, 0xB6, 0x62, 0x40, 0xB4, 0x98, 0xAC, 0x4A, 0x50,
0xC5, 0x2D, 0x4F, 0xE2, 0x97, 0x23, 0xB2, 0xEE, 0xE7, 0x31, 0xEE, 0xC2, 0xA1, 0x75, 0x89,
0xD3, 0xF2, 0x27, 0x73, 0x5E, 0x8F, 0x93, 0x56, 0x01, 0x53, 0xA2, 0xEC, 0x7C, 0x39, 0x2F,
0x19, 0xCA, 0x58, 0x7A, 0xA4, 0xA0, 0x8A, 0xA3, 0x0E, 0x6A, 0x7A, 0x5B, 0xFE, 0x45, 0x12,
0xF1, 0xF1, 0x44, 0x59, 0x73, 0xFC, 0x5E, 0x8C, 0x25, 0xF9, 0xED, 0xBE, 0xA4, 0x47, 0x49,
0xDD, 0x18, 0x79, 0xF9, 0x25, 0xA7, 0xD9, 0xA6, 0x74, 0xE3, 0xE3, 0x48, 0x9D, 0xE3, 0x55,
0xA1, 0x98, 0xB8, 0xCC, 0x16, 0xE4, 0xF6, 0x5F, 0xBE, 0xDF, 0x8E, 0x74, 0x42, 0x9F, 0x4D,
0xC0, 0x0F, 0xA7, 0xFE, 0x76, 0x14, 0x5D, 0x65, 0xDB, 0x3F, 0xA9, 0x54, 0xC7, 0x9D, 0x8B,
0xCD, 0x7D, 0x3E, 0xAA, 0xD4, 0x24, 0x77, 0x99, 0x7F, 0x54, 0xA9, 0xAA, 0x69, 0x7E, 0x3F,
0xCE, 0xEA, 0xFA, 0x67, 0x9B, 0x3A, 0x2A, 0x24, 0x9D, 0x49, 0x9F, 0x23, 0x99, 0x64, 0x71,
0x9D, 0xA8, 0x51, 0xC5, 0x6F, 0x36, 0x21, 0x5B, 0xF9, 0x3B, 0x95, 0xAA, 0x1A, 0x52, 0xAD,
0xB1, 0x24, 0x7C, 0x00, 0x22, 0x8E, 0xDE, 0x4C, 0xC4, 0x8F, 0x11, 0x7F, 0x00, 0x00, 0x00,
0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82,
};
Bitmap qr;
using (var ms = new MemoryStream(img))
{
qr = new Bitmap(ms);
}
int factor = 4;
var l = new List<Test>();
for (int i = 0; i <= 7; i++)
for (int s = 0; s <= 4; s++)
l.Add(new Test(s, i, 0, $"s={s} i={i}"));
l.Add(new Test(2, 8, 99, $"Mode 99"));
Bitmap fullimage = new Bitmap(((qr.Width * factor) + 20) * 5, ((qr.Height * factor) + 20) * 9);
fullimage.SetResolution(72, 72);
var font = new Font("Arial", 10);
using (var grPhoto = Graphics.FromImage(fullimage))
using (var blackbrush = new SolidBrush(System.Drawing.Color.Black))
using (var whitebrush = new SolidBrush(System.Drawing.Color.White))
{
grPhoto.InterpolationMode = InterpolationMode.High;
grPhoto.FillRectangle(whitebrush, 0, 0, fullimage.Width, fullimage.Height);
foreach (var t in l)
{
var newqr = GrowImage(qr, factor: 4, mode: t.mode, imode: t.imode, smode: t.smode);
grPhoto.DrawImage(newqr,
t.smode * ((qr.Width * factor) + 20),
t.imode * ((qr.Height * factor) + 20));
grPhoto.DrawString(t.title, font, blackbrush,
t.smode * ((qr.Width * factor) + 20),
(t.imode + 1) * ((qr.Height * factor) + 20) - 20);
}
}
fullimage.Save(@"c:\temp\newqr.png", ImageFormat.Png);
return null;
}
public static Bitmap GrowImage(Bitmap im, int factor = 4, int mode = 1, int imode = 0, int smode = 0, int border = 2)
{
bool translate = true;
var bmPhoto = new Bitmap(im.Width * factor + 2 * border, im.Height * factor + border * 2, PixelFormat.Format24bppRgb);
bmPhoto.SetResolution(72, 72);
using (var grPhoto = Graphics.FromImage(bmPhoto))
using (var blackbrush = new SolidBrush(System.Drawing.Color.Black))
using (var whitebrush = new SolidBrush(System.Drawing.Color.White))
{
grPhoto.FillRectangle(whitebrush, 0, 0, bmPhoto.Width, bmPhoto.Height);
switch (smode)
{
case 0:
grPhoto.SmoothingMode = SmoothingMode.Default;
break;
case 1:
grPhoto.SmoothingMode = SmoothingMode.AntiAlias;
break;
case 2:
grPhoto.SmoothingMode = SmoothingMode.HighQuality;
break;
case 3:
grPhoto.SmoothingMode = SmoothingMode.HighSpeed;
break;
case 4:
grPhoto.SmoothingMode = SmoothingMode.None;
break;
default:
break;
}
switch (imode)
{
case 0:
grPhoto.InterpolationMode = InterpolationMode.Default;
break;
case 1:
grPhoto.InterpolationMode = InterpolationMode.Bicubic;
break;
case 2:
grPhoto.InterpolationMode = InterpolationMode.Bilinear;
break;
case 3:
grPhoto.InterpolationMode = InterpolationMode.High;
break;
case 4:
grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
break;
case 5:
grPhoto.InterpolationMode = InterpolationMode.HighQualityBilinear;
break;
case 6:
grPhoto.InterpolationMode = InterpolationMode.Low;
break;
case 7:
grPhoto.InterpolationMode = InterpolationMode.NearestNeighbor;
break;
default:
break;
}
switch (mode)
{
case 99:
// These are what worked best for me...
grPhoto.SmoothingMode = SmoothingMode.None;
grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
for (int x = 0; x < im.Width; x++)
for (int y = 0; y < im.Height; y++)
{
var g = im.GetPixel(x, y);
if (g.R < 120 && g.B < 120) // Just being really lazy here... if the pixel has not much blue and not much red I'll treat it as black
{
grPhoto.FillRectangle(blackbrush, border + factor * x, border + factor * y, factor, factor);
}
}
translate = false;
break;
default:
break;
}
if (translate) // If we used mode 99, don't draw the image
grPhoto.DrawImage(im, new System.Drawing.Rectangle(border, border, im.Width * factor, im.Height * factor), 0, 0, im.Width, im.Height, System.Drawing.GraphicsUnit.Pixel);
}
return bmPhoto;
}
}
}

发布于 2022-02-09 21:20:53
这就是我如何用Bitmap和最近邻抽样一次调整一个DrawImage的大小。
在您的示例中,我准备了原始位图,每个逻辑点对应于模式的几何尺寸(例如,在21×21),然后使用下面提供的代码使用Interpolationmode.NearestNeighbor抽样将该位图调整为可打印/可显示的输出格式。
设置PixelOffsetMode.HighQuality很重要。(除非您想将坐标调整为-0.5f,-0.5f,这太烦人了,所以不要麻烦)。
public static Bitmap Resize(this Bitmap oldBitmap, int newWidth, int newHeight)
{
Bitmap newBitmap = new Bitmap(newWidth, newHeight, oldBitmap.PixelFormat);
using (Graphics g = Graphics.FromImage(newBitmap)) {
RectangleF dst = new RectangleF(0, 0, newWidth, newHeight);
RectangleF src = new RectangleF(0, 0, oldBitmap.Width, oldBitmap.Height);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.DrawImage(oldBitmap, dst, src, GraphicsUnit.Pixel);
}
return newBitmap;
}发布于 2022-02-09 19:27:56
您可以使用像ImageTracer.NET这样的库将图像转换为矢量图像,然后它将按照您的需要进行缩放:
发布于 2022-02-09 20:18:39
首先,我觉得奇怪的是,您的生成器不允许您指定单元格大小/模数。再检查一下手册。
要从单个像素单元格/模数中获取所需的内容,可以使用任何大小为原始图像的精确倍数的图像放大函数,使用最近的邻居重采样规则,而不使用其他方法。
如果找不到这样的函数,那么编写一个复制图像的函数,同时将每个像素复制为NxN正方形(在锁位下)是一件很容易的事情。
https://stackoverflow.com/questions/71055395
复制相似问题