Task
This task is the ''opposite'' of the [[PPM conversion through a pipe]]. In this task, using a delegate tool (like '''cjpeg''', one of the netpbm package, or '''convert''' of the ImageMagick package) we read an image file and load it into the data storage type [[Basic bitmap storage|defined here]]. We can also use the code from [[Read ppm file]], so that we can use PPM format like a (natural) bridge between the foreign image format and our simple data storage.
AutoHotkey
Uses [http://www.autohotkey.com/forum/viewtopic.php?t=16823 StdoutTovar.ahk]
ppm := Run("cmd.exe /c convert lena50.jpg ppm:-")
; pipe in from imagemagick
img := ppm_read("", ppm) ;
x := img[4,4] ; get pixel(4,4)
y := img[24,24] ; get pixel(24,24)
msgbox % x.rgb() " " y.rgb()
img.write("lena50copy.ppm")
return
ppm_read(filename, ppmo=0) ; only ppm6 files supported
{
if !ppmo ; if image not already in memory, read from filename
fileread, ppmo, % filename
index := 1
pos := 1
loop, parse, ppmo, `n, `r
{
if (substr(A_LoopField, 1, 1) == "#")
continue
loop,
{
if !pos := regexmatch(ppmo, "\d+", pixel, pos)
break
bitmap%A_Index% := pixel
if (index == 4)
Break
pos := regexmatch(ppmo, "\s", x, pos)
index ++
}
}
type := bitmap1
width := bitmap2
height := bitmap3
maxcolor := bitmap4
bitmap := Bitmap(width, height, color(0,0,0))
index := 1
i := 1
j := 1
bits := pos
loop % width * height
{
bitmap[i, j, "r"] := numget(ppmo, 3 * A_Index + bits, "uchar")
bitmap[i, j, "g"] := numget(ppmo, 3 * A_Index + bits + 1, "uchar")
bitmap[i, j, "b"] := numget(ppmo, 3 * A_Index + bits + 2, "uchar")
if (j == width)
{
j := 1
i += 1
}
else
j++
}
return bitmap
}
#include bitmap_storage.ahk ; from http://rosettacode.org/wiki/Basic_bitmap_storage/AutoHotkey
#include run.ahk ; http://www.autohotkey.com/forum/viewtopic.php?t=16823
C
Here I've used '''convert''' by ImageMagick. It is up to the program to ''understand'' the source file type; in this way, we can read theoretically any image format ImageMagick can handle. The get_ppm function defined in [[Read ppm file]] is used.
image read_image(const char *name);
#include "imglib.h"
#define MAXCMDBUF 100
#define MAXFILENAMELEN 256
#define MAXFULLCMDBUF (MAXCMDBUF + MAXFILENAMELEN)
image read_image(const char *name)
{
FILE *pipe;
char buf[MAXFULLCMDBUF];
image im;
FILE *test = fopen(name, "r");
if ( test == NULL ) {
fprintf(stderr, "cannot open file %s\n", name);
return NULL;
}
fclose(test);
snprintf(buf, MAXFULLCMDBUF, "convert \"%s\" ppm:-", name);
pipe = popen(buf, "r");
if ( pipe != NULL )
{
im = get_ppm(pipe);
pclose(pipe);
return im;
}
return NULL;
}
Go
This example uses convert to convert the test image for the flood fill task. It reads through the pipe as required for this task, then writes as a .ppm file convenient for the flood fill task.
package main
// Files required to build supporting package raster are found in:
// * Bitmap
// * Read a PPM file
// * Write a PPM file
import (
"log"
"os/exec"
"raster"
)
func main() {
c := exec.Command("convert", "Unfilledcirc.png", "-depth", "1", "ppm:-")
pipe, err := c.StdoutPipe()
if err != nil {
log.Fatal(err)
}
if err = c.Start(); err != nil {
log.Fatal(err)
}
b, err := raster.ReadPpmFrom(pipe)
if err != nil {
log.Fatal(err)
}
if err = b.WritePpmFile("Unfilledcirc.ppm"); err != nil {
log.Fatal(err)
}
}
Julia
using Images, FileIO
img = load("data/bitmapOutputTest.jpg")
save("data/bitmapOutputTest.ppm", img)
Kotlin
The code for this is similar to that for the [[Bitmap/Read a PPM file]] task except that the .jpg file is converted via a pipe to .ppm format using the ImageMagick 'convert' tool and stored in a BasicBitmapStorage object. It is then converted to grayscale and saved back to disk as a .jpg file.
// Version 1.2.40
import java.awt.Color
import java.awt.Graphics
import java.awt.image.BufferedImage
import java.io.PushbackInputStream
import java.io.File
import javax.imageio.ImageIO
class BasicBitmapStorage(width: Int, height: Int) {
val image = BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR)
fun fill(c: Color) {
val g = image.graphics
g.color = c
g.fillRect(0, 0, image.width, image.height)
}
fun setPixel(x: Int, y: Int, c: Color) = image.setRGB(x, y, c.getRGB())
fun getPixel(x: Int, y: Int) = Color(image.getRGB(x, y))
fun toGrayScale() {
for (x in 0 until image.width) {
for (y in 0 until image.height) {
var rgb = image.getRGB(x, y)
val red = (rgb shr 16) and 0xFF
val green = (rgb shr 8) and 0xFF
val blue = rgb and 0xFF
val lumin = (0.2126 * red + 0.7152 * green + 0.0722 * blue).toInt()
rgb = (lumin shl 16) or (lumin shl 8) or lumin
image.setRGB(x, y, rgb)
}
}
}
}
fun PushbackInputStream.skipComment() {
while (read().toChar() != '\n') {}
}
fun PushbackInputStream.skipComment(buffer: ByteArray) {
var nl: Int
while (true) {
nl = buffer.indexOf(10) // look for newline at end of comment
if (nl != -1) break
read(buffer) // read another buffer full if newline not yet found
}
val len = buffer.size
if (nl < len - 1) unread(buffer, nl + 1, len - nl - 1)
}
fun Byte.toUInt() = if (this < 0) 256 + this else this.toInt()
fun main(args: Array<String>) {
// use file, output_piped.jpg, created in the
// Bitmap/PPM conversion through a pipe task
val pb = ProcessBuilder("convert", "output_piped.jpg", "ppm:-")
pb.directory(null)
pb.redirectOutput(ProcessBuilder.Redirect.PIPE)
val proc = pb.start()
val pStdOut = proc.inputStream
val pbis = PushbackInputStream(pStdOut, 80)
pbis.use {
with (it) {
val h1 = read().toChar()
val h2 = read().toChar()
val h3 = read().toChar()
if (h1 != 'P' || h2 != '6' || h3 != '\n') {
println("Not a P6 PPM file")
System.exit(1)
}
val sb = StringBuilder()
while (true) {
val r = read().toChar()
if (r == '#') { skipComment(); continue }
if (r == ' ') break // read until space reached
sb.append(r.toChar())
}
val width = sb.toString().toInt()
sb.setLength(0)
while (true) {
val r = read().toChar()
if (r == '#') { skipComment(); continue }
if (r == '\n') break // read until new line reached
sb.append(r.toChar())
}
val height = sb.toString().toInt()
sb.setLength(0)
while (true) {
val r = read().toChar()
if (r == '#') { skipComment(); continue }
if (r == '\n') break // read until new line reached
sb.append(r.toChar())
}
val maxCol = sb.toString().toInt()
if (maxCol !in 0..255) {
println("Maximum color value is outside the range 0..255")
System.exit(1)
}
var buffer = ByteArray(80)
// get rid of any more opening comments before reading data
while (true) {
read(buffer)
if (buffer[0].toChar() == '#') {
skipComment(buffer)
}
else {
unread(buffer)
break
}
}
// read data
val bbs = BasicBitmapStorage(width, height)
buffer = ByteArray(width * 3)
var y = 0
while (y < height) {
read(buffer)
for (x in 0 until width) {
val c = Color(
buffer[x * 3].toUInt(),
buffer[x * 3 + 1].toUInt(),
buffer[x * 3 + 2].toUInt()
)
bbs.setPixel(x, y, c)
}
y++
}
// convert to grayscale and save to a file
bbs.toGrayScale()
val grayFile = File("output_piped_gray.jpg")
ImageIO.write(bbs.image, "jpg", grayFile)
}
}
}
Mathematica
Based off the Julia program.
Export["data/bitmapOutputTest.ppm",Import["data/bitmapOutputTest.jpg"]];
OCaml
The read_ppm
function of the page [[read ppm file]] and used by the code below would need to be changed to take as parameter an input channel instead of the filename.
let read_image ~filename =
if not(Sys.file_exists filename)
then failwith(Printf.sprintf "the file %s does not exist" filename);
let cmd = Printf.sprintf "convert \"%s\" ppm:-" filename in
let ic, oc = Unix.open_process cmd in
let img = read_ppm ~ic in
(img)
;;
Perl 6
Uses pieces from [[Bitmap#Perl_6| Bitmap]] and [[Bitmap/Read_a_PPM_file#Perl_6| Read a PPM file]] tasks. Included here to make a complete, runnable program.
Uses imagemagick convert to pipe the image in.
class Pixel { has UInt ($.R, $.G, $.B) }
class Bitmap {
has UInt ($.width, $.height);
has Pixel @.data;
}
role PPM {
method P6 returns Blob {
"P6\n{self.width} {self.height}\n255\n".encode('ascii')
~ Blob.new: flat map { .R, .G, .B }, self.data
}
}
sub getline ( $proc ) {
my $line = '#'; # skip comment when reading a .png
$line = $proc.out.get while $line.substr(0,1) eq '#';
$line;
}
my $filename = './camelia.png';
my $proc = run 'convert', $filename, 'ppm:-', :enc('ISO-8859-1'), :out;
my $type = getline($proc);
my ($width, $height) = getline($proc).split: ' ';
my $depth = getline($proc);
my Bitmap $b = Bitmap.new( width => $width.Int, height => $height.Int) but PPM;
$b.data = $proc.out.slurp.ords.rotor(3).map:
{ Pixel.new(R => .[0], G => .[1], B => .[2]) };
'./camelia.ppm'.IO.open(:bin, :w).write: $b.P6;
See [https://github.com/thundergnat/rc/blob/master/img/camelia.png camelia image here].
PicoLisp
(setq *Ppm (ppmRead '("convert" "img.jpg" "ppm:-")))
Python
"""
Adapted from https://stackoverflow.com/questions/26937143/ppm-to-jpeg-jpg-conversion-for-python-3-4-1
Requires pillow-5.3.0 with Python 3.7.1 32-bit on Windows.
Sample ppm graphics files from http://www.cs.cornell.edu/courses/cs664/2003fa/images/
"""
from PIL import Image
# boxes_1.jpg is the jpg version of boxes_1.ppm
im = Image.open("boxes_1.jpg")
im.save("boxes_1v2.ppm")
Does not need to pipe through a conversion utility because the Pillow module does the conversion.
Racket
(define (read-ppm port)
(parameterize ([current-input-port port])
(define magic (read-line))
(match-define (list w h) (string-split (read-line) " "))
(define width (string->number w))
(define height (string->number h))
(define maxcol (string->number (read-line)))
(define bm (make-object bitmap% width height))
(define dc (new bitmap-dc% [bitmap bm]))
(send dc set-smoothing 'unsmoothed)
(define (adjust v) (* 255 (/ v maxcol)))
(for/list ([x width])
(for/list ([y height])
(define red (read-byte))
(define green (read-byte))
(define blue (read-byte))
(define color (make-object color% (adjust red) (adjust green) (adjust blue)))
(send dc set-pen color 1 'solid)
(send dc draw-point x y)))
bm))
(define (image->bmp filename)
(define command (format "convert ~a ppm:-" filename))
(match-define (list in out pid err ctrl) (process command))
(define bmp (read-ppm in))
(close-input-port in)
(close-output-port out)
bmp)
(image->bmp "input.jpg")
Ruby
Extending [[Read ppm file#Ruby]] and [[PPM conversion through a pipe#Ruby]]. Uses the ImageMagick convert
tool.
class Pixmap
def self.read_ppm(ios)
format = ios.gets.chomp
width, height = ios.gets.chomp.split.map {|n| n.to_i }
max_colour = ios.gets.chomp
if (not PIXMAP_FORMATS.include?(format)) or
width < 1 or height < 1 or
max_colour != '255'
then
ios.close
raise StandardError, "file '#{filename}' does not start with the expected header"
end
ios.binmode if PIXMAP_BINARY_FORMATS.include?(format)
bitmap = self.new(width, height)
height.times do |y|
width.times do |x|
# read 3 bytes
red, green, blue = case format
when 'P3' then ios.gets.chomp.split
when 'P6' then ios.read(3).unpack('C3')
end
bitmap[x,y] = RGBColour.new(red, green, blue)
end
end
ios.close
bitmap
end
def self.open(filename)
read_ppm(File.open(filename, 'r'))
end
def self.open_from_jpeg(filename)
read_ppm(IO.popen("convert jpg:#{filename} ppm:-", 'r'))
end
end
bitmap = Pixmap.open_from_jpeg('file.jpg')
Tcl
package require Tk
proc magickalReadImage {bufferImage fileName} {
set f [open |[list convert [file normalize $fileName] ppm:-] "rb"]
try {
$bufferImage put [read $f] -format ppm
} finally {
close $f
}
}
zkl
Uses the PPM class from http://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#zkl
Using the convert utility by ImageMagick:
p:=System.popen(0'|convert "fractalTree.jpg" ppm:-|,"r");
img:=PPM.readPPM(p); p.close();
{{omit from|TI-83 BASIC}} {{omit from|TI-89 BASIC}}