c# - solo - monocromo photoshop
Se necesita la función C#para convertir TIFF en escala de grises a TIFF en blanco y negro(monocromo/1BPP) (7)
Necesito una función C # que tomará un Byte [] de un TIFF de escala de grises de 8 bits y devolverá un Byte [] de un TIFF de 1 bit (blanco y negro).
Soy bastante nuevo para trabajar con TIFF, pero la idea general es que debemos convertirlos de escala de grises o color a formato de imagen en blanco y negro / monocromo / binario.
Recibimos las imágenes a través de un WCF como un byte [], luego tenemos que hacer esta conversión a blanco y negro para enviarlas a un componente que realice un procesamiento posterior. No planificamos en este momento, guardarlos como archivos.
Como referencia, en nuestro cliente de prueba, así es como creamos el 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();
--------actualizar---------
Creo que puede haber más de 1 buena respuesta aquí, pero terminamos usando el código del sitio de CodeProject con el siguiente método agregado para sobrecargar la función de conversión para aceptar Byte [] y 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();
}
Algo como esto podría funcionar, no lo he probado. (Debería ser fácil 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")
Esta podría ser una mejor manera aún:
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
Creo que el término que está buscando es "remuestrear".
Aquí hay un artículo sobre CodeProject que describe lo que necesita.
El producto de mi empresa, dotImage , hará esto.
Dada una imagen, puede convertir de varios bits a un solo bit utilizando varios métodos, incluidos el umbral simple, el umbral global, el umbral local, el umbral adaptativo, el difuminado (ordenado y Floyd Steinberg) y el umbral dinámico. La elección correcta depende del tipo de imagen de entrada (documento, imagen, gráfico).
El código típico se ve así:
AtalaImage image = new AtalaImage("path-to-tiff", null);
ImageCommand threshold = SomeFactoryToConstructAThresholdCommand();
AtalaImage finalImage = threshold.Apply(image).Image;
SomeFactoryToConstructAThresholdCommand () es un método que devolverá un nuevo comando que procesará la imagen. Podría ser tan simple como
return new DynamicThresholdCommand();
o
return new GlobalThresholdCommand();
Y en términos generales, si está buscando convertir un tiff completo de varias páginas a blanco y negro, haría algo como esto:
// 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);
}
}
}
En primer lugar, necesitaría saber cómo una ubicación X, Y de píxeles se correlaciona con un valor de índice en su matriz. Esto dependerá de cómo se construyó su Byte []. Necesita conocer los detalles de su formato de imagen, por ejemplo, ¿cuál es el paso ?
No veo TIFF de escala de grises de 8 bits en la enumeración PixelFormat. Si estuviera allí, le diría lo que necesita saber.
Luego, recorra cada píxel y observe su valor de color. Debe decidir sobre un valor umbral: si el color del píxel está por encima del umbral, haga que el nuevo color sea blanco; de lo contrario, hazlo negro.
Si desea simular el sombreado en escala de grises con 1BPP, puede ver técnicas más avanzadas, como el difuminado.
Quizás quiera echarle un vistazo a la "Biblioteca de herramientas de Craigs". Creo que tiene esa funcionalidad en su lugar. Biblioteca de utilidades de Craig
@neodymium tiene una buena respuesta, pero GetPixel / SetPixel matará el rendimiento. Bob Powell tiene un gran método aquí: http://www.bobpowell.net/onebit.htm
DO#:
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);
}
la manipulación píxel a pixel es extremadamente lenta. 40 veces más lento que System.DrawImage. La imagen de System.Draw es la mitad de la solución, daña la imagen (300 ppp -> 96 ppp) y produce archivos de resultados de gran tamaño de 300 ppp a 300 ppp.
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;
}
La forma perfecta es simplemente convertir a TIF decodificado CCITT, que contiene solo BW. Método mucho más eficiente con un archivo de resultados de 30-50kb, 300dpi también sigue siendo correcto:
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);
}
}
}
Buena suerte hermano,