使用PHP实现将jpg/png转成.wbmp/.bmp格式图片后再转为16进制字符串(单色位图取模)

2018-11-27日更新:
  由于没有找到生成.bmp格式图片的好办法,改为使用.wbmp格式,转换和读取都改为.wbmp格式,原来的bmp2hex函数逻辑没有变化,改名为wbmp2hex,并不再使用ImageCreateFromBMP函数,可以收藏一下这个函数还是有用的,最新的代码我也提供了下载在文章末尾

准备阶段:

  • pctoLCD2002
    网上找到的一款取模软件,可以读取.bmp图片并生成字模,当然我们还是要用代码来完成,这个只是起到了一个对照作用,我将它放在了我的网盘下供大家下载
    链接:点我下载pctoLCD2002 密码:2lyl

  • PHP GD扩展
    强大的PHP图像生成和处理扩展

  • Windows自带画图工具
    主要用来生成.bpm格式的图片,目前我还没有找到好的用PHP将.jpg.png图片转为单色.bmp格式图片的办法,暂时只好用画图工具来生成

操作步骤分解演示

一. 使用画图工具获得.bmp格式图片
  1. 使用画图工具打开一张事先准备好的图片,另存为.bmp单色位图,这样我们就得到了一张.bmp格式的图片,白色背景,只有黑色
    打开图片

    另存为

    .bmp格式
  2. 或者我们自己动手来画一张,打开画图工具,调整画布大小为你需要的尺寸,示例为100*70像素,取消勾选保持纵横比,调整好后点击确定,然后我们可以用刷子随便画些什么在画布上,你喜欢就好,然后重复前面的另存为.bmp单色位图步骤
    image.png
  3. pctoLCD2002也可以新建一幅.bmp图片,并且非常简单


    pctoLCD2002新建bmp文件
二. 使用pctoLCD2002取模

找到PCtoLCD2002.exe并双击打开

1. 规则解析,及本文配置项参考

在取字模之前我们先来说下PCtoLCD2002设置项和取模规则

  • 配置信息:

    pctoLCD2002设置项

  • 取模说明:
    a. 逐行式逐列式:顾名思义就是读取每张图片时取点时是逐行还是逐列的
    b. 取模走向:
    逆向:从低位到高位
    顺向:从高位到低位
    举例:
    *星号代表图中非空白的像素点,_代表空白的像素点,取八位为一个字节
    * _ _ _ _ _ _ _代表一个字节(为了方便查看,每个符号键我加入了一个空格,实际是没有的)
    逆向即是从后往前写,表示为00000001
    顺向即是从前往后写,表示为10000000
    c. 输出数制:
    这里选择十六进制,因需选择,不够我需要的是十六进制,后面的代码也只有十六进制的

  • 本文取模规则:
    逐行式 顺向 十六进制
    从第一行开始,每行每隔8个像素点为一个字节,每行结尾最后不足8位,用0补满

2. 生成字模

设置好规则后,打开之前制作的.bmp图片,点击生成字模,这时下方会生成出十六进制串,如图:


image.png

但是这还不是我最后想要的格式,需要处理一下:

  • 去掉开始处和结束处的文件路径
  • 去掉所有的标点符号,'{' '}'
  • 去掉十六进制的标识部分,所有的0x

最后得到一串连贯的字符串,类似:

0000000000000007F8000000000000000000000000003FFC00000000000000000000000000000003FF80000000

这就是我们最终需要的部分了!下面我们用代码来实现这个功能:

问题解决:

一. 实现过程及思路
0. 生成单色位图

卡在这里好久,钻进了死胡同,其实.wbmp的图片完全符合我的要求:
GD库就可以将jpg/png转换成wbmp格式,使用时可以调节threshold参数,解释如下,我理解为精度不知道准不准确,也没有查到阀值到底是什么意思...

threshold

生成.wbmp格式图片代码示例:

        $filename = 'static/img/1.jpg';
        $path = 'static/img/11.wbmp';
        $image = getimagesize($filename);
        jpeg2wbmp($filename, $path, $image[1], $image[0], 5);//threshold == 5时,和给的软件转换后结果完全一致

图片样式:


.wbmp格式
1. 读取.wbmp格式图片(原读取.bmp格式)

使用gd库的imagecreatefromwbmp函数:

        $filename = 'static/img/11.wbmp';
        $im = imagecreatefromwbmp($filename);

安装好GD库扩展后,我发现gd库只能读取.wbmp文件,并不支持.bmp文件,我的gd库版本信息如下gd_info(),经过一番google,找到了一个可以使用的读取.bmp的函数ImageCreateFromBMP(),感谢前辈

gd_info()

2. 逐个像素读取

可以读取.wbmp格式了,我们该如何能得出每个像素的颜色值呢?
通过查看gd库文档过程,我发现一个函数imagecolorat(),可以根据传入的位置,获取每个像素的索引值
使用示例:

        // 取得一点的颜色
        $file_name = '';//wbmp图片路径
        $im = imagecreatefromwbmp($file_name);//读取wbmp格式
        $start_x = 5;//行,从0开始
        $start_y = 10;//列,从0开始
        $color_index = imagecolorat($im, $start_x, $start_y);
        print_r($color_index);
3. 获取图片宽高

第2步中可以获取每个像素中的值了,但是我们总不能每个点都手动传入,这时我们就需要获取图片的宽高了

gd库中有获取图片宽高的函数imagesx()imagesy(),代码示例:

        $im = imagecreatefromwbmp($file_name);//读取bmp格式,非gd库
        $width = imagesx($im);
        $height = imagesy($im);
        echo $width.'*'.$height;

到这里,最主要的部分都已经可以获取到了,后面就是逻辑部分了(代码中可以看到具体实现方式):

  • 根据图片的宽高,逐行逐点读取每个像素的值,每8位组合成1个字节,然后取模,再转为16进制
  • 检测每一行的最后是否满足8个像素,不足则用0补满
  • 最终将每一行组合到一起,组成16进制字符串
二. 源码

整个过程可以大概分为三步完成,你可以根据自己的需求参考或者直接copy使用,如果对你有帮助,希望可以点个赞,转载请注明本篇文章链接地址及作者,谢谢!

  • 将普通jpg/png格式转为.wbmp
        $filename = 'static/img/1.jpg';
        $path = 'static/img/11.wbmp';
        $image = getimagesize($filename);
        jpeg2wbmp($filename, $path, $image[1], $image[0], 5);//threshold == 5时,和给的软件转换后结果完全一致
  • 读取.wbmp格式图片
        $filename = 'static/img/11.wbmp';
        $im = imagecreatefromwbmp($filename);
  • .wbmp转为hex_str
/**
     * @param $im
     * @return string
     * Commented by liu
     */
    public function wbmp2hex($im)
    {
        $width = imagesx($im);
        $height = imagesy($im);
        $num = $width % 8;

        $hex_str = '';
        for ($start_y = 0; $start_y < $height; $start_y++) {
            $binary_str = '';
            for ($start_x = 0; $start_x < $width; $start_x++) {
                $color_index = imagecolorat($im, $start_x, $start_y);//指定像素的索引值

                $binary_str .= $color_index == 1 ? 1 : 0;
                if ((1 + $start_x) % 8 == 0 && $start_x != 0) {//每隔8位转换1次
                    $hex = (string)dechex(bindec($binary_str));
                    $hex = strlen($hex) == 1 ? '0' . $hex : $hex;//补0
                    $hex_str .= $hex;
                    $binary_str = '';
                }
            }

            //这时如果$binary_str不为空,说明需要向后补0
            if ($num) {
                for ($i = 0; $i < 8 - $num; $i++) {
                    $binary_str .= 0;
                }
                $hex = (string)dechex(bindec($binary_str));
                $hex = strlen($hex) == 1 ? '0' . $hex : $hex;//补0
                $hex_str .= $hex;
            }
        }

        $hex_str = strtoupper($hex_str);//转为大写
        return $hex_str;
    }

最后附上读取.bmp格式图片的函数:

<?php

/**
 * Commented by liu
 * Create on 2018/11/23 17:51
 * Class Image_api
 */

class Image_api
{
    function __construct (){

    }

    /**
     * ImageCreateFromBMP函数,读取bmp格式图片
     * 注:phpGD扩展中没有ImageCreateFromBMP函数,只有ImageCreateFromWBMP
     * @param $filename
     * @return bool|resource
     * Commented by liu
     */
    function ImageCreateFromBMP($filename)
    {
        //Ouverture du fichier en mode binaire
        if (!$f1 = fopen($filename, "rb"))
            return FALSE;

        //1 : Chargement des ent�tes FICHIER
        $FILE = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1, 14));
        if ($FILE['file_type'] != 19778)
            return FALSE;

        //2 : Chargement des ent�tes BMP
        $BMP = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel' .
            '/Vcompression/Vsize_bitmap/Vhoriz_resolution' .
            '/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1, 40));
        $BMP['colors'] = pow(2, $BMP['bits_per_pixel']);
        if ($BMP['size_bitmap'] == 0)
            $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset'];
        $BMP['bytes_per_pixel'] = $BMP['bits_per_pixel'] / 8;
        $BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']);
        $BMP['decal'] = ($BMP['width'] * $BMP['bytes_per_pixel'] / 4);
        $BMP['decal'] -= floor($BMP['width'] * $BMP['bytes_per_pixel'] / 4);
        $BMP['decal'] = 4 - (4 * $BMP['decal']);
        if ($BMP['decal'] == 4)
            $BMP['decal'] = 0;

        //3 : Chargement des couleurs de la palette
        $PALETTE = array();
        if ($BMP['colors'] < 16777216) {
            $PALETTE = unpack('V' . $BMP['colors'], fread($f1, $BMP['colors'] * 4));
        }

        //4 : Cr�ation de l'image
        $IMG = fread($f1, $BMP['size_bitmap']);
        $VIDE = chr(0);

        $res = imagecreatetruecolor($BMP['width'], $BMP['height']);
        $P = 0;
        $Y = $BMP['height'] - 1;
        while ($Y >= 0) {
            $X = 0;
            while ($X < $BMP['width']) {
                if ($BMP['bits_per_pixel'] == 24)
                    $COLOR = unpack("V", substr($IMG, $P, 3) . $VIDE);
                elseif ($BMP['bits_per_pixel'] == 16) {
                    $COLOR = unpack("n", substr($IMG, $P, 2));
                    $COLOR[1] = $PALETTE[$COLOR[1] + 1];
                } elseif ($BMP['bits_per_pixel'] == 8) {
                    $COLOR = unpack("n", $VIDE . substr($IMG, $P, 1));
                    $COLOR[1] = $PALETTE[$COLOR[1] + 1];
                } elseif ($BMP['bits_per_pixel'] == 4) {
                    $COLOR = unpack("n", $VIDE . substr($IMG, floor($P), 1));
                    if (($P * 2) % 2 == 0)
                        $COLOR[1] = ($COLOR[1] >> 4);
                    else
                        $COLOR[1] = ($COLOR[1] & 0x0F);
                    $COLOR[1] = $PALETTE[$COLOR[1] + 1];
                } elseif ($BMP['bits_per_pixel'] == 1) {
                    $COLOR = unpack("n", $VIDE . substr($IMG, floor($P), 1));
                    if (($P * 8) % 8 == 0)
                        $COLOR[1] = $COLOR[1] >> 7;
                    elseif (($P * 8) % 8 == 1)
                        $COLOR[1] = ($COLOR[1] & 0x40) >> 6;
                    elseif (($P * 8) % 8 == 2)
                        $COLOR[1] = ($COLOR[1] & 0x20) >> 5;
                    elseif (($P * 8) % 8 == 3)
                        $COLOR[1] = ($COLOR[1] & 0x10) >> 4;
                    elseif (($P * 8) % 8 == 4)
                        $COLOR[1] = ($COLOR[1] & 0x8) >> 3;
                    elseif (($P * 8) % 8 == 5)
                        $COLOR[1] = ($COLOR[1] & 0x4) >> 2;
                    elseif (($P * 8) % 8 == 6)
                        $COLOR[1] = ($COLOR[1] & 0x2) >> 1;
                    elseif (($P * 8) % 8 == 7)
                        $COLOR[1] = ($COLOR[1] & 0x1);
                    $COLOR[1] = $PALETTE[$COLOR[1] + 1];
                } else
                    return FALSE;
                imagesetpixel($res, $X, $Y, $COLOR[1]);
                $X++;
                $P += $BMP['bytes_per_pixel'];
            }
            $Y--;
            $P += $BMP['decal'];
        }

        //Fermeture du fichier
        fclose($f1);
        return $res;
    }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容