分类存档: Tools

PyPuzzle

前一阵子贴吧洪水泛滥, 写了个Bot过滤关键词删贴. 后来发现广告贴有改贴图的趋势, 这个过滤有压力啊. 想了想读图这种高难度还是不考虑了, 建个图片索引库然后比较相似度好了. 搜了下有现成的库libpuzzle, 不过是个C库, 而Bot是Python写的, 那就顺便包装个Extension吧, 也顺便学写下Python的C扩展, 于是就有了这个PyPuzzle. 参考了PycURL, 拿来当模板还是很不错的.

项目主页: https://github.com/ArchangelSDY/PyPuzzle

结果写完广告贴就销声匿迹了, 囧.

imageInstant – 图片即时搜索脚本

现在以图搜图的搜索引擎也不少了, 一般都支持通过URL查找或通过上传文件查找. 不过每次想找图时都要另开一页把图片地址复制进去颇为不便, 于是就做了这么一个脚本以Bookmarklet的形式调用, 在图片上生成一个菜单, 直接选择搜索引擎搜索即可.

imageInstant

其实这个脚本还是上个学期末写的, 寒假看了本《Javascript语言精粹》, 受益匪浅, 可谓字字精华没有一句废话, 于是这几天就把这个脚本重写了一遍, 感觉正如书中所说, 如果除去那些糟粕, Javascript确实是门非常优雅的语言.

项目托管在GitHub.

主页: http://archangelsdy.github.com/iqdbInstant/
源码: https://github.com/ArchangelSDY/iqdbInstant

32-bit BMP转PNG之C#版

.Net库中默认的Bitmap类是不支持读取32-bit BMP的, 注意是不支持直接读, 本身是可以处理ARGB结构的. 既然这样那就自己一个个字节读吧, 反正BMP又不需要解码一个一个像素挨个读取便是.

不过读取了每个像素的各个分量值后如果挨个setPixel()的效率是不可忍受的. 且不说双层循环本来效率就很差, setPixel()本来就不快. 还好微软自己也觉得这速度太不靠谱, 留下了用指针直接操作的方法.

主要代码如下:

using (FileStream fs = new FileStream(file, FileMode.Open))
{
    //判断像素深度, 仅对32-bit BMP操作
    fs.Position = 0x1C;
    byte[] byteDepth = new byte[4];
    fs.Read(byteDepth, 0, 4);
    int depth = BitConverter.ToInt32(byteDepth, 0);
    if (depth != 32)
    {
        Console.Write("Not 32-bit bmp file.\n");
        continue;
    }
    else
    {
        DateTime dt = DateTime.Now;
    
        //Pixel数据区开始位置
        fs.Position = 0xA;
        byte[] bytePixelArrayStart = new byte[4];
        fs.Read(bytePixelArrayStart, 0, 4);
        int posPixelArrayStart = BitConverter.ToInt32(bytePixelArrayStart, 0);
    
        //图片宽度
        fs.Position = 0x12;
        byte[] byteWidth = new byte[4];
        fs.Read(byteWidth, 0, 4);
        int width = BitConverter.ToInt32(byteWidth, 0);
    
        //图片高度
        fs.Position = 0x16;
        byte[] byteHeight = new byte[4];
        fs.Read(byteHeight, 0, 4);
        int height = BitConverter.ToInt32(byteHeight, 0);

        fs.Position = posPixelArrayStart;
        //建立一个与原图等大的Bitmap对象, 设置像素格式为32-bitARGB
        using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb))
        {
            //获取该Bitmap对象的BitmapData数据
            BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bmp.PixelFormat);
            //获取数据开始的指针
            IntPtr ptr = bmpData.Scan0;
            //计算数据区的长度
            int imgLen = Math.Abs(bmpData.Stride) * bmp.Height;
            byte[] pixels = new byte[imgLen];
            
            //逐个Byte读取值并写入对应像素的各个通道
            for (int counter = 0; counter < pixels.Length; counter++)
            {
                byte[] color = new byte[1];
                fs.Read(color, 0, 1);
                pixels[counter] = color[0];
            }
            
            //直接将新的Pixel数据复制入BitmapData对象, 覆盖原有数据
            System.Runtime.InteropServices.Marshal.Copy(pixels, 0, ptr, imgLen);
            bmp.UnlockBits(bmpData);
    
            //这样生成的图时y方向上下颠倒的, 需要做一次翻转
            //这样的翻转不会导致图片质量的损失
            bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
    
            //转换成PNG格式并写入新文件
            string pngFile = string.Format(@"{0}\{1}.png", Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file));
            bmp.Save(pngFile, ImageFormat.Png);
    
            Console.Write("Used: {0}ms.\n", (DateTime.Now - dt).TotalMilliseconds);
        }
    }
}

测试表明使用指针直接操作Pixel数据后速度有显著提升.

另有个小工具叫AlphaConv也可以实现32-bit BMP和PNG互转, Delphi写的, 调的libpng, 效率应该不错, 可惜只有图形界面.

有空来试试直接用C调libpng来实现一下, 看能比.Net快多少><.

LilyBot

10.1期间写了LilyBot, 用来定时抓取教务网的最新通知和百合上特定板块的帖子, 然后生成RSS. 从此系版和教务网上的最新通知都可以统一抓到Google Reader中, 我等收不到飞信不用人人不逛BBS的人终于也可以与时俱进了><. 无奈10.1期间vps抽得厉害, 掉包率40%~80%, ping值能上4000ms, 根本没法ssh上去调试, 于是拖到了现在. 目前抓取目标如下:

1. 教务网通知;
2. 百合DII版;
3. 百合Pictures版;
4. 百合M_Academic版.

百合限制了单位时间内同一ip的连接数, 抓帖子内容比较慢, 这些抓一遍大概2min(其实应该能更快, 保险起见设置了抓一贴sleep(1)), 所以暂时就先抓这么多, 谁有需求我再加.

Firefox可以直接把RSS订阅到书签栏. Chrome有RSS Live Links扩展. IE也可以直接订阅, 而且还能同时在桌面小工具里显示.

教务网通知只抓了标题, 啥时有空把内容也抓了好了><~