BMP-bitmapafbeeldingen lezen in VHDL-testbenches met TEXTIO
Bij het werken met VHDL-testbenches is de simpelste manier om een afbeelding te importeren het BMP-rastergrafischformaat. BMP-bestanden worden native ondersteund door Windows en blootleggen ruwe pixeldata zonder compressie, wat ze ideaal maakt voor simulatie.
Dit artikel legt uit hoe je een binaire BMP-bestand leest, de pixeldata opslaat in dynamisch geheugen, het aan een VHDL-DUT voedt, en de verwerkte afbeelding terugschrijft naar een nieuw BMP-bestand. De voorbeeld-DUT voert een grijswaarde-conversie uit, maar de techniek is van toepassing op elke pixelgewijze verwerkingsmodule.
Deel van een TEXTIO-serie:
Waarom BMP het Optimale Formaat is voor VHDL
JPEG en PNG zijn alomtegenwoordig op het web, maar beide gebruiken compressie. JPEG is verliesbehaftet, PNG is verliesvrij; beide voegen een laag complexiteit toe wanneer een testbench direct toegang moet hebben tot pixeldata. BMP daarentegen slaat afbeeldingsdata op als ongedefinieerde rastergrafica, zodat elke pixel vertegenwoordigd wordt door ruwe RGB-bytes. Dit elimineert de noodzaak voor decompressiecode in VHDL en garandeert deterministische simulatiestijd.
Algemene afbeeldingseditors zoals Photoshop of GIMP laden afbeeldingen intern in het geheugen als rasterdata. Voor VHDL is het converteren van de bronafbeelding naar BMP handmatig (of via een script) de meest directe aanpak.
Het BMP-bestand Voorbereiden
Gebruik Windows Paint om een consistente bestandslayout te garanderen:
- Open de afbeelding.
- Selecteer Bestand > Opslaan als.
- Kies 24-bit Bitmap (*.bmp; *.dib) en bewaar.
Met deze methode zal de bestandsheader altijd de 54-byte BITMAPINFOHEADER variant zijn met een RGB24-pixelformaat. De volgende tabel geeft de header-velden weer die we uitlezen.
| Offset (Dec) | Grootte (B) | Verwacht (Hex) | Beschrijving |
|---|---|---|---|
| 0 | 2 | 42 4D | ID-veld |
| 10 | 4 | 54 (36 00 00 00) | Pixel-array offset |
| 14 | 4 | 40 (28 00 00 00) | Header-grootte |
| 18 | 4 | Leeswaarde | Afbeeldingsbreedte in pixels |
| 22 | 4 | Leeswaarde | Afbeeldingshoogte in pixels |
| 26 | 1 | 01 | Aantal kleurvlakken |
| 28 | 1 | 18 | Aantal bits per pixel |
Alleen de groen-gemarkeerde velden zijn essentieel; de rest dient als sanity-checks.
De Testcase
Hieronder is een minimale grijswaarde-DUT die 24-bit RGB-invoer accepteert en de corresponderende luminance-waarde uitvoert, gecodeerd terug in RGB. De module is puur combinatorisch, dus geen klok vereist.
entity grayscale is port ( r_in : in std_logic_vector(7 downto 0); g_in : in std_logic_vector(7 downto 0); b_in : in std_logic_vector(7 downto 0); r_out : out std_logic_vector(7 downto 0); g_out : out std_logic_vector(7 downto 0); b_out : out std_logic_vector(7 downto 0) ); end grayscale;
Voor volledigheid is de volledige projectbron (inclusief een 1000×1000 pixel Boeing 747 BMP) beschikbaar voor download door een e-mailadres op te geven in het gelinkte formulier.
TEXTIO Importeren
Bovenaan je VHDL-bestand toevoegen:
use std.textio.all; use std.env.finish;
Deze declaraties vereisen VHDL-2008 of nieuwer.
Aangepaste Typen voor Afbeeldingsopslag
Definieer de volgende typen in het declaratieve gebied van je testbench:
type header_type is array (0 to 53) of character; type pixel_type is record red : std_logic_vector(7 downto 0); green : std_logic_vector(7 downto 0); blue : std_logic_vector(7 downto 0); end record; type row_type is array (integer range <>) of pixel_type; type row_pointer is access row_type; type image_type is array (integer range <>) of row_pointer; type image_pointer is access image_type;
Deze dynamische datastructuren laten de testbench toe om afbeeldingen van elke grootte te accommoderen zonder statische array-limieten.
De DUT Instantiëren
signal r_in, g_in, b_in : std_logic_vector(7 downto 0); signal r_out, g_out, b_out : std_logic_vector(7 downto 0); begin DUT : entity work.grayscale port map ( r_in => r_in, g_in => g_in, b_in => b_in, r_out => r_out, g_out => g_out, b_out => b_out );
De BMP-header Lezen
Het proces begint met het laden van de 54-byte header in een header variabele en het valideren van sleutelvelden met assert statements. Deze bescherming voorkomt per ongelik het gebruik van een niet-ondersteund bestandsformaat.
for i in header_type'range loop read(bmp_file, header(i)); end loop; -- Basis sanity checks assert header(0) = 'B' and header(1) = 'M' report "Eerste twee bytes zijn niet ""BM"". Dit is geen BMP-bestand" severity failure; assert character'pos(header(10)) = 54 and character'pos(header(11)) = 0 and character'pos(header(12)) = 0 and character'pos(header(13)) = 0 report "Pixel-array offset is niet 54 bytes" severity failure; assert character'pos(header(14)) = 40 and character'pos(header(15)) = 0 and character'pos(header(16)) = 0 and character'pos(header(17)) = 0 report "DIB-header-grootte is niet 40 bytes, is dit een Windows BMP?" severity failure; assert character'pos(header(26)) = 1 and character'pos(header(27)) = 0 report "Kleurvlakken is niet 1" severity failure; assert character'pos(header(28)) = 24 and character'pos(header(29)) = 0 report "Bits per pixel is niet 24" severity failure;
Na validatie worden de afbeeldingsbreedte en -hoogte geëxtraheerd:
image_width := character'pos(header(18)) + character'pos(header(19)) * 2**8 + character'pos(header(20)) * 2**16 + character'pos(header(21)) * 2**24; image_height := character'pos(header(22)) + character'pos(header(23)) * 2**8 + character'pos(header(24)) * 2**16 + character'pos(header(25)) * 2**24; report "image_width: " & integer'image(image_width) & ", image_height: " & integer'image(image_height);
Pixeldata Lezen
Elke BMP-rij is opgevuld tot een veelvoud van vier bytes. Bereken de padding per rij en reserveer dynamische opslag voor de afbeelding.
padding := (4 - image_width*3 mod 4) mod 4; image := new image_type(0 to image_height - 1); for row_i in 0 to image_height - 1 loop row := new row_type(0 to image_width - 1); for col_i in 0 to image_width - 1 loop read(bmp_file, char); row(col_i).blue := std_logic_vector(to_unsigned(character'pos(char), 8)); read(bmp_file, char); row(col_i).green := std_logic_vector(to_unsigned(character'pos(char), 8)); read(bmp_file, char); row(col_i).red := std_logic_vector(to_unsigned(character'pos(char), 8)); end loop; for i in 1 to padding loop read(bmp_file, char); end loop; -- padding negeren image(row_i) := row; end loop;
De DUT Testen
Voer elke pixel in de DUT in, wacht een kleine deltacyclus, en vang de grijswaarde-uitgang op.
for row_i in 0 to image_height - 1 loop row := image(row_i); for col_i in 0 to image_width - 1 loop r_in <= row(col_i).red; g_in <= row(col_i).green; b_in <= row(col_i).blue; wait for 10 ns; row(col_i).red := r_out; row(col_i).green := g_out; row(col_i).blue := b_out; end loop; end loop;
De Uitvoer-BMP Schrijven
Eerst schrijven we de bewaarde header. Vervolgens serialiseren we de pixeldata rij voor rij, met padding waar nodig. Ten slotte ruimen we dynamisch geheugen op en sluiten we de bestanden.
for i in header_type'range loop write(out_file, header(i)); end loop; for row_i in 0 to image_height - 1 loop row := image(row_i); for col_i in 0 to image_width - 1 loop write(out_file, character'val(to_integer(unsigned(row(col_i).blue)))); write(out_file, character'val(to_integer(unsigned(row(col_i).green)))); write(out_file, character'val(to_integer(unsigned(row(col_i).red)))); end loop; deallocate(row); for i in 1 to padding loop write(out_file, character'val(0)); end loop; end loop; deallocate(image); file_close(bmp_file); file_close(out_file); report "Simulation done. Check ""out.bmp"" image."; finish; end process;
Resulterende Afbeelding
Het gegenereerde out.bmp bestand toont de grijswaardeversie van de originele Boeing 747-afbeelding. Een JPEG-voorvertoning wordt in het artikel getoond; het volledige BMP-bestand is beschikbaar via het downloadformulier.
Verder Overwegingen
- Voor hoog-doorvoer beeldverwerking, overweeg YUV-codering waar luminance (Y) gescheiden is van chrominantie (U/V). Converteren tussen RGB en YUV is eenvoudig en vaak meer hardware-vriendelijk.
- CMYK-conversie is complexer omdat een één-op-één pixelmapping niet bestaat; het vereist typisch een lookup-tabel of een uitgebreider algoritme.
- Bij werken met aangepaste coderingen, kun je een lichtgewicht bestandsformaat ontwerpen (bijv.
.yuvof.cmyk) dat de BMP-header volledig weglaat, wat parsing vereenvoudigt. - Het automatiseren van de BMP-conversiestap (bijv. via ImageMagick's
convertcommando) zorgt voor consistente testvectoren over simulati-runs heen.
VHDL
- Holografie Ontmaskerd: Van Gabors Nobelprijswinnende Uitvinding tot Toekomstige Dataopslag
- NASA's Langley Research Center Onthult Geavanceerde Plenoptische Camera voor 3D Kleurimaging
- Digitale Handtekeningen Gebruiken om Binaire Bestandsintegriteit te Verifiëren op Linux
- C# using-statements: imports, aliasen en static directives
- C Bestandsbehandeling: I/O onder de knie krijgen met fopen, fprintf, fread en fseek
- C Header-bestanden begrijpen: Typen, gebruik en best practices
- FPGA Block RAM initialiseren vanuit tekstbestanden met VHDL's TEXTIO-bibliotheek
- Verilog File I/O onder de knie: Openen, Lezen, Schrijven en Sluiten
- Python JSON: Encoding, Decoding en Bestandsafhandeling – Een Praktische Gids
- Java-bestandsbewerkingen onder de knie krijgen met java.io – Aanmaken, lezen, schrijven en verwijderen
- Bestanden Lezen in Java met BufferedReader – Een Praktische Gids met Voorbeelden