Skip to content

Instantly share code, notes, and snippets.

@ingoogni
Last active May 16, 2022 07:49
Show Gist options
  • Save ingoogni/b422eaa251e9c9192071be94d8269de9 to your computer and use it in GitHub Desktop.
Save ingoogni/b422eaa251e9c9192071be94d8269de9 to your computer and use it in GitHub Desktop.
Read .pgm file
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