Last active
May 16, 2022 07:49
-
-
Save ingoogni/b422eaa251e9c9192071be94d8269de9 to your computer and use it in GitHub Desktop.
Read .pgm file
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import streams | |
from strutils import parseInt, Whitespace | |
#from os import getFileSize | |
type | |
PGMError* = object of Defect | |
proc pgmRead(strm: FileStream, filePos: int=0): auto= | |
var data: char | |
strm.setPosition(filePos) | |
return iterator: (int, char)= | |
while not strm.atEnd: | |
let idx = getPosition(strm) | |
read(strm, data) | |
yield (idx, data) | |
strm.close() | |
proc pgmSkipComment(pgmData: iterator: (int, char)): auto= | |
# remove all comments, excluding the new lines \n | |
var | |
idx: int | |
data: char | |
comment: bool = false | |
return iterator: (int, char)= | |
while true: | |
(idx, data) = pgmData() | |
if finished(pgmData): | |
raise newException(PGMError, "incomplete file") | |
if data == '#': | |
comment = true | |
while comment == true: | |
(idx, data) = pgmData() | |
if finished(pgmData): | |
raise newException(PGMError, "incomplete file") | |
if data == '\n': | |
comment = false | |
yield (idx, data) | |
proc pgmSkipWhiteSpace(pgmData: iterator: (int,char)): auto= | |
# Remove all multiple white spaces so the stream | |
# is in shape to extract the header data | |
var | |
idx: int | |
data: char | |
isWhiteSpace: bool = false | |
return iterator(): (int, char)= | |
while true: | |
(idx, data) = pgmData() | |
if finished(pgmData): | |
raise newException(PGMError, "incomplete file") | |
while data in Whitespace and isWhiteSpace == true: | |
(idx, data) = pgmData() | |
if finished(pgmData): | |
raise newException(PGMError, "incomplete file") | |
isWhiteSpace = false | |
if data in Whitespace: | |
isWhiteSpace = true | |
yield (idx, data) | |
proc pgmGetHeader(pgmData: iterator: (int, char)): auto= | |
# extract the header data and ad a few extras | |
let nWhiteSpaceBeforeData = 4 | |
var | |
header: tuple[magick: string, width: int, height: int, maxValue: int, startData: int, bytes: int] | |
nWhiteSpace: int | |
temp: string | |
isHeader: bool = true | |
return iterator(): (string, int, int, int, int, int) = | |
while isHeader: | |
let (idx, data) = pgmData() | |
if finished(pgmData): | |
raise newException(PGMError, "incomplete file") | |
if data in Whitespace: | |
case nWhiteSpace: | |
of 0: header.magick = temp | |
of 1: header.width = parseInt(temp) | |
of 2: header.height = parseInt(temp) | |
of 3: | |
header.maxValue = parseInt(temp) | |
header.startData = idx + 1 | |
case header.maxValue: | |
of 0..255: | |
header.bytes = 1 | |
of 256..65535: | |
header.bytes = 2 | |
else: | |
raise newException(PGMError, "bit_depth not 8 or 16") | |
else: discard | |
temp = "" | |
nWhiteSpace += 1 | |
else: | |
temp.add(data) | |
if nWhiteSpace == nWhiteSpaceBeforeData: | |
isHeader = false | |
yield header | |
proc pgmHeader*(file: string): tuple= | |
let | |
strm = newFileStream(file, fmRead) | |
pgm = pgmRead(strm) | |
comment = pgmSkipComment(pgm) | |
whitespace = pgmSkipWhiteSpace(comment) | |
header: tuple[magick: string, width: int, height: int, maxValue: int, startData: int, bytes: int] = pgmGetHeader(whitespace)() | |
strm.close() | |
return header | |
proc pgmReadBody*(file: string, header: tuple): auto= | |
let strm = newFileStream(file, fmRead) | |
strm.setPosition(header.startData) | |
return iterator(): uint= | |
while not strm.atEnd: | |
var data:uint | |
case header.bytes: | |
of 1: | |
data = readUint8(strm) | |
of 2: | |
data = 256 * readUint8(strm) + readUint8(strm) #endian | |
else: | |
raise newException(PGMError, "Ground control to Major Tom.....") | |
yield data | |
strm.close() | |
proc pgmReadBodyColumn*(file: string, header: tuple): auto= | |
# jump through the file to extract the data column wise. | |
let strm = newFileStream(file, fmRead) | |
let rowLength = header.width * header.bytes | |
return iterator(): uint= | |
for j in countup(0, rowLength-1, header.bytes): | |
for i in 0..<header.height: | |
let pos = j + (i * rowLength) + header.startData | |
strm.setPosition(pos) | |
var data: uint | |
case header.bytes: | |
of 1: | |
data = readUint8(strm) | |
of 2: | |
data = 256 * readUint8(strm) + readUint8(strm) #endian | |
else: | |
raise newException(PGMError, "Ground control to Major Tom.....") | |
yield data | |
strm.close() | |
proc toFloat*(pgmData: iterator:uint, header:tuple): auto= | |
return iterator(): float= | |
while true: | |
let data = pgmData().float / header.maxValue.float | |
if finished(pgmData): | |
break | |
yield data | |
proc pgmDataRow*[T](pgmData: iterator:T, header: tuple): auto= | |
return iterator(): seq[T]= | |
while true: | |
var row: seq[T] | |
for i in 0..<header.width: | |
let data = pgmData() | |
row.add(data) | |
if finished(pgmData): | |
break | |
yield row | |
proc pgmDataColumn*[T](pgmData: iterator:T, header: tuple): auto= | |
return iterator(): seq[T]= | |
while true: | |
var col: seq[T] | |
for i in 0..<header.height: | |
let data = pgmData() | |
col.add(data) | |
if finished(pgmData): | |
break | |
yield col | |
when is_main_module: | |
let | |
filename = "testGrayScale.pgm" | |
header = pgmHeader(filename) | |
pgmBody = pgmReadBody(filename, header) | |
row = pgmDataRow(pgmBody, header) | |
pgmBodyCol = pgmReadBodyColumn(filename, header) | |
floats = toFloat(pgmBodyCol, header) | |
col = pgmDataColumn(floats, header) | |
echo "header : ", header | |
echo "as rows: " | |
while true: | |
let rowData = row() | |
if finished(row): | |
break | |
echo " ",rowData | |
echo "as columns: " | |
while true: | |
let colData = col() | |
if finished(col): | |
break | |
echo " ", colData | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment