可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I need a C# function that will take a Byte[] of an 8 bit grayscale TIFF, and return a Byte[] of a 1 bit (black & white) TIFF.
I'm fairly new to working with TIFFs, but the general idea is that we need to convert them from grayscale or color to black and white/monochrome/binary image format.
We receive the images via a WCF as a Byte[], then we need to make this conversion to black & white in order to send them to a component which does further processing. We do not plan at this point, to ever save them as files.
For reference, in our test client, this is how we create the 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();
--------update---------
I think there may be more than 1 good answer here, but we ended up using the code from the CodeProject site with the following method added to overload the convert function to accept Byte[] as well as bitmap:
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();
}
回答1:
There is an article on CodeProject here that describes what you need.
回答2:
@neodymium has a good answer, but GetPixel/SetPixel will kill performance. Bob Powell has a great method here: http://www.bobpowell.net/onebit.htm
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);
}
回答3:
might want to check out 'Craigs Utility Library' I believe he has that functionality in place.
Craig's Utility Library
回答4:
My company's product, dotImage, will do this.
Given an image, you can convert from multi-bit to single bit using several methods including simple threshold, global threshold, local threshold, adaptive threshold, dithering (ordered and Floyd Steinberg), and dynamic threshold. The right choice depends on the type of the input image (document, image, graph).
The typical code looks like this:
AtalaImage image = new AtalaImage("path-to-tiff", null);
ImageCommand threshold = SomeFactoryToConstructAThresholdCommand();
AtalaImage finalImage = threshold.Apply(image).Image;
SomeFactoryToConstructAThresholdCommand() is a method that will return a new command that will process the image. It could be as simple as
return new DynamicThresholdCommand();
or
return new GlobalThresholdCommand();
And generally speaking, if you're looking to convert an entire multi-page tiff to black and white, you would do something like this:
// 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);
}
}
}
回答5:
First, you would need to know how an X,Y pixel location maps to an index value in you array.
This will depend upon how your Byte[] was constructed.
You need to know the details of your image format - for example, what is the stride?
I don't see 8 bit grayscale TIFF in the PixelFormat enumeration. If it was there, it would tell you what you need to know.
Then, iterate through each pixel and look at its color value.
You need to decide on a threshold value - if the color of the pixel is above the threshold, make the new color white; otherwise, make it black.
If you want to simulate grayscale shading with 1BPP, you could look at more advanced techniques, such as dithering.
回答6:
Something like this might work, I haven't tested it. (Should be easy to C# it.)
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")
This might be a better way still:
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
I believe the term you are looking for is "resampling".
回答7:
pixel by pixel manipulation is extremly slow. 40 times slower than System.DrawImage.
System.Draw image is half solution, corrupts the picture (300dpi-->96dpi) and produces at 300dpi source 200-400kb large result files.
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;
}
The perfect way is just simply convert to CCITT decoded tif, that contains only BW. Much more efficent method with 30-50kb result file, 300dpi also remains correct as well:
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);
}
}
}
Good Luck Bro,