大家好,又见面了,我是你们的朋友全栈君。
用微软自带的画图,打开一个已经存在的单色PNG图片文件,然后复制图像粘贴上去,做点反色或其他处理再保存,可以得到黑白单色PNG图片;但是,如果有很多黑底白字的图片要想改成白纸黑字的单色PNG格式保存这就很麻烦了,譬如2,3百张BMP或JPG图片用来保存只有白纸黑字的书页真是浪费。可是,有些索引格式图像如单色位图,或者单色PNG,如果用C#的Graphics类处理之后,保存文件只能得到非索引色格式或者GIF格式,若想以原格式保存文件似乎没有直接方便的方法可用,不得已只能用自己的代码一点一点去写像。 以下代码,参考网上找了些的,加上实际证得:(注释并不是必需要的,有些编写代码过程中的记录,或者为了方便说明)
using System; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices;
//打开任一索引色的或者非索引色的图像 Image img=Image.FromFile(“r.bmp”); ImageAttributes ta=new ImageAttributes(); /* 下面用Graphics类改变像点颜色,是靠ImageAttributes来把 * 彩色变成灰度,或者颠倒黑白,发现用矩阵处理还是很方便的 */ //如实际发现几个简单又好用的矩阵: /* float[][] mm=new float[][]{ //彩色变灰度的矩阵 new float[]{0.4f, 0.4f, 0.4f, 0, 0}, new float[]{0.3f, 0.3f, 0.3f, 0, 0}, new float[]{0.3f, 0.3f, 0.3f, 0, 0}, new float[]{0, 0, 0, 1, 0}, new float[]{0, 0, 0, 0, 1} }; float[][] mm1=new float[][]{ //彩色反相的矩阵 new float[]{0, 0.3f, 0.5f, 0, 0}, new float[]{0.5f, 0.3f, 0.5f, 0, 0}, new float[]{0.5f, 0.4f, 0, 0, 0}, new float[]{0, 0, 0, 1, 0}, new float[]{0, 0, 0, 0, 1} }; */ float[][] mm2=new float[][]{ //彩色变反相灰度的矩阵 new float[]{-0.4f, -0.4f, -0.4f, 0, 0}, new float[]{-0.3f, -0.3f, -0.3f, 0, 0}, new float[]{-0.3f, -0.3f, -0.3f, 0, 0}, new float[]{1, 1, 1, 1, 0}, new float[]{0, 0, 0, 0, 1} }; ColorMatrix cmt=new ColorMatrix(mm2); ta.SetColorMatrix(cmt); /* //如果确知图像里仅有纯黑白二色,也可用ColorMap来反相,它可逐色改变 ColorMap map1=new ColorMap(); map1.OldColor=Color.Black; map1.NewColor=Color.White; ColorMap map2=new ColorMap(); map2.OldColor=Color.White; map2.NewColor=Color.Black; ta.SetRemapTable(new ColorMap[]{map1,map2},ColorAdjustType.Bitmap); */ /* 有的图像比如索引格式的位图或GIF是无法创建Graphics的, * 需要新建一非索引色位图取得Graphics对象以便做画或改变像点颜色。 */ Bitmap bmp=new Bitmap(img.Width,img.Height); Graphics g=Graphics.FromImage(bmp); g.DrawImage(img,new Rectangle(0,0,bmp.Width,bmp.Height),0,0,img.Width,img.Height,GraphicsUnit.Pixel,ta); //g.DrawString(“Foxit PDF Reader”,new Font(“宋体”,8),new SolidBrush(Color.White),0,0); g.Dispose(); /* 在如下构造图像数据之前,也可以先创建一单色位图并锁定数据, * 利用它现成的Stride简单计算出实际每行有效数据之后的填充字节数,而且可 * 在下面循环里直接写点Marshal.WriteByte(dt.Scan0,k,val);而不用数组拷贝 */ //以下,把反相或者涂画后的像点数据每一行的每8点简单合并成1byte存储 int midrgb=Color.FromArgb(128,128,128).ToArgb(); int stride;//简单公式((width/8)+3)&(~3) stride = (bmp.Width%8)==0 ? (bmp.Width/8) : (bmp.Width/8)+1; stride = (stride%4)==0 ? stride : ((stride/4)+1)*4; int k=bmp.Height*stride; byte[] buf=new byte[k]; for(int j=0;j<bmp.Height;j++){ k=j*stride;//因图像宽度不同、有的可能有填充字节需要跳越 int x=0,ab=0; for(int i=0;i<bmp.Width;i++){ //从灰度变单色(下法如果直接从彩色变单色效果不太好,不过反相也可以在这里控制) if ((bmp.GetPixel(i,j)).ToArgb() > midrgb) ab=ab*2+1; else ab=ab*2; x++; if(x==8){ buf[k++]=(byte)ab; ab=0; x=0; } } if(x>0){ //循环实现:剩余有效数据不满1字节的情况下须把它们移往字节的高位部分 for(int t=x;t<8;t++)ab=ab*2; buf[k++]=(byte)ab; } } Bitmap bb=new Bitmap(img.Width,img.Height,PixelFormat.Format1bppIndexed); BitmapData dt=bb.LockBits(new Rectangle(0,0,bb.Width,bb.Height),ImageLockMode.ReadWrite,bb.PixelFormat); Marshal.Copy(buf,0,dt.Scan0,buf.Length); bb.UnlockBits(dt); //bb.MakeTransparent(Color.White); /* 如果需要生成透明的单色图像,必须根据背景颜色不同,在此指定不同颜色为透明。 * 但是带透明的PNG不再是单色PNG格式,文件尺寸也更大一些。 * 在此是以原图黑底白字的为例,因为经过上面反相以后背景已变成白色,所以这里 * 指定了白色为透明色;反之应当指定黑色为透明。 * 注意:如果原图是透明背景白字的,等同黑底白字,这段代码全部不需要修改,可 * 以反相输出透明背景黑字的。但是,如果原图是透明背景黑字的,上面就不能用矩 * 阵做反相,否则由于变成全黑而最后输出无字透明; * 因此,只需Graphics那里改成不使用ImageAttribute参数的DrawImage画出白色背景 * 黑字,然后这里不用改变就仍然输出原样的透明背景黑字; * 如果一定要求把它反相成透明背景白字的(一般也没有这种需要),只要在上面循环 * 里注释着从灰度变单色的地方把if条件的>号改成<号,这里改成指定黑色为透明即可。 */ //保存时若不指定第2参数,单色位图默认地被以单色PNG格式保存。虽然是单色像素格式, //不影响保存为GIF文件或者非索引色图片,只不过GIF不能指定透明色,否则全黑。 bb.Save(“w.png”,ImageFormat.Png); bb.Dispose(); bmp.Dispose(); img.Dispose();
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/162700.html原文链接:https://javaforall.cn
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有