June 2018 Update

This commit is contained in:
Ingy döt Net 2018-06-22 20:57:24 +00:00
parent ba8067c3b7
commit 22f33d4004
5278 changed files with 84726 additions and 14379 deletions

View file

@ -0,0 +1,102 @@
% This module provides basic operations on ppm files:
% Read from file, create ppm in memory (from generic bitmap) and save to file.
% Writing PPM files was introduced in roseta code task 'Bitmap/Write a PPM file'
% but the same code is included here to provide whole set of operations on ppm
% needed for purposes of this task.
-module(ppm).
-export([ppm/1, write/2, read/1]).
% constants for writing ppm file
-define(WHITESPACE, <<10>>).
-define(SPACE, <<32>>).
% constants for reading ppm file
-define(WHITESPACES, [9, 10, 13, 32]).
-define(PPM_HEADER, "P6").
% data structure introduced in task Bitmap (module ros_bitmap.erl)
-record(bitmap, {
mode = rgb,
pixels = nil,
shape = {0, 0}
}).
%%%%%%%%% API %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% read ppm file from file
read(Filename) ->
{ok, File} = file:read_file(Filename),
parse(File).
% create ppm image from bitmap record
ppm(Bitmap) ->
{Width, Height} = Bitmap#bitmap.shape,
Pixels = ppm_pixels(Bitmap),
Maxval = 255, % original ppm format maximum
list_to_binary([
header(), width_and_height(Width, Height), maxval(Maxval), Pixels]).
% write bitmap as ppm file
write(Bitmap, Filename) ->
Ppm = ppm(Bitmap),
{ok, File} = file:open(Filename, [binary, write]),
file:write(File, Ppm),
file:close(File).
%%%%%%%%% Reading PPM %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
parse(Binary) ->
{?PPM_HEADER, Data} = get_next_token(Binary),
{Width, HeightAndRest} = get_next_token(Data),
{Height, MaxValAndRest} = get_next_token(HeightAndRest),
{_MaxVal, RawPixels} = get_next_token(MaxValAndRest),
Shape = {list_to_integer(Width), list_to_integer(Height)},
Pixels = load_pixels(RawPixels),
#bitmap{pixels=Pixels, shape=Shape}.
% load binary as a list of RGB triplets
load_pixels(Binary) when is_binary(Binary)->
load_pixels([], Binary).
load_pixels(Acc, <<>>) ->
array:from_list(lists:reverse(Acc));
load_pixels(Acc, <<R, G, B, Rest/binary>>) ->
load_pixels([<<R,G,B>>|Acc], Rest).
is_whitespace(Byte) ->
lists:member(Byte, ?WHITESPACES).
% get next part of PPM file, skip whitespaces, and return the rest of a binary
get_next_token(Binary) ->
get_next_token("", true, Binary).
get_next_token(CurrentToken, false, <<Byte, Rest/binary>>) ->
case is_whitespace(Byte) of
true ->
{lists:reverse(CurrentToken), Rest};
false ->
get_next_token([Byte | CurrentToken], false, Rest)
end;
get_next_token(CurrentToken, true, <<Byte, Rest/binary>>) ->
case is_whitespace(Byte) of
true ->
get_next_token(CurrentToken, true, Rest);
false ->
get_next_token([Byte | CurrentToken], false, Rest)
end.
%%%%%%%%% Writing PPM %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
header() ->
[<<"P6">>, ?WHITESPACE].
width_and_height(Width, Height) ->
[encode_decimal(Width), ?SPACE, encode_decimal(Height), ?WHITESPACE].
maxval(Maxval) ->
[encode_decimal(Maxval), ?WHITESPACE].
ppm_pixels(Bitmap) ->
% 24 bit color depth
array:to_list(Bitmap#bitmap.pixels).
encode_decimal(Number) ->
integer_to_list(Number).

View file

@ -0,0 +1,3 @@
Colorful = ppm:read("colorful.ppm"),
Gray = ros_bitmap:convert(ros_bitmap:convert(Colorful, grey), rgb),
ppm:write(Gray, "gray.ppm"),

View file

@ -1,13 +1,5 @@
using Color, Images, FixedPointNumbers
using Images, FileIO, Netpbm
const M_RGB_Y = reshape(Color.M_RGB_XYZ[2,:], 3)
function rgb2gray(img::Image)
g = red(img)*M_RGB_Y[1] + green(img)*M_RGB_Y[2] + blue(img)*M_RGB_Y[3]
g = clamp(g, 0.0, 1.0)
return grayim(g)
end
ima = imread("bitmap_read_ppm_in.ppm")
imb = convert(Image{Gray{Ufixed8}}, ima)
imwrite(imb, "bitmap_read_ppm_out.png")
rgbimg = load("data/bitmapInputTest.ppm")
greyimg = Gray.(rgbimg)
save("data/bitmapOutputTest.ppm", greyimg)

View file

@ -0,0 +1,130 @@
// Version 1.2.40
import java.awt.Color
import java.awt.Graphics
import java.awt.image.BufferedImage
import java.io.FileInputStream
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.ppm, created in the Bitmap/Write a PPM file task
val pbis = PushbackInputStream(FileInputStream("output.ppm"), 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_gray.jpg")
ImageIO.write(bbs.image, "jpg", grayFile)
}
}
}

View file

@ -0,0 +1,36 @@
class Pixel { has UInt ($.R, $.G, $.B) }
class Bitmap {
has UInt ($.width, $.height);
has Pixel @.data;
}
role PGM {
has @.GS;
method P5 returns Blob {
"P5\n{self.width} {self.height}\n255\n".encode('ascii')
~ Blob.new: self.GS
}
}
sub load-ppm ( $ppm ) {
my $fh = $ppm.IO.open( :enc('ISO-8859-1') );
my $type = $fh.get;
my ($width, $height) = $fh.get.split: ' ';
my $depth = $fh.get;
Bitmap.new( width => $width.Int, height => $height.Int,
data => ( $fh.slurp.ords.rotor(3).map:
{ Pixel.new(R => $_[0], G => $_[1], B => $_[2]) } )
)
}
sub grayscale ( Bitmap $bmp ) {
$bmp.GS = map { (.R*0.2126 + .G*0.7152 + .B*0.0722).round(1) min 255 }, $bmp.data;
}
my $filename = './camelia.ppm';
my Bitmap $b = load-ppm( $filename ) but PGM;
grayscale($b);
'./camelia-gs.pgm'.IO.open(:bin, :w).write: $b.P5;

View file

@ -0,0 +1,36 @@
/*REXX program reads a PPM formatted image file, and creates a gray─scale image of it. */
parse arg iFN oFN /*obtain optional argument from the CL.*/
if iFN=='' | iFN=="," then iFN= 'Lenna50' /*Not specified? Then use the default.*/
if oFN=='' | oFN=="," then oFN= 'greyscale' /* " " " " " " */
iFID= iFN'.ppm'; oFID= oFN'.ppm' /*complete the input and output FIDs.*/
call charin iFID, 1, 0 /*set the position of the input file. */
y=charin(iFID, , copies(9, digits() ) ) /*read the entire input file ───► X */
parse var y id 3 c 4 3 width height # pixels /*extract header info from the PPM hdr.*/
LF= 'a'x /*define a comment separator (in hdr).*/ /* ◄─── LF delimiters & comments*/
if c==LF then do; commentEND=pos(LF, y, 4) /*point to the last char in the comment*/ /* ◄─── LF delimiters & comments*/
parse var y =(commentEND) +1 width height # pixels /* ◄─── LF delimiters & comments*/
end /* ◄─── LF delimiters & comments*/
/* [↓] has an alternative delimiter? */ /* ◄─── LF delimiters & comments*/
z=pos(LF, height); if z\==0 then parse var height height =(z) +1 # pixels /* ◄─── LF delimiters & comments*/
z=pos(LF, # ); if z\==0 then parse var # # =(z) +1 pixels /* ◄─── LF delimiters & comments*/
chunk=4000 /*chunk size to be written at one time.*/
LenPixels= length(pixels)
do j=0 for 256; _=d2c(j); @._=j; @@.j=_ /*build two tables for fast conversions*/
end /*j*/
call charout oFID, , 1 /*set the position of the output file. */
call charout oFID, id || width height #' ' /*write the header followed by a blank.*/
!=1
do until !>=LenPixels; $= /*$: partial output string so far.*/
do !=! by 3 for chunk /*chunk: # pixels converted at 1 time.*/
parse var pixels =(!) r +1 g +1 b +1 /*obtain the next RGB of a PPM pixel.*/
if r=='' then leave /*has the end─of─string been reached? */
_=(.2126*@.r + .7152*@.g + .0722*@.b )%1 /*an integer RGB greyscale of a pixel. */
$=$ || @@._ || @@._ || @@._ /*lump (grey) R G B pixels together. */
end /*!*/ /* [↑] D2C converts decimal ───► char*/
call charout oFID, $ /*write the next bunch of pixels. */
end /*until*/
call charout oFID /*close the output file just to be safe*/
say 'File ' oFID " was created." /*stick a fork in it, we're all done. */