使用PHP實現(xiàn)將jpg/png轉(zhuǎn)成.wbmp/.bmp格式圖片后再轉(zhuǎn)為16進(jìn)制字符串(單色位圖取模)

2018-11-27日更新:
??由于沒有找到生成.bmp格式圖片的好辦法,改為使用.wbmp格式,轉(zhuǎn)換和讀取都改為.wbmp格式,原來的bmp2hex函數(shù)邏輯沒有變化,改名為wbmp2hex,并不再使用ImageCreateFromBMP函數(shù),可以收藏一下這個函數(shù)還是有用的,最新的代碼我也提供了下載在文章末尾

準(zhǔn)備階段:

  • pctoLCD2002
    網(wǎng)上找到的一款取模軟件,可以讀取.bmp圖片并生成字模,當(dāng)然我們還是要用代碼來完成,這個只是起到了一個對照作用,我將它放在了我的網(wǎng)盤下供大家下載
    鏈接:點我下載pctoLCD2002 密碼:2lyl

  • PHP GD擴(kuò)展
    強(qiáng)大的PHP圖像生成和處理擴(kuò)展

  • Windows自帶畫圖工具
    主要用來生成.bpm格式的圖片,目前我還沒有找到好的用PHP將.jpg.png圖片轉(zhuǎn)為單色.bmp格式圖片的辦法,暫時只好用畫圖工具來生成

操作步驟分解演示

一. 使用畫圖工具獲得.bmp格式圖片
  1. 使用畫圖工具打開一張事先準(zhǔn)備好的圖片,另存為.bmp單色位圖,這樣我們就得到了一張.bmp格式的圖片,白色背景,只有黑色
    打開圖片

    另存為

    .bmp格式
  2. 或者我們自己動手來畫一張,打開畫圖工具,調(diào)整畫布大小為你需要的尺寸,示例為100*70像素,取消勾選保持縱橫比,調(diào)整好后點擊確定,然后我們可以用刷子隨便畫些什么在畫布上,你喜歡就好,然后重復(fù)前面的另存為.bmp單色位圖步驟
    image.png
  3. pctoLCD2002也可以新建一幅.bmp圖片,并且非常簡單


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

找到PCtoLCD2002.exe并雙擊打開

1. 規(guī)則解析,及本文配置項參考

在取字模之前我們先來說下PCtoLCD2002設(shè)置項和取模規(guī)則

  • 配置信息:

    pctoLCD2002設(shè)置項

  • 取模說明:
    a. 逐行式逐列式:顧名思義就是讀取每張圖片時取點時是逐行還是逐列的
    b. 取模走向:
    逆向:從低位到高位
    順向:從高位到低位
    舉例:
    *星號代表圖中非空白的像素點,_代表空白的像素點,取八位為一個字節(jié)
    * _ _ _ _ _ _ _代表一個字節(jié)(為了方便查看,每個符號鍵我加入了一個空格,實際是沒有的)
    逆向即是從后往前寫,表示為00000001
    順向即是從前往后寫,表示為10000000
    c. 輸出數(shù)制:
    這里選擇十六進(jìn)制,因需選擇,不夠我需要的是十六進(jìn)制,后面的代碼也只有十六進(jìn)制的

  • 本文取模規(guī)則:
    逐行式 順向 十六進(jìn)制
    從第一行開始,每行每隔8個像素點為一個字節(jié),每行結(jié)尾最后不足8位,用0補(bǔ)滿

2. 生成字模

設(shè)置好規(guī)則后,打開之前制作的.bmp圖片,點擊生成字模,這時下方會生成出十六進(jìn)制串,如圖:


image.png

但是這還不是我最后想要的格式,需要處理一下:

  • 去掉開始處和結(jié)束處的文件路徑
  • 去掉所有的標(biāo)點符號,'{' '}'
  • 去掉十六進(jìn)制的標(biāo)識部分,所有的0x

最后得到一串連貫的字符串,類似:

0000000000000007F8000000000000000000000000003FFC00000000000000000000000000000003FF80000000

這就是我們最終需要的部分了!下面我們用代碼來實現(xiàn)這個功能:

問題解決:

一. 實現(xiàn)過程及思路
0. 生成單色位圖

卡在這里好久,鉆進(jìn)了死胡同,其實.wbmp的圖片完全符合我的要求:
GD庫就可以將jpg/png轉(zhuǎn)換成wbmp格式,使用時可以調(diào)節(jié)threshold參數(shù),解釋如下,我理解為精度不知道準(zhǔn)不準(zhǔn)確,也沒有查到閥值到底是什么意思...

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時,和給的軟件轉(zhuǎn)換后結(jié)果完全一致

圖片樣式:


.wbmp格式
1. 讀取.wbmp格式圖片(原讀取.bmp格式)

使用gd庫的imagecreatefromwbmp函數(shù):

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

安裝好GD庫擴(kuò)展后,我發(fā)現(xiàn)gd庫只能讀取.wbmp文件,并不支持.bmp文件,我的gd庫版本信息如下gd_info(),經(jīng)過一番google,找到了一個可以使用的讀取.bmp的函數(shù)ImageCreateFromBMP(),感謝前輩

gd_info()

2. 逐個像素讀取

可以讀取.wbmp格式了,我們該如何能得出每個像素的顏色值呢?
通過查看gd庫文檔過程,我發(fā)現(xiàn)一個函數(shù)imagecolorat(),可以根據(jù)傳入的位置,獲取每個像素的索引值
使用示例:

        // 取得一點的顏色
        $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庫中有獲取圖片寬高的函數(shù)imagesx()imagesy(),代碼示例:

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

到這里,最主要的部分都已經(jīng)可以獲取到了,后面就是邏輯部分了(代碼中可以看到具體實現(xiàn)方式):

  • 根據(jù)圖片的寬高,逐行逐點讀取每個像素的值,每8位組合成1個字節(jié),然后取模,再轉(zhuǎn)為16進(jìn)制
  • 檢測每一行的最后是否滿足8個像素,不足則用0補(bǔ)滿
  • 最終將每一行組合到一起,組成16進(jìn)制字符串
二. 源碼

整個過程可以大概分為三步完成,你可以根據(jù)自己的需求參考或者直接copy使用,如果對你有幫助,希望可以點個贊,轉(zhuǎn)載請注明本篇文章鏈接地址及作者,謝謝!

  • 將普通jpg/png格式轉(zhuǎn)為.wbmp
        $filename = 'static/img/1.jpg';
        $path = 'static/img/11.wbmp';
        $image = getimagesize($filename);
        jpeg2wbmp($filename, $path, $image[1], $image[0], 5);//threshold == 5時,和給的軟件轉(zhuǎn)換后結(jié)果完全一致
  • 讀取.wbmp格式圖片
        $filename = 'static/img/11.wbmp';
        $im = imagecreatefromwbmp($filename);
  • .wbmp轉(zhuǎn)為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位轉(zhuǎn)換1次
                    $hex = (string)dechex(bindec($binary_str));
                    $hex = strlen($hex) == 1 ? '0' . $hex : $hex;//補(bǔ)0
                    $hex_str .= $hex;
                    $binary_str = '';
                }
            }

            //這時如果$binary_str不為空,說明需要向后補(bǔ)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;//補(bǔ)0
                $hex_str .= $hex;
            }
        }

        $hex_str = strtoupper($hex_str);//轉(zhuǎn)為大寫
        return $hex_str;
    }

最后附上讀取.bmp格式圖片的函數(shù):

<?php

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

class Image_api
{
    function __construct (){

    }

    /**
     * ImageCreateFromBMP函數(shù),讀取bmp格式圖片
     * 注:phpGD擴(kuò)展中沒有ImageCreateFromBMP函數(shù),只有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;
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容