Bitmap/Read an image through a pipe: Difference between revisions

From Rosetta Code
Content added Content deleted
(Added Wren)
Line 533: Line 533:
}
}
}</lang>
}</lang>

=={{header|Wren}}==
{{libheader|DOME}}
As DOME doesn't have a method for calling an external process (''ImageMagick'' in this case), we re-use the small plug-in (''pipeconv.so'') we created in the 'PPM conversion through a pipe' task
to add this functionality.

We can now use this plug-in in the following script which calls ''ImageMagick'' to convert the ''output_piped.jpg'' file to a ''ppm'' file so that we can load the latter, convert it to a gray scale image, display it and save it to a .jpg file.

<lang ecmascript>import "graphics" for Canvas, ImageData, Color
import "dome" for Window, Process
import "io" for FileSystem
import "plugin" for Plugin

Plugin.load("pipeconv")

import "pipeconv" for PipeConv

class Bitmap {
construct new(fileName, fileName2, fileName3, width, height) {
Window.title = "Bitmap - read image via pipe"
Window.resize(width, height)
Canvas.resize(width, height)
_w = width
_h = height
_fn3 = fileName3
// convert .jpg file to .ppm via a pipe
PipeConv.convert(fileName, fileName2)
// load the .ppm file
loadPPMFile(fileName2)
}

init() {
toGrayScale()
// display gray scale image
_bmp2.draw(0, 0)
// save it to file
_bmp2.saveToFile(_fn3)
}

loadPPMFile(fileName) {
var ppm = FileSystem.load(fileName)
if (ppm[0..1] != "P6") {
System.print("The loaded file is not a P6 file.")
Process.exit()
}
var lines = ppm.split("\n")
if (Num.fromString(lines[2]) > 255) {
System.print("The maximum color value can't exceed 255.")
Process.exit()
}
var wh = lines[1].split(" ")
var w = Num.fromString(wh[0])
var h = Num.fromString(wh[1])
_bmp = ImageData.create(fileName, w, h)
var bytes = ppm.bytes
var i = bytes.count - 3 * w * h
for (y in 0...h) {
for (x in 0...w) {
var r = bytes[i]
var g = bytes[i+1]
var b = bytes[i+2]
var c = Color.rgb(r, g, b)
pset(x, y, c)
i = i + 3
}
}
}

toGrayScale() {
_bmp2 = ImageData.create("gray scale", _bmp.width, _bmp.height)
for (x in 0..._bmp.width) {
for (y in 0..._bmp.height) {
var c1 = _bmp.pget(x, y)
var lumin = (0.2126 * c1.r + 0.7152 * c1.g + 0.0722 * c1.b).floor
var c2 = Color.rgb(lumin, lumin,lumin, c1.a)
_bmp2.pset(x, y, c2)
}
}
}

pset(x, y, col) { _bmp.pset(x, y, col) }

pget(x, y) { _bmp.pget(x, y) }

update() {}

draw(alpha) {}
}

var Game = Bitmap.new("output_piped.jpg", "output_piped.ppm", "output_piped_gs.jpg", 350, 350)</lang>


=={{header|zkl}}==
=={{header|zkl}}==

Revision as of 13:55, 18 October 2021

Task
Bitmap/Read an image through a pipe
You are encouraged to solve this task according to the task description, using any language you may know.

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 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

Works with: AutoHotkey_L

Uses StdoutTovar.ahk <lang AutoHotkey>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  
 }
  1. include bitmap_storage.ahk ; from http://rosettacode.org/wiki/Basic_bitmap_storage/AutoHotkey
  2. include run.ahk ; http://www.autohotkey.com/forum/viewtopic.php?t=16823

</lang>

C

Works with: POSIX version .1-2001

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.

<lang c>image read_image(const char *name);</lang>

<lang c>#include "imglib.h"

  1. define MAXCMDBUF 100
  2. define MAXFILENAMELEN 256
  3. 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;

}</lang>

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. <lang go>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)
   }

}</lang>

Julia

Works with: Julia version 0.6

<lang julia>using Images, FileIO

img = load("data/bitmapOutputTest.jpg") save("data/bitmapOutputTest.ppm", img)</lang>

Kotlin

Works with: Ubuntu 16.04

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. <lang scala>// 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)
       }
   }

}</lang>

Lua

Uses Bitmap class here, with an RGB tuple pixel representation, and the rudimentary PPM support here, and the Lenna image here.

First, the loadPPM() method is altered to allow passing an existing file handle: <lang lua>function Bitmap:loadPPM(filename, fp)

 if not fp then fp = io.open(filename, "rb") end
 if not fp then return end
 local head, width, height, depth, tail = fp:read("*line", "*number", "*number", "*number", "*line")
 self.width, self.height = width, height
 self:alloc()
 for y = 1, self.height do
   for x = 1, self.width do
     self.pixels[y][x] = { string.byte(fp:read(1)), string.byte(fp:read(1)), string.byte(fp:read(1)) }
   end
 end
 fp:close()

end</lang> Then, for the actual "read-from-pipe" task, a Lua environment that supports io.popen() is required: <lang lua>local bitmap = Bitmap(0,0) fp = io.popen("magick Lenna100.jpg ppm:-", "rb") bitmap:loadPPM(nil, fp)

bitmap:savePPM("Lenna100.ppm") -- just as "proof"</lang>

Mathematica/Wolfram Language

Based off the Julia program. <lang Mathematica>Export["data/bitmapOutputTest.ppm",Import["data/bitmapOutputTest.jpg"]];</lang>

Nim

Using "jpegtopnm" from Netpbm suite. Input is a JPEG file and result (the PPM file) is sent to stdout. The procedure "readPPM" reads directly from the stream and build the image container.

<lang Nim>import bitmap import osproc import ppm_read import streams

  1. Launch Netpbm "jpegtopnm".
  2. Input is taken from "input.jpeg" and result sent to stdout.

let p = startProcess("jpegtopnm", args = ["input.jpeg"], options = {poUsePath}) let stream = FileStream(p.outputStream()) let image = stream.readPPM() echo image.w, " ", image.h p.close()</lang>

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. <lang ocaml>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)
</lang>

Phix

Uses the demo\rosetta\viewppm.exw utility to accomplish this task.
The returned data is raw binary, so you can either write it direct or chuck it through read_ppm/write_ppm. <lang Phix>-- demo\rosetta\Bitmap_Read_an_image_through_a_pipe.exw requires("0.8.4") include builtins\pipeio.e include ppm.e -- read_ppm(), write_ppm()

sequence pipes = repeat(0,3) pipes[PIPOUT] = create_pipe(INHERIT_READ)

-- Create the child process, with replacement stdout. string cmd = sprintf("%s viewppm -load test.jpg",{get_interpreter(true)}) atom hProc = system_exec(cmd, 12, pipes),

    hPipe = pipes[PIPOUT][READ_PIPE]

string ppm = read_from_pipe(hPipe, hProc) while true do

   object chunk = read_from_pipe(hPipe, hProc)
   if chunk=-1 then exit end if
   ppm &= chunk

end while

pipes = close_handles(pipes)

if 0 then

   sequence img = read_ppm(ppm,bText:=true)
   write_ppm("Lenapipe.ppm", img)

else -- or

   integer fn = open("Lenapipe.ppm","wb")
   puts(fn,ppm)
   close(fn)

end if</lang>

PicoLisp

<lang PicoLisp>(setq *Ppm (ppmRead '("convert" "img.jpg" "ppm:-")))</lang>

Python

<lang 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

  1. boxes_1.jpg is the jpg version of boxes_1.ppm

im = Image.open("boxes_1.jpg") im.save("boxes_1v2.ppm") </lang> Does not need to pipe through a conversion utility because the Pillow module does the conversion.

Racket

<lang 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")</lang>

Raku

(formerly Perl 6)

Works with: Rakudo version 2017.09

Uses pieces from Bitmap and Read a PPM file tasks. Included here to make a complete, runnable program.

Uses imagemagick convert to pipe the image in.

<lang perl6>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;</lang>

See camelia image here.

Ruby

Uses Raster graphics operations/Ruby.

<lang ruby># frozen_string_literal: true

require_relative 'raster_graphics'

class Pixmap

 def self.read_ppm(ios)
   format = ios.gets.chomp
   width, height = ios.gets.chomp.split.map(&:to_i)
   max_colour = ios.gets.chomp
   if !PIXMAP_FORMATS.include?(format) ||
      (width < 1) || (height < 1) ||
      (max_colour != '255')
     ios.close
     raise StandardError, "file '#{filename}' does not start with the expected header"
   end
   ios.binmode if PIXMAP_BINARY_FORMATS.include?(format)
   bitmap = 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('foto.jpg') bitmap.save('foto.ppm') </lang>

Tcl

Works with: Tcl version 8.6
Library: Tk

<lang 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
   }

}</lang>

Wren

Library: DOME

As DOME doesn't have a method for calling an external process (ImageMagick in this case), we re-use the small plug-in (pipeconv.so) we created in the 'PPM conversion through a pipe' task to add this functionality.

We can now use this plug-in in the following script which calls ImageMagick to convert the output_piped.jpg file to a ppm file so that we can load the latter, convert it to a gray scale image, display it and save it to a .jpg file.

<lang ecmascript>import "graphics" for Canvas, ImageData, Color import "dome" for Window, Process import "io" for FileSystem import "plugin" for Plugin

Plugin.load("pipeconv")

import "pipeconv" for PipeConv

class Bitmap {

   construct new(fileName, fileName2, fileName3, width, height) {
       Window.title = "Bitmap - read image via pipe"
       Window.resize(width, height)
       Canvas.resize(width, height)
       _w = width
       _h = height
       _fn3 = fileName3
       // convert .jpg file to .ppm via a pipe
       PipeConv.convert(fileName, fileName2)
       // load the .ppm file
       loadPPMFile(fileName2)
   }
   init() {
       toGrayScale()
       // display gray scale image
       _bmp2.draw(0, 0)
       // save it to file
       _bmp2.saveToFile(_fn3)
   }
   loadPPMFile(fileName) {
       var ppm = FileSystem.load(fileName)
       if (ppm[0..1] != "P6") {
           System.print("The loaded file is not a P6 file.")
           Process.exit()
       }
       var lines = ppm.split("\n")
       if (Num.fromString(lines[2]) > 255) {
           System.print("The maximum color value can't exceed 255.")
           Process.exit()
       }
       var wh = lines[1].split(" ")
       var w = Num.fromString(wh[0])
       var h = Num.fromString(wh[1])
       _bmp = ImageData.create(fileName, w, h)
       var bytes = ppm.bytes
       var i = bytes.count - 3 * w * h
       for (y in 0...h) {
           for (x in 0...w) {
               var r = bytes[i]
               var g = bytes[i+1]
               var b = bytes[i+2]
               var c = Color.rgb(r, g, b)
               pset(x, y, c)
               i = i + 3
           }
       }
   }
   toGrayScale() {
       _bmp2 = ImageData.create("gray scale", _bmp.width, _bmp.height)
       for (x in 0..._bmp.width) {
           for (y in 0..._bmp.height) {
               var c1 = _bmp.pget(x, y)
               var lumin = (0.2126 * c1.r + 0.7152 * c1.g + 0.0722 * c1.b).floor
               var c2 = Color.rgb(lumin, lumin,lumin, c1.a)
               _bmp2.pset(x, y, c2)
           }
       }
   }
   pset(x, y, col) { _bmp.pset(x, y, col) }
   pget(x, y) { _bmp.pget(x, y) }
   update() {}
   draw(alpha) {}

}

var Game = Bitmap.new("output_piped.jpg", "output_piped.ppm", "output_piped_gs.jpg", 350, 350)</lang>

zkl

Translation of: C

Uses the PPM class from http://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#zkl

Using the convert utility by ImageMagick: <lang zkl>p:=System.popen(0'|convert "fractalTree.jpg" ppm:-|,"r"); img:=PPM.readPPM(p); p.close();</lang>