Rekommenderade övningsuppgifter
Syfte
Syftet med denna laboration är att du skall:- Träna på användning av flerdimensionella arrayer.
- Prova att använda kod som andra personer har skrivit.
- Få grundläggande insikt i hur bilder representeras i en dator och hur man kan manipulera dem.
Introduktion
Din uppgift är att skriva ett program som kan läsa in en bild från en fil i 24-bitars okomprimerat BMP-format (dvs en Windows-bitmapp) och sedan antingen justera ljusstyrkan för bilden, eller konvertera den till gråskala. Därefter ska bilden sparas till fil igen.
Ändra ljusstyrka- Läsa in en bild från en fil.
- Justera ljusstyrkan för R-, G- och B-kanalerna oberoende av varandra. (Mer information om vad som menas med R/G/B-kanaler finns nedan.) Användaren anger tre tal som anger hur mycket varje färgvärde ska skalas. Talen 0.5 1.2 0 anger exempelvis halverad ljusstyrka för röda färger och 20% ökning för gröna färger, medan den blå färgkomponenten helt ska tas bort.
- Spara den ändrade bilden till en fil.
- Avsluta programmet
- Läsa in en bild från en fil.
- Konvertera bilden till gråskala.
- Spara den ändrade bilden till en fil.
- Avsluta programmet
Att tolka BMP-filer är lite besvärligt (även om det är ett av de enklare bildformaten), särskilt som vi inte diskuterat filhantering ännu. Därför får du färdig kod för att läsa in en bild respektive spara en bild. Koden består av filerna bm.h, bm.c. Du måste separatkompilera C-filen, dvs du får inte bara klistra in den i filen där din main-funktion ligger. Koden i de ovannämnda filerna är knappast föredömlig, utan dess syfte är snarare att ge er en realistiskt bild av hur färdig kod kan se ut.
Datastruktur för bilden
Denna typ av bilder är uppdelade i tre komponenter (kanaler): en för rött, en för grönt och en för blått. Dessa bilder överlagras sedan ovanpå varandra för att ge den färdiga bilden. Varje komponent kan ses som ett array av array, där elementen är heltal mellan 0 och 255 som anger ljusstyrkan för respektive färg. Anta att du har en bild som är tre punkter hög och tre punkter bred. Den är helt röd, bortsett från punkten i mitten som är svagt blå. Komponenterna kan då se ut så här:
Röd: Grön: Blå: 255 255 255 0 0 0 0 0 0 255 0 255 0 0 0 0 30 0 255 255 255 0 0 0 0 0 0
I detta program representeras en bild som en tvådimensionell array av pixel-structar där varje pixel struct innehåller de tre färgkomponenterna i varsin unsigned char. Eftersom en char kan representera alla tal mellan 0 och 255 behöver vi inte använda int, och eftersom alla tal betraktas som positiva används unsigned char.
typedef struct {
unsigned char red;
unsigned char green;
unsigned char blue;
} pixel;
typedef struct {
pixel array[MAXROWS][MAXCOLUMNS];
int rows, columns;
} image;image-structen svarar mot själva bilden, den innehåller förutom en tvådimensionell array med pixlar, rows som är antalet rader i bilden och columns som är antal kolumner (dvs storleken på den inlästa bilden). Structen pixel innehåller värdet för färgerna vid en viss pixel (punkt) i bilden.
Om bildrutinerna
Du behöver två funktioner från den kod du fått:
bmp_read (läser in en bild till minnet)
bmp_write (skriver en bild som är lagrad i minnet till en fil).
Prototyperna ser ut på följande sätt:
image bmp_read(char fname[]); int bmp_write(char fname[], image picture);
Dessa två funktioner läser, respektive skriver en BMP-bild antingen
från en fil till minnet, eller från minnet till en fil.
fname är namnet på den fil som ska hanteras.
picture svarar mot bilden.
bmp_write returnerar 1 om det gick bra, och 0
något ej fungerade, medans bmp_read ger tillbaka
en bild med rows och columns satt till -1 om något går fel vid inläsningen.
Algoritmer
Ändra ljusstyrkaÄndringen av ljusstyrkan går till så att ljusstyrkan för respektive färgkomponent skalas i varje pixel. Har en pixel ljusstyrkan (25 255 50) och de skall skalas med (0 1.2 0.5) för R, G respektive B, blir resultatet (0 255 25) för den pixeln.
GråskalaHos en bild i gråskala är ljusstyrkan hos de tre färgkomponenterna lika stora, t.ex. (128 128 128).
Att konvertera en bild till gråskala görs enklast bäst genom att räkna ut gråskalevärde = (76 * R / 255) + (150 * G / 255) + (29 * B / 255) och sätta färgernas värde till detta beräknade värde. Så om pixeln hade värdet (5, 230, 70) blir gråskalevärdet (144, 144, 144).
Exempel på användning av bildrutinerna
Följande kodsnutt läser in bilden "image1.bmp", halverar ljusstyrkan för den röda komponenten och skriver data till bilden "image2.bmp":
#include "bm.h"
#include <stdio.h>
#include <stdlib.h>
int main(void) {
image picture;
int r, k;
picture=bmp_read("image1.bmp");
if (picture.columns == -1) {
printf("Error: could not perform bmp_read.\n");
exit(1);
}
for (r = 0; r < picture.rows; r++)
for (k = 0; k < picture.columns; k++) {
picture.array[r][k].red /= 2;
}
if(!bmp_write("image2.bmp", picture)) {
fprintf(stderr, "Error: Could not perform bmp_write.\n");
exit(1);
}
return 0;
} Exempel på användning av programmet
Menyn visas när användaren startar programmet och följer sedan stegen "läsa fil", "modifiera fil", "spara fil", "avsluta" enligt ovan, utan att visa menyn flera gånger. Det första filnamnet är en länk till en bild ni kan använda för att testa ert program (endast i specifikation, ditt eget program ska inte ha några länkar). Om du klickar på dem visan hur bilden ser ut. Ni måste ta hand om validering av indata. Det är tillåtet avbryta programkörningen om felaktiga indata ges, men bara om vettiga felmeddelanden ges till användaren.
Exempel på att ändra ljusstyrka:
MENY 1 Ändra ljusstyrka 2 Konvertera till gråskala 3 Avsluta utan åtgärd Välj ett alternativ: 1 Ange filnamn: rgb.bmp Ange faktorer (R G B): 1.3 0.8 0.2 Ange filnamn: rgb2.bmp Allt gick bra, programmet avslutas.
Exempel på att konvertera till gråskala:
MENY 1 Ändra ljusstyrka 2 Konvertera till gråskala 3 Avsluta utan åtgärd Välj ett alternativ: 2 Ange filnamn att läsa från: testbild.bmp Konvertering utförd. Ange filnamn att skriva till: testbild2.bmp Allt gick bra, programmet avslutas.
Några saker att tänka på
- Som nämndes ovan kan ett färgvärde inte vara större än 255. Vad händer då om värdet 200 ska skalas med en faktor 2? Det blir ju 400. I detta fall ska du sätta värdet till 255.
- Det här programmet är för stort för att ha all kod i main(). Dela upp det i funktioner. OBS! Tänk på att varje funktion ska utföra EN uppgift. Låt inte en funktion först läsa in något från tangentbordet, sedan göra en beräkning och slutligen skriva ut resultatet. Detta bör göras i tre funktioner. Konstruera en samling funktioner som bara hanterar indata, en kategori funktioner som löser problem, samt en grupp funktioner som hanterar utdata. Tänk på att de funktioner ni skriver ska kunna återanvändas. Man vill sannolikt inte att en funktion som gör en matematisk beräkning läser sin indata från tangentbordet. Data ska vara inläst och validerad innan funktionen anropas.
- Bilder att experimentera med kan hämtas från nätet, men välj bilder där det är möjligt dra slutsatser utifrån de konverteringar man gör. Använd små bilder för att lättare kunnat kontrollera dem. De givna rutinerna klarar för tillfället bara bilder som är max 800x600 pixlar. Vill ni prova med större bilder så måste ni ändra konstanterna MAXCOLUMNS och MAXROWS i bm.h till värden som är större än bildens storlek. Glöm inte heller att konvertera bilderna du vill prova till 24-bit okomprimerad BMP. Om du inte vet hur man gör, eller inte vill ge dig ut och leta själv, kan du använda bilden rgb.bmp från exemplet ovan och bilden testbild.bmp från exemplet ovan. Tryck på Shift samtidigt som du klickar på länken om du vill spara bilden som en fil.
Tips på arbetsgång för er som har svårt att komma igång med uppgiften:
- Börja skriv ett enkelt program som läser in en bild mha bmp_read och sedan sparar denna bild till en ny fil (mha bmp_write). En bra utgångspunkt är exempelprogrammet i uppgiften (testa så att det fungerar).
- Skriv en funktion för att givet en pixel-struct (som tas in som parameter) beräkna den pixelns gråskalevärde. Funktionen kan tex returner en ny pixel med detta värde för alla 3 färgkomponenter. Testa så att den fungerar genom att skicka in en pixel och se så att du får tillbaka en pixel med korrekta värden.
- Skriv en funktion som tar in hur hur ljusstyrkan ska förändras för en pixels 3 olika färgkomponenter (en parameter per färg) samt en pixel och ger tillbaka en ny pixel med färgvärdena ändrade.
- Skriv en funktion som givet en bild skapar en ny bild som innehåller pixlarna i första bilden konverterade till gråskala (använder sig lämpligtvis av funktionen från punkt 2 ovan). och returnerar den nya bilden
- Testa att det funkar genom att läsa in en bild. Konvertera den till gråskala samt skriva den till en fil (och kontrollera så att den ser ok ut)
- Skriv en funktion som givet en bild och hur ljusstyrkan för denna ska ändras skapar en ny bild som innehåller pixlarna i första bilden med ljusstyrkan konverterad (använder sig lämpligtvis av funktionen från punkt 3 ovan) och returnerar den nya bilden
- Skriv en meny där man kan välja vad som ska göras (gråskalekonverteringen eller ljusstyrkeförändringen) och där man får möjlighet att välja filnamn och för ljusstyrkeändringen ange vilka värden bilden ska konverteras med.
Inlämning
Väl kommenterad källkod ska vara inlämnad via det webbaserade inlämningssystemet som ni hittar här senast 15/10 kl.
13.00. Skriv ert namn och användarnamn som en kommentar i filen.
Uppgiften ska lösas enskilt.