[英]Need C# function to convert grayscale TIFF to black & white (monochrome/1BPP) TIFF
我需要一个 C# 函数,它将采用 8 位灰度 TIFF 的 Byte[],并返回 1 位(黑白)TIFF 的 Byte[]。
我对使用 TIFF 还很陌生,但总体思路是我们需要将它们从灰度或彩色转换为黑白/单色/二进制图像格式。
我们通过 WCF 作为 Byte[] 接收图像,然后我们需要将此转换为黑白,以便将它们发送到进行进一步处理的组件。 我们目前不打算将它们保存为文件。
作为参考,在我们的测试客户端中,这是我们创建 Byte[] 的方式:
FileStream fs = new FileStream("test1.tif", FileMode.Open, FileAccess.Read);
this.image = new byte[fs.Length];
fs.Read(this.image, 0, System.Convert.ToInt32(fs.Length));
fs.Close();
- - - - 更新 - - - - -
我认为这里可能有不止 1 个好的答案,但我们最终使用了 CodeProject 站点的代码,并添加了以下方法来重载转换函数以接受 Byte[] 以及位图:
public static Byte[] ConvertToBitonal(Byte[] original)
{
Bitmap bm = new Bitmap(new System.IO.MemoryStream(original, false));
bm = ConvertToBitonal(bm);
System.IO.MemoryStream s = new System.IO.MemoryStream();
bm.Save(s, System.Drawing.Imaging.ImageFormat.Tiff);
return s.ToArray();
}
这里有一篇关于 CodeProject 的文章,描述了您的需要。
@neodymium 有一个很好的答案,但 GetPixel/SetPixel 会降低性能。 鲍勃鲍威尔有一个很好的方法。
C#:
private Bitmap convertTo1bpp(Bitmap img)
{
BitmapData bmdo = img.LockBits(new Rectangle(0, 0, img.Width, img.Height),
ImageLockMode.ReadOnly,
img.PixelFormat);
// and the new 1bpp bitmap
Bitmap bm = new Bitmap(img.Width, img.Height, PixelFormat.Format1bppIndexed);
BitmapData bmdn = bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height),
ImageLockMode.ReadWrite,
PixelFormat.Format1bppIndexed);
// scan through the pixels Y by X
for(int y = 0; y < img.Height; y++)
{
for(int x = 0; x < img.Width; x++)
{
// generate the address of the colour pixel
int index = y * bmdo.Stride + x * 4;
// check its brightness
if(Color.FromArgb(Marshal.ReadByte(bmdo.Scan0, index + 2),
Marshal.ReadByte(bmdo.Scan0, index + 1),
Marshal.ReadByte(bmdo.Scan0, index)).GetBrightness() > 0.5F)
{
setIndexedPixel(x, y, bmdn, true); // set it if its bright.
}
}
}
// tidy up
bm.UnlockBits(bmdn);
img.UnlockBits(bmdo);
return bm;
}
private void setIndexedPixel(int x, int y, BitmapData bmd, bool pixel)
{
int index = y * bmd.Stride + (x >> 3);
byte p = Marshal.ReadByte(bmd.Scan0, index);
byte mask = (byte)(0x80 >> (x & 0x7));
if (pixel)
{
p |= mask;
}
else
{
p &= (byte)(mask ^ 0xFF);
}
Marshal.WriteByte(bmd.Scan0, index, p);
}
可能想查看“Craigs Utility Library”,我相信他具有该功能。 克雷格的实用程序库
我公司的产品dotImage将执行此操作。
给定一张图像,您可以使用多种方法将多位转换为单位,包括简单阈值、全局阈值、局部阈值、自适应阈值、抖动(有序和 Floyd Steinberg)和动态阈值。 正确的选择取决于输入图像的类型(文档、图像、图形)。
典型的代码如下所示:
AtalaImage image = new AtalaImage("path-to-tiff", null);
ImageCommand threshold = SomeFactoryToConstructAThresholdCommand();
AtalaImage finalImage = threshold.Apply(image).Image;
SomeFactoryToConstructAThresholdCommand() 是一种方法,它将返回处理图像的新命令。 它可以很简单
return new DynamicThresholdCommand();
要么
return new GlobalThresholdCommand();
一般来说,如果你想将整个多页 tiff 转换为黑白,你会做这样的事情:
// open a sequence of images
FileSystemImageSource source = new FileSystemImageSource("path-to-tiff", true);
using (FileStream outstm = new FileStream("outputpath", FileMode.Create)) {
// make an encoder and a threshold command
TiffEncoder encoder = new TiffEncoder(TiffCompression.Auto, true);
// dynamic is good for documents -- needs the DocumentImaging SDK
ImageCommand threshold = new DynamicThreshold();
while (source.HasMoreImages()) {
// get next image
AtalaImage image = source.AcquireNext();
AtalaImage final = threshold.Apply(image).Image;
try {
encoder.Save(outstm, final, null);
}
finally {
// free memory from current image
final.Dispose();
// release the source image back to the image source
source.Release(image);
}
}
}
首先,您需要知道 X、Y 像素位置如何映射到数组中的索引值。
这将取决于您的 Byte[] 是如何构建的。
您需要了解图像格式的详细信息——例如,步幅是多少?
我在PixelFormat 枚举中没有看到 8 位灰度 TIFF。 如果它在那里,它会告诉你你需要知道什么。
然后,遍历每个像素并查看其颜色值。 您需要确定一个阈值——如果像素的颜色高于阈值,则将新颜色设为白色; 否则,将其设为黑色。
如果你想用 1BPP 模拟灰度着色,你可以看看更高级的技术,比如抖动。
像这样的东西可能有用,我还没有测试过。 (应该很容易用 C# 吧。)
Dim bmpGrayscale As Bitmap = Bitmap.FromFile("Grayscale.tif")
Dim bmpMonochrome As New Bitmap(bmpGrayscale.Width, bmpgrayscale.Height, Imaging.PixelFormat.Format1bppIndexed)
Using gfxMonochrome As Graphics = Graphics.FromImage(bmpMonochrome)
gfxMonochrome.Clear(Color.White)
End Using
For y As Integer = 0 To bmpGrayscale.Height - 1
For x As Integer = 0 To bmpGrayscale.Width - 1
If bmpGrayscale.GetPixel(x, y) <> Color.White Then
bmpMonochrome.SetPixel(x, y, Color.Black)
End If
Next
Next
bmpMonochrome.Save("Monochrome.tif")
这可能是更好的方法:
Using bmpGrayscale As Bitmap = Bitmap.FromFile("Grayscale.tif")
Using bmpMonochrome As New Bitmap(bmpGrayscale.Width, bmpgrayscale.Height, Imaging.PixelFormat.Format1bppIndexed)
Using gfxMonochrome As Graphics = Graphics.FromImage(bmpMonochrome)
gfxMonochrome.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
gfxMonochrome.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
gfxMonochrome.DrawImage(bmpGrayscale, new Rectangle(0, 0, bmpMonochrome.Width, bmpMonochrome.Height)
End Using
bmpMonochrome.Save("Monochrome.tif")
End Using
End Using
我相信您正在寻找的术语是“重采样”。
逐像素操作非常慢。 比 System.DrawImage 慢 40 倍。 System.Draw 图像是半解决方案,损坏图片 (300dpi-->96dpi) 并在 300dpi 源生成 200-400kb 大结果文件。
public static Image GetBlackAndWhiteImage(Image SourceImage)
{
Bitmap bmp = new Bitmap(SourceImage.Width, SourceImage.Height);
using (Graphics gr = Graphics.FromImage(bmp)) // SourceImage is a Bitmap object
{
var gray_matrix = new float[][] {
new float[] { 0.299f, 0.299f, 0.299f, 0, 0 },
new float[] { 0.587f, 0.587f, 0.587f, 0, 0 },
new float[] { 0.114f, 0.114f, 0.114f, 0, 0 },
new float[] { 0, 0, 0, 1, 0 },
new float[] { 0, 0, 0, 0, 1 }
};
var ia = new System.Drawing.Imaging.ImageAttributes();
ia.SetColorMatrix(new System.Drawing.Imaging.ColorMatrix(gray_matrix));
ia.SetThreshold(float.Parse(Settings.Default["Threshold"].ToString())); // Change this threshold as needed
var rc = new Rectangle(0, 0, SourceImage.Width, SourceImage.Height);
gr.DrawImage(SourceImage, rc, 0, 0, SourceImage.Width, SourceImage.Height, GraphicsUnit.Pixel, ia);
}
return bmp;
}
完美的方法就是简单地转换为仅包含 BW 的 CCITT 解码的 tif。 使用 30-50kb 结果文件的更有效方法,300dpi 也保持正确:
public void toCCITT(string tifURL)
{
byte[] imgBits = File.ReadAllBytes(tifURL);
using (MemoryStream ms = new MemoryStream(imgBits))
{
using (Image i = Image.FromStream(ms))
{
EncoderParameters parms = new EncoderParameters(1);
ImageCodecInfo codec = ImageCodecInfo.GetImageDecoders()
.FirstOrDefault(decoder => decoder.FormatID == ImageFormat.Tiff.Guid);
parms.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionCCITT4);
i.Save(@"c:\test\result.tif", codec, parms);
}
}
}
祝你好运,兄弟,
我已经测试了这段代码并且对我来说工作得很好:
//You should use System.Linq for this to work
public static ImageCodecInfo TiffCodecInfo => ImageCodecInfo.GetImageDecoders().
FirstOrDefault(decoder => decoder.FormatID == ImageFormat.Tiff.Guid);
//Encapsulate this in a try catch block for possible exceptions
public static Bitmap ConvertToBitonal(Bitmap original)
{
EncoderParameters encoderParameters;
MemoryStream ms = new MemoryStream();
Bitmap result;
encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.ColorDepth, 1L);
original.Save(ms, TiffCodecInfo, encoderParameters);
result = new Bitmap(Image.FromStream(ms));
ms.Dispose();
return result;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.