[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖

[问题求助] 求助高手将CS代码转换成PS代码提高Gif质量

本帖最后由 5i365 于 2022-6-6 16:25 编辑

以前发过一个类似的将Png图片24位改为256色的贴子, 目前还没有解决!

我已经有PS的截屏保存GIF的代码了,但是GIF不清晰,有噪点, 体积大, 代码如下:
  1. Function Get-Screenshot($FilePath)
  2. {
  3.         $ScreenBounds = [Windows.Forms.SystemInformation]::VirtualScreen
  4.         $ScreenshotObject = New-Object Drawing.Bitmap $ScreenBounds.Width, $ScreenBounds.Height
  5.         $DrawingGraphics = [Drawing.Graphics]::FromImage($ScreenshotObject)
  6.         $DrawingGraphics.CopyFromScreen($ScreenBounds.Location, [Drawing.Point]::Empty, $ScreenBounds.Size)
  7.         $DrawingGraphics.Dispose()
  8.         $ScreenshotObject.Save($FilePath, ([system.drawing.imaging.imageformat]::gif))
  9.         $ScreenshotObject.Dispose()
  10. }
  11. Get-Screenshot "$HOME\Desktop\GIF.gif"
复制代码


刚刚在国外博客上,找到了优化GIF图像C#代码的两个CS类文件, 对C#不太懂, 请求同时精通C#和PS的前辈能帮忙转化一下, 提前感谢!

下面引用了某博主的关于GIF问题的描述和代码:

在C#中默认可以将bitmap保存为gif等格式,但是这种保存方法保存的gif会严重失真, 有噪点, 正常情况下的代码:

  1. System.Drawing.Bitmap b = new System.Drawing.Bitmap("c:\\original_image.gif");
  2. System.Drawing.Image thmbnail = b.GetThumbnailImage(100, 75, null, new IntPtr());
  3. thmbnail.Save("c:\\thumnail.gif", System.Drawing.Imaging.ImageFormat.Gif);
复制代码


找到了一个解决办法,保存出来的gif容量大减,但是效果还不错
这个方法就是就是"Octree"算法, 允许我们插入自己的算法来量子化我们的图像
一个好的"颜色量子化"算法应该考虑在两个像素颗粒之间填充与这两个像素颜色相近的过渡颜色,提供更多可视颜色空间
国外的Morgan Skinner提供了很好的"Octree"算法, 大家可以参考使用

使用OctreeQuantizer很方便:

  1. System.Drawing.Bitmap b = new System.Drawing.Bitmap("c:\\original_image.gif");
  2. System.Drawing.Image thmbnail = b.GetThumbnailImage(100, 75, null, new IntPtr());
  3. OctreeQuantizer quantizer = new OctreeQuantizer(255, 8);
  4. using (Bitmap quantized = quantizer.Quantize(thmbnail))
  5. {
  6.         quantized.Save("c:\\thumnail.gif", System.Drawing.Imaging.ImageFormat.Gif);
  7. }
  8. OctreeQuantizer grayquantizer = new GrayscaleQuantizer();
  9. using (Bitmap quantized = grayquantizer.Quantize(thmbnail))
  10. {
  11.         quantized.Save("c:\\thumnail.gif", System.Drawing.Imaging.ImageFormat.Gif);
  12. }
复制代码


根据试用,只需要两个类文件(OctreeQuantizer.cs,Quantizer.cs)即可运行,将这两个类文件的namespace改成你项目的名称就行
还有,需要在不安全编译的方式下编译,右击项目名称,在生成选项卡里选择"允许不安全代码"即可

两个CS类文件夹下载:
https://send.cm/d/Bym3
本人所发所有贴子或代码, 诸大侠若认为有改进之处,请不吝赐教,感激不尽!

回复 6# flashercs

感谢大侠帮忙

生成的文件,查看属性, 颜色已经变成了8位, 但是却变成了灰度, 另外体积也很大,


可能我对楼上贴出的代码理解错了, 那个代码不能8位彩色?

实际手动截图,存成8位彩色的PNG和GIF, 都小很多


看来只能使用1楼提到的原子量化函数了   
   
本人所发所有贴子或代码, 诸大侠若认为有改进之处,请不吝赐教,感激不尽!

TOP

微信:flashercs
QQ:49908356

TOP

上面链接中的代码, 留个备份
  1. using System;
  2. class Program
  3. {
  4.   static void Main(string[] args)
  5.   { System.Drawing.Bitmap b = new System.Drawing.Bitmap("test.jpg");
  6.     SplashImage(b,0,0);
  7.     //
  8.     DateTime dtFaq=DateTime.Now;
  9.     System.Drawing.Bitmap b0 = CopyToBpp(b,1);
  10.     TimeSpan tsFaq=DateTime.Now-dtFaq;
  11.     Console.WriteLine("GDI conversion time: "+tsFaq.ToString());
  12.     SplashImage(b0,200,100);
  13.     //
  14.     DateTime dtLu=DateTime.Now;
  15.     System.Drawing.Bitmap b1 = FaqCopyTo1bpp(b);
  16.     TimeSpan tsLu=DateTime.Now-dtLu;
  17.     Console.WriteLine("FAQ conversion time: "+tsLu.ToString());
  18.     SplashImage(b1,400,200);
  19.     //
  20.     System.Threading.Thread.Sleep(1000);
  21.     InvalidateRect(IntPtr.Zero, IntPtr.Zero, 1);   
  22.   }
  23. /// Copies a bitmap into a 1bpp/8bpp bitmap of the same dimensions, fast
  24. /// <param name="b">original bitmap</param>
  25. /// <param name="bpp">1 or 8, target bpp</param>
  26. /// <returns>a 1bpp copy of the bitmap</returns>
  27. static System.Drawing.Bitmap CopyToBpp(System.Drawing.Bitmap b, int bpp)
  28. { if (bpp!=1 && bpp!=8) throw new System.ArgumentException("1 or 8","bpp");
  29.   // Plan: built into Windows GDI is the ability to convert
  30.   // bitmaps from one format to another. Most of the time, this
  31.   // job is actually done by the graphics hardware accelerator card
  32.   // and so is extremely fast. The rest of the time, the job is done by
  33.   // very fast native code.
  34.   // We will call into this GDI functionality from C#. Our plan:
  35.   // (1) Convert our Bitmap into a GDI hbitmap (ie. copy unmanaged->managed)
  36.   // (2) Create a GDI monochrome hbitmap
  37.   // (3) Use GDI "BitBlt" function to copy from hbitmap into monochrome (as above)
  38.   // (4) Convert the monochrone hbitmap into a Bitmap (ie. copy unmanaged->managed)
  39.   
  40.   int w=b.Width, h=b.Height;
  41.   IntPtr hbm = b.GetHbitmap(); // this is step (1)
  42.   //
  43.   // Step (2): create the monochrome bitmap.
  44.   // "BITMAPINFO" is an interop-struct which we define below.
  45.   // In GDI terms, it's a BITMAPHEADERINFO followed by an array of two RGBQUADs
  46.   BITMAPINFO bmi = new BITMAPINFO();
  47.   bmi.biSize=40;  // the size of the BITMAPHEADERINFO struct
  48.   bmi.biWidth=w;
  49.   bmi.biHeight=h;
  50.   bmi.biPlanes=1; // "planes" are confusing. We always use just 1. Read MSDN for more info.
  51.   bmi.biBitCount=(short)bpp; // ie. 1bpp or 8bpp
  52.   bmi.biCompression=BI_RGB; // ie. the pixels in our RGBQUAD table are stored as RGBs, not palette indexes
  53.   bmi.biSizeImage = (uint)(((w+7)&0xFFFFFFF8)*h/8);
  54.   bmi.biXPelsPerMeter=1000000; // not really important
  55.   bmi.biYPelsPerMeter=1000000; // not really important
  56.   // Now for the colour table.
  57.   uint ncols = (uint)1<<bpp; // 2 colours for 1bpp; 256 colours for 8bpp
  58.   bmi.biClrUsed=ncols;
  59.   bmi.biClrImportant=ncols;
  60.   bmi.cols=new uint[256]; // The structure always has fixed size 256, even if we end up using fewer colours
  61.   if (bpp==1) {bmi.cols[0]=MAKERGB(0,0,0); bmi.cols[1]=MAKERGB(255,255,255);}
  62.   else {for (int i=0; i<ncols; i++) bmi.cols[i]=MAKERGB(i,i,i);}
  63.   // For 8bpp we've created an palette with just greyscale colours.
  64.   // You can set up any palette you want here. Here are some possibilities:
  65.   // greyscale: for (int i=0; i<256; i++) bmi.cols[i]=MAKERGB(i,i,i);
  66.   // rainbow: bmi.biClrUsed=216; bmi.biClrImportant=216; int[] colv=new int[6]{0,51,102,153,204,255};
  67.   //          for (int i=0; i<216; i++) bmi.cols[i]=MAKERGB(colv[i/36],colv[(i/6)%6],colv[i%6]);
  68.   // optimal: a difficult topic: http://en.wikipedia.org/wiki/Color_quantization
  69.   //
  70.   // Now create the indexed bitmap "hbm0"
  71.   IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.
  72.   IntPtr hbm0 = CreateDIBSection(IntPtr.Zero,ref bmi,DIB_RGB_COLORS,out bits0,IntPtr.Zero,0);
  73.   //
  74.   // Step (3): use GDI's BitBlt function to copy from original hbitmap into monocrhome bitmap
  75.   // GDI programming is kind of confusing... nb. The GDI equivalent of "Graphics" is called a "DC".
  76.   IntPtr sdc = GetDC(IntPtr.Zero);       // First we obtain the DC for the screen
  77.    // Next, create a DC for the original hbitmap
  78.   IntPtr hdc = CreateCompatibleDC(sdc); SelectObject(hdc,hbm);
  79.   // and create a DC for the monochrome hbitmap
  80.   IntPtr hdc0 = CreateCompatibleDC(sdc); SelectObject(hdc0,hbm0);
  81.   // Now we can do the BitBlt:
  82.   BitBlt(hdc0,0,0,w,h,hdc,0,0,SRCCOPY);
  83.   // Step (4): convert this monochrome hbitmap back into a Bitmap:
  84.   System.Drawing.Bitmap b0 = System.Drawing.Bitmap.FromHbitmap(hbm0);
  85.   //
  86.   // Finally some cleanup.
  87.   DeleteDC(hdc);
  88.   DeleteDC(hdc0);
  89.   ReleaseDC(IntPtr.Zero,sdc);
  90.   DeleteObject(hbm);
  91.   DeleteObject(hbm0);
  92.   //
  93.   return b0;
  94. }
  95.   /// Draws a bitmap onto the screen. Note: this will be overpainted
  96.   /// by other windows when they come to draw themselves. Only use it
  97.   /// if you want to draw something quickly and can't be bothered with forms.
  98.   /// <param name="b">the bitmap to draw on the screen</param>
  99.   /// <param name="x">x screen coordinate</param>
  100.   /// <param name="y">y screen coordinate</param>
  101.   static void SplashImage(System.Drawing.Bitmap b, int x, int y)
  102.   { // Drawing onto the screen is supported by GDI, but not by the Bitmap/Graphics class.
  103.     // So we use interop:
  104.     // (1) Copy the Bitmap into a GDI hbitmap
  105.     IntPtr hbm = b.GetHbitmap();
  106.     // (2) obtain the GDI equivalent of a "Graphics" for the screen
  107.     IntPtr sdc = GetDC(IntPtr.Zero);
  108.     // (3) obtain the GDI equivalent of a "Graphics" for the hbitmap
  109.     IntPtr hdc = CreateCompatibleDC(sdc);
  110.     SelectObject(hdc,hbm);
  111.     // (4) Draw from the hbitmap's "Graphics" onto the screen's "Graphics"
  112.     BitBlt(sdc,x,y,b.Width,b.Height,hdc,0,0,SRCCOPY);
  113.     // and do boring GDI cleanup:
  114.     DeleteDC(hdc);
  115.     ReleaseDC(IntPtr.Zero,sdc);
  116.     DeleteObject(hbm);
  117.   }
  118.   [System.Runtime.InteropServices.DllImport("gdi32.dll")]
  119.   public static extern bool DeleteObject(IntPtr hObject);
  120.   [System.Runtime.InteropServices.DllImport("user32.dll")]
  121.   public static extern int InvalidateRect(IntPtr hwnd, IntPtr rect, int bErase);
  122.   [System.Runtime.InteropServices.DllImport("user32.dll")]
  123.   public static extern IntPtr GetDC(IntPtr hwnd);
  124.   [System.Runtime.InteropServices.DllImport("gdi32.dll")]
  125.   public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
  126.   [System.Runtime.InteropServices.DllImport("user32.dll")]
  127.   public static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);
  128.   [System.Runtime.InteropServices.DllImport("gdi32.dll")]
  129.   public static extern int DeleteDC(IntPtr hdc);
  130.   [System.Runtime.InteropServices.DllImport("gdi32.dll")]
  131.   public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
  132.   [System.Runtime.InteropServices.DllImport("gdi32.dll")]
  133.   public static extern int BitBlt(IntPtr hdcDst, int xDst, int yDst, int w, int h, IntPtr hdcSrc, int xSrc, int ySrc, int rop);
  134.   static int SRCCOPY = 0x00CC0020;
  135.   [System.Runtime.InteropServices.DllImport("gdi32.dll")]
  136.   static extern IntPtr CreateDIBSection(IntPtr hdc, ref BITMAPINFO bmi, uint Usage, out IntPtr bits, IntPtr hSection, uint dwOffset);
  137.   static uint BI_RGB = 0;
  138.   static uint DIB_RGB_COLORS=0;
  139.   [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
  140.   public struct BITMAPINFO
  141.   { public uint biSize;
  142.     public int biWidth, biHeight;
  143.     public short biPlanes, biBitCount;
  144.     public uint biCompression, biSizeImage;
  145.     public int biXPelsPerMeter, biYPelsPerMeter;
  146.     public uint biClrUsed, biClrImportant;
  147.     [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=256)]
  148.     public uint[] cols;
  149.   }
  150.   static uint MAKERGB(int r,int g,int b)
  151.   { return ((uint)(b&255)) | ((uint)((r&255)<<8)) | ((uint)((g&255)<<16));
  152.   }
  153.   /// Copies a bitmap into a 1bpp bitmap of the same dimensions, slowly, using code from Bob Powell's GDI+ faq http://www.bobpowell.net/onebit.htm
  154.   /// <param name="b">original bitmap</param>
  155.   /// <returns>a 1bpp copy of the bitmap</returns>
  156.   static System.Drawing.Bitmap FaqCopyTo1bpp(System.Drawing.Bitmap b)
  157.   { int w=b.Width, h=b.Height; System.Drawing.Rectangle r = new System.Drawing.Rectangle(0,0,w,h);
  158.     if (b.PixelFormat!=System.Drawing.Imaging.PixelFormat.Format32bppPArgb)
  159.     { System.Drawing.Bitmap temp=new System.Drawing.Bitmap(w,h,System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
  160.       System.Drawing.Graphics g=System.Drawing.Graphics.FromImage(temp);
  161.       g.DrawImage(b,r,0,0,w,h,System.Drawing.GraphicsUnit.Pixel);
  162.       g.Dispose(); b=temp;
  163.     }
  164.     System.Drawing.Imaging.BitmapData bdat = b.LockBits(r,System.Drawing.Imaging.ImageLockMode.ReadOnly,b.PixelFormat);
  165.     System.Drawing.Bitmap b0 = new System.Drawing.Bitmap(w,h,System.Drawing.Imaging.PixelFormat.Format1bppIndexed);
  166.     System.Drawing.Imaging.BitmapData b0dat=b0.LockBits(r,System.Drawing.Imaging.ImageLockMode.ReadWrite,System.Drawing.Imaging.PixelFormat.Format1bppIndexed);
  167.     for(int y=0; y<h; y++)
  168.     { for (int x=0; x<w; x++)
  169.       { int index = y*bdat.Stride+(x*4);
  170.         if (System.Drawing.Color.FromArgb(System.Runtime.InteropServices.Marshal.ReadByte(bdat.Scan0,index+2),System.Runtime.InteropServices.Marshal.ReadByte(bdat.Scan0,index+1),System.Runtime.InteropServices.Marshal.ReadByte(bdat.Scan0,index)).GetBrightness()>0.5f)
  171.         { int index0 = y*b0dat.Stride+(x>>3);
  172.           byte p=System.Runtime.InteropServices.Marshal.ReadByte(b0dat.Scan0,index0);
  173.           byte mask=(byte)(0x80>>(x&0x7));
  174.           System.Runtime.InteropServices.Marshal.WriteByte(b0dat.Scan0,index0,(byte)(p|mask));
  175.         }
  176.       }
  177.     }
  178.     b0.UnlockBits(b0dat);
  179.     b.UnlockBits(bdat);
  180.     return b0;
  181.   }
  182. }
复制代码
本人所发所有贴子或代码, 诸大侠若认为有改进之处,请不吝赐教,感激不尽!

TOP

回复 2# flashercs

又搜索到一个稍短C#代码的文章, 看中文翻译, 好像可以同时解决PNG和GIF的问题, 等待高手支招



http://www.wischik.com/lu/programmer/1bpp.html
本人所发所有贴子或代码, 诸大侠若认为有改进之处,请不吝赐教,感激不尽!

TOP

回复 2# flashercs


感谢大侠提醒!

用第三方使用起来不太方便


那两个cs,代码, 有一半以上是注释, 感觉PS应该也能实现
本人所发所有贴子或代码, 诸大侠若认为有改进之处,请不吝赐教,感激不尽!

TOP

png可以用第三方工具压缩成8bit
论坛有个工具 http://bcn.bathome.net/tool/pngquant.exe
可以的
微信:flashercs
QQ:49908356

TOP

返回列表