Question

In PHP, a simple read and write file can be done by using fread() and fwrite(). The unpack() and pack() operator are used to extract binary information.

The question is, how can I read and write PGM (P5) image in PHP without using any additional PHP extension / library?

Was it helpful?

Solution

Depending on maximum value for gray you will have to use C* for values up to 255 and n* for greater values.

Sample class to read / write all pixel from array:

class PGM{
    public
        $magicNumber = '',
        $pixelArray = array(),
        $width = 0,
        $height = 0,
        $grayMax = 0,
        $createdBy = '';

    public function loadPGM($filename){
        $this->grayMax = $this->height = $this->width = $this->magicNumber = 0;
        $this->pixelArray = array();
        $fh = fopen($filename, 'rb');
        while($line = fgets($fh)){
            if(strpos($line, '#') === 0){
                continue;
            }
            if(!$this->grayMax){
                $arr = preg_split('/\s+/', trim($line));
                if($arr && !$this->magicNumber){
                    $this->magicNumber = array_shift($arr);
                    if($this->magicNumber != 'P5'){
                        throw new Exception("Unsupported PGM version");
                    }
                }
                if($arr && !$this->width)
                    $this->width = array_shift($arr);
                if($arr && !$this->height)
                    $this->height = array_shift($arr);
                if($arr && !$this->grayMax)
                    $this->grayMax = array_shift($arr);
            }else{
                $unpackMethod = ($this->grayMax > 255)?'n*':'C*';
                foreach(unpack($unpackMethod, $line) as $pixel){
                    $this->pixelArray[] = $pixel;
                }
            }
        }
        fclose($fh);
    }

    public function savePGM($filename){
        $fh = fopen($filename, 'wb');
        $this->magicNumber = 'P5';
        fwrite($fh, "{$this->magicNumber}\n");
        if($this->createdBy){
            fwrite($fh, "# {$this->createdBy}\n");
        }
        fwrite($fh, "{$this->width} {$this->height}\n");
        fwrite($fh, "{$this->grayMax}\n");
        $packMethod = ($this->grayMax > 255)?'n*':'C*';
        fwrite($fh, call_user_func_array('pack', array_merge(array($packMethod), $this->pixelArray)));  
        fclose($fh);
    }
}

Test case for your file:

$pgm = new PGM;
$pgm->loadPGM('a.pgm');
$pgm->createdBy = 'Created by IrfanView';
$pgm->savePGM('b.pgm');
echo (md5_file('a.pgm') == md5_file('b.pgm'))?'Images are identical':'Images are different';

OTHER TIPS

According to the spec: Portable Gray Map

This looks pretty simple.

You don't even need pack/unpack as sscanf() should cope with most of the ASCII to integer translation.

This C program which converts PGM to BMP may give you a few pointers.

I wrote a simple function to convert an image resource to PGM (portable graymap) in order to feed it to an OCR program. It works just like the rest of the image output functions, and will convert to grayscale for you:

<?php
     function imagepgm($image, $filename = null)
    {
        $pgm = "P5 ".imagesx($image)." ".imagesy($image)." 255\n";
        for($y = 0; $y < imagesy($image); $y++)
        {
            for($x = 0; $x < imagesx($image); $x++)
            {
                $colors = imagecolorsforindex($image, imagecolorat($image, $x, $y));
                $pgm .= chr(0.3 * $colors["red"] + 0.59 * $colors["green"] + 0.11 * $colors["blue"]);
            }
        }
        if($filename != null)
        {
            $fp = fopen($filename, "w");
            fwrite($fp, $pgm);
            fclose($fp);
        }
        else
        {
            return $pgm;
        }
    }
?>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top