Reading image files with VHDL part 2

Having set up a library for reading images, let’s now go on to read an image in!

pgm_read Recall, we have a function:

impure function pgm_read (filename : string)
return pixel_array_ptr;
 So, some declarations - fairly self-explanatory: 

file pgmfile : text;
variable width, height : coordinate; -- storage for image dimensions
variable l : line; -- buffer for a line of text
variable s : string(1 to 2); -- to check the P2 header
variable ints : integer_vector(1 to 3); -- store the first three integers (width, height and depth)
variable int : integer; -- temporary storage
variable ch : character; -- temporary storage
variable good : boolean; -- to record whether a read is successful or not
variable count : positive; -- keep track of how many numbers we've read
variable empty_image : pixel_array_ptr := null; -- return this on error
variable ret : pixel_array_ptr; -- actual return value
variable x, y : coordinate; -- coordinate tracking
 We begin by opening the file in read mode and reading the first line: 

-- setup some defaults
width := 0;
height := 0;
file_open(pgmfile, filename, read_mode);
readline(pgmfile, l);
 Now, reading the header.. The P2 format PGM header is very simple: 

P2
# optional comments - next two ints are
# xsize and ysize
128 90
# maybe some more comments
# next int is the max value for pixel
255
 All the integers are separated by whitespace (may be space characters or linefeeds or carriage returns, we know not). This suits VHDL, as the 

textio library can read whitespace separated integers from a text file! It’s slightly tricky as we have to manage the cases where the values appear on separate lines. First, check the “P2-ness”:
read(l, s(1));
read(l, s(2), good);
if not good or s /= “P2” then
report “PGM file ‘”&filename&”‘ not P2 type” severity warning;
file_close(pgmfile);
return empty_image;
end if;
VHDL’s

textio is a bit different from in most other languages – we read a line from the file then read values from the line buffer. So, we read the first two characters and check them against “P2”. Now to read the next three integers:
allints : loop — read until we have 3 integers (width, height and colour depth).
line_reading:loop
readline(pgmfile, l);
exit when l.all(1) = ‘#’; — skip comments;
if l’length = 0 then
report “EOF reached in pgmfile before opening integers found”
severity warning;
file_close(pgmfile);
return empty_image;
end if;
number_reading: loop
read(l, ints(count), good);
exit number_reading when not good; — need to read some more from file
count := count + 1;
exit allints when count > ints’high;
end loop;
end loop;
exit when count > ints’high;
end loop;
— Now we have our header sorted. store it
width := ints(1);
height := ints(2);
We have three loops – the outermost

allints loop runs until count increments beyond the end of the ints array. The line_reading loop reads a line, checks for comment lines to skip them, passes non-comment lines onto the next loop: number_reading – this reads numbers from the line buffer until it fails or has filled the ints array. In the former case, the line_reading loop provides more data, otherwise, we drop out and store our data. Once we have the width and height we can allocate an array and read the pixels:
— now read the image pixels
x := 0;
y := 0;
— allocate storage
ret := new pixel_array(0 to width-1, 0 to height-1);
allpixels : loop
readline(pgmfile, l);
exit when l = null; — oh dear, something went wrong!
exit when l’length = 0; — more wrongness!
numbers: loop
read(l, int, good);
exit numbers when not good;
ret(x, y) := int;
exit allpixels when x = width-1 and y = height-1;
x := x + 1;
if x >= width then
x := 0;
y := y + 1;
end if;
end loop numbers;
end loop allpixels;
Again a nested loop structure, one for reading the lines from the file and another for extracting the integers from the linebuffer. The x and y coordinates are updated on each integer read to “scan” across and down the image. Once

x and y reach their terminal values, the loop exits.
assert (x = width-1 and y = height-1)
report “Don’t seem to have read all the pixels I should have”
severity warning;
return ret;
Finally, a little error checking and warning, and return the data.

Onwards, to making use of it… Code for this series can be found on github

Leave a comment

Your email address will not be published. Required fields are marked *