图像格式PPMPGMPBM剖析及代码实现

 1 E币 
成为会员,免费下载资料
文件大小:182.5 KB 上传者:A-小鲸鱼 时间:2020-02-13 14:56:23 下载量:10

可移植像素图格式 PPM,灰度图格式 PGM,位图格式 PBM 的介绍 简介

可移植像素图格式(PPM),可移植灰度图格式(PGM)和可移植位图格式(PBM)是便于跨平台的图像格式。有时候也被统称为 PNM 格式

文件格式描述

这三种格式其实是一样的描述方法,只不过 PBM 是单色,PGM 是灰度图,PPM 使用 RGB 颜色。
每个文件的开头两个字节(ASCII 码)作为文件描述符,指出具体格式和编码形式。

TypeMagic numberExtensionColors
ASCIIBinary
Portable BitMapP1P4.pbm0–1 (white & black)
Portable GrayMapP2P5.pgm0–255 (gray scale)
Portable PixMapP3P6.ppm0–255 (RGB)
Portable Arbitrary Map
P7.pam0–255 (RGB_ALPHA)

格式例子 PBM

注意每行结束有换行符

P1
# This is an example bitmap of the letter "J"
6 10
0 0 0 0 1 0
0 0 0 0 1 0
0 0 0 0 1 0
0 0 0 0 1 0
0 0 0 0 1 0
0 0 0 0 1 0
1 0 0 0 1 0
0 1 1 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0

上面的图像是一个J

PGM

P2
# Shows the word "FEEP" (example from Netpbm man page on PGM)
24 7
15
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
0  3  3  3  3  0  0  7  7  7  7  0  0 11 11 11 11  0  0 15 15 15 15  0
0  3  0  0  0  0  0  7  0  0  0  0  0 11  0  0  0  0  0 15  0  0 15  0
0  3  3  3  0  0  0  7  7  7  0  0  0 11 11 11  0  0  0 15 15 15 15  0
0  3  0  0  0  0  0  7  0  0  0  0  0 11  0  0  0  0  0 15  0  0  0  0
0  3  0  0  0  0  0  7  7  7  7  0  0 11 11 11 11  0  0 15  0  0  0  0
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

上面的图像是
rame id="frameimg0.6304309008711702" frameborder="0" scrolling="no" width="100%" height="220px" style="margin: 0px; padding: 0px;">rame>

PPM

P3
3 2
255
# The part above is the header
# "P3" means this is a RGB color image in ASCII
# "3 2" is the width and height of the image in pixels
# "255" is the maximum value for each color
# The part below is image data: RGB triplets
255   0   0     0 255   0     0   0 255
255 255   0   255 255 255     0   0   0

把上面六个像素放大后显示如下
rame id="frameimg0.9168214516251931" frameborder="0" scrolling="no" width="100%" height="504px" style="margin: 0px; padding: 0px;">rame>

PAM

P7
WIDTH 4
HEIGHT 2
DEPTH 4
MAXVAL 255
TUPLTYPE RGB_ALPHA
ENDHDR
0000FFFF 00FF00FF FF0000FF FFFFFFFF
0000FF7F 00FF007F FF00007F FFFFFF7F

上面数据放大显示如下:
rame id="frameimg0.4605669245523565" frameborder="0" scrolling="no" width="100%" height="477px" style="margin: 0px; padding: 0px;">rame>

用go生成PPM文件

下面是简单的ppm包

package ppm
 
import (
    "fmt"
    "io"
    "os"
)

// WriteTo outputs 8-bit P6 PPM format to an io.Writer.
func (b *Bitmap) WritePpmTo(w io.Writer) (err error) {
    // magic number
    if _, err = fmt.Fprintln(w, "P6"); err != nil {
        return
    }

    // comments
    for _, c := range b.Comments {
        if _, err = fmt.Fprintln(w, c); err != nil {
            return
        }
    }

    // x, y, depth
    _, err = fmt.Fprintf(w, "%d %d\n255\n", b.cols, b.rows)
    if err != nil {
        return
    }

    // raster data in a single write
    b3 := make([]byte, 3*len(b.px))
    n1 := 0
    for _, px := range b.px {
        b3[n1] = px.R
        b3[n1+1] = px.G
        b3[n1+2] = px.B
        n1 += 3
    }
    if _, err = w.Write(b3); err != nil {
        return
    }
    return
}

// WriteFile writes to the specified filename.
func (b *Bitmap) WritePpmFile(fn string) (err error) {
    var f *os.File
    if f, err = os.Create(fn); err != nil {
        return
    }
    if err = b.WritePpmTo(f); err != nil {
        return
    }
    return f.Close()
}

下面是生成ppm的程序

package main

// Files required to build supporting package raster are found in:
// * This task (immediately above)
// * Bitmap task

import (
    "raster"
    "fmt"
)

func main() {
    b := raster.NewBitmap(400, 300)
    b.FillRgb(0x240008) // a dark red
    err := b.WritePpmFile("write.ppm")
    if err != nil {
        fmt.Println(err)
    }
}

用C生成PPM文件

#include <stdio.h>

int main()
{
  const char *filename = "n.pgm";
  int x, y;
  
  const int x_max = 100;  
  const int y_max = 100;  
  
  unsigned char data[y_max][x_max];
  
  const int MaxColorComponentValue = 255;
  FILE * fp;
  
  const char *comment = "# this is my new binary pgm file";

  
  for (y = 0; y < y_max; ++y) {
    for (x = 0; x < x_max; ++x) {
      data[y][x] = (x + y) & 255;
    }
  }

  
  
  fp = fopen(filename, "wb");
  
  fprintf(fp, "P5\n %s\n %d\n %d\n %d\n", comment, x_max, y_max,
          MaxColorComponentValue);
  
  fwrite(data, sizeof(data), 1, fp);
  fclose(fp);
  printf("OK - file %s saved\n", filename);
  return 0;
}

或者

imglib.h

#ifndef _IMGLIB_0
#define _IMGLIB_0
 
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <math.h>
#include <sys/queue.h>
 
typedef unsigned char color_component;
typedef color_component pixel[3];
typedef struct {
    unsigned int width;
    unsigned int height;
    pixel * buf;
} image_t;
typedef image_t * image;
 
image alloc_img(unsigned int width, unsigned int height);
void free_img(image);
void fill_img(image img,
        color_component r,
        color_component g,
        color_component b );
void put_pixel_unsafe(
           image img,
        unsigned int x,
        unsigned int y,
        color_component r,
        color_component g,
        color_component b );
void put_pixel_clip(
           image img,
        unsigned int x,
        unsigned int y,
        color_component r,
        color_component g,
        color_component b );
#define GET_PIXEL(IMG, X, Y) (IMG->buf[ ((Y) * IMG->width + (X)) ])
#endif

imglib.c

image alloc_img(unsigned int width, unsigned int height)
{
    image img;
    img = malloc(sizeof(image_t));
    img->buf = malloc(width * height * sizeof(pixel));
    img->width = width;
    img->height = height;
    return img;
}
 
void free_img(image img)
{
    free(img->buf);
    free(img);
}
 
void fill_img(
        image img,
        color_component r,
        color_component g,
        color_component b )
{
    unsigned int i, n;
    n = img->width * img->height;
    for (i=0; i < n; ++i)
    {
        img->buf[i][0] = r;
        img->buf[i][1] = g;
        img->buf[i][2] = b;
    }
}
 
void put_pixel_unsafe(
           image img,
        unsigned int x,
        unsigned int y,
        color_component r,
        color_component g,
        color_component b )
{
    unsigned int ofs;
    ofs = (y * img->width) + x;
    img->buf[ofs][0] = r;
    img->buf[ofs][1] = g;
    img->buf[ofs][2] = b;
}
 
void put_pixel_clip(
           image img,
        unsigned int x,
        unsigned int y,
        color_component r,
        color_component g,
        color_component b )
{
    if (x < img->width && y < img->height)
      put_pixel_unsafe(img, x, y, r, g, b);
}

output_ppm

#include "imglib.h"
 
void output_ppm(FILE *fd, image img)
{
  unsigned int n;
  (void) fprintf(fd, "P6\n%d %d\n255\n", img->width, img->height);
  n = img->width * img->height;
  (void) fwrite(img->buf, sizeof(pixel), n, fd);
  (void) fflush(fd);
}

生成raw rgb格式

其实raw rgb和ppm数据是一样的,我们生成出300帧的ppm数据,然后送给ffplayer(ffmpeg)来播放
下面是我的go代码:

//
// Created by         :  Harris Zhu
// Filename           :  genrgb.go
// Author             :  Harris Zhu
// Created On         :  2018-09-14 02:13:11
// Last Modified      :  2018-09-14 02:13:11
// Update Count       :  1
// Tags               :
// Description        :
// Conclusion         :
//
//=======================================================================

package main

import (
    "bufio"
    "os"
    "strconv"
    "time"

    "github.com/urfave/cli"
)

func main() {
    app := cli.NewApp()
    app.Name = "genrgb"
    app.Version = "1.0.0"
    app.Compiled = time.Now()
    app.Authors = []cli.Author{
        cli.Author{
            Name:  "Harris Zhu",
            Email: "zhuzhzh@163.com",
        },
    }
    app.Usage = "svpaser <sv file>"
    name := "rgb.data"
    w := 600
    h := 480
    f := 20
    app.Action = func(c *cli.Context) error {
        name = c.Args().Get(0)
        w, _ = strconv.Atoi(c.Args().Get(1))
        h, _ = strconv.Atoi(c.Args().Get(2))
        f, _ = strconv.Atoi(c.Args().Get(3))

        genrgb(name, w, h, f)

        return nil
    }
    app.Run(os.Args)
}

func genrgb(filepath string, w int, h int, f int) {
    fout, err := os.Create(filepath)
    defer fout.Close()
    if err != nil {
        panic(err)
    }
    bufout := bufio.NewWriter(fout)
//    bufout.WriteString("P6\n # this is my ppm file\n")
//    bufout.WriteString(strconv.Itoa(w))
//    bufout.WriteString("\n")
//    bufout.WriteString(strconv.Itoa(h))
//    bufout.WriteString("\n")
//    bufout.WriteString(strconv.Itoa(255))
//    bufout.WriteString("\n")
    r := []byte{255, 0, 0}
    g := []byte{0, 255, 0}
    b := []byte{0, 0, 255}
    dcnt := 0
    //k: frame
    //i: height column
    //j: width line
    for k := 0; k < f; k++ {
        for i := 0; i < h; i++ {
            //fmt.Println("hline: ", i)
            for j := 0; j < w; j++ {
                if k%3 == 0 {
                    bufout.Write(r)
                    dcnt++
                } else if k%3 == 1 {
                    bufout.Write(g)
                    dcnt++
                } else if k%3 == 2 {
                    bufout.Write(b)
                    dcnt++
                }
                //fmt.Println("vline: ", j)
            }
        }
        bufout.Flush()
        //fmt.Printf("total pixels = %d\n", dcnt)
    }
}

下面是makefile:

FILE = genrgb
GENFILE = rgb.data

b build:
    go build  -gcflags "-N -l" $(FILE).go

g gen:
    ./$(FILE) $(GENFILE) 60 40 300

p play:
    cat $(GENFILE) | ffplay -i pipe:0 -f rawvideo -pix_fmt rgb24 -video_size 60x40

依次执行make b; make g; make p可以看到下面的播放视频

rame id="frameimg0.13861476547951157" frameborder="0" scrolling="no" width="100%" height="951px" style="margin: 0px; padding: 0px;">rame>

它交替显示红绿蓝

总结

RGB比较直观的图像显示方法,但相对YUV来说比较占空间,每个像素占用3个byte, 像1080P的图像就要占用$1920*1080*3=6,220,800byte$, 但它用于作为图像和显示入门是非常好的。能够让你很快就常会编程显示图像。


展开
折叠
887
评论
共 0 个
内容存在敏感词
    易百纳技术社区暂无数据
相关资料
关于作者
易百纳技术社区
A-小鲸鱼
贡献资料 1241
易百纳技术社区 我上传的资料
登录查看
我赚取的积分
登录查看
我赚取的收益
登录查看
上传资料 赚取积分兑换E币
易百纳技术社区
删除原因
广告/SPAM
恶意灌水
违规内容
文不对题
重复发帖
置顶时间设置
结束时间
举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-资料模块

审核失败

失败原因
备注
易百纳技术社区