C-Programmierung: Aufgaben: Bild bearbeiten

AufgabenstellungBearbeiten

In dieser Aufgabe soll ein Programm erstellt werden, das mithilfe einer Bibliothek eine Bilddatei verändert. Sie können den Dateityp der Bilddatei und die Art, wie das Bild geöffnet und gespeichert wird, selbst wählen.

In dem Bild sollen in jeder zweiten Zeile die Helligkeit aller Pixel reduziert werden, also in der zweiten, vierten, sechsten etc. Zeile.
Dazu soll der r, g und b Wert aller betroffenen Pixel halbiert werden. Wenn der Farbwert nicht durch 2 teilbar ist, wird abgerundet.

BeispiellösungBearbeiten

Hier wird ein PNG Bild von stdin gelesen, decodiert, verändert, encodiert und dann auf stdout ausgegeben.

Dazu wird die PNG Bibliothek verwendet. Wie man das macht, ist dort, wo sich auch der Quellcode von libpng befindet, beschrieben.

Um die Bibliothek zu verwenden, müssen evtl. erst die Entwicklerdateien für libpng installiert werden. Unter Linux mit apt Paketverwaltungssystem kann dazu sudo apt install libpng-dev ausgeführt werden, dann kann man sich, wenn man möchte, die Typdefinitionen und vieles weiteres in /usr/include/png.h ansehen.
Des weiteren muss man den Kompiler mitteilen, dass er die Bibliothek verwenden soll. Bei gcc übergibt man dazu das Argument -lpng nach Angabe der C Quelldatei.

#include <stdlib.h> // malloc, EXIT_*
#include <string.h> // memset
#include <png.h>

// Bricht bei Problemen ab und gibt die Fehlermeldung aus
#define EXIT_PNG(F) if (!F) { \
	fprintf(stderr, "%s\n", bild.message); \
	return EXIT_FAILURE; \
}

struct pixel {
	unsigned char r;
	unsigned char g;
	unsigned char b;
	unsigned char a;
};

int main()
{
	// Vorbereitung zum Laden des PNG Bildes von stdin
	png_image bild;
	memset(&bild, 0, sizeof(bild));
	bild.version = PNG_IMAGE_VERSION;
	EXIT_PNG(png_image_begin_read_from_stdio(&bild, stdin))

	// Breite und Höhe des Bildes
	int w = bild.width;
	int h = bild.height;

	// w x h Pixel, jeweils ein Bytes für r, g, b und a
	struct pixel *pixels = malloc(w * h * 4);

	// Inhalt des Bildes laden
	bild.format = PNG_FORMAT_RGBA;
	EXIT_PNG(png_image_finish_read(&bild, NULL, pixels, 0, NULL))


	// r, g und b Werte der Pixel jeder zweiten Zeile halbieren
	for (int y = 1; y < h; y += 2) {
		int i = y * w;
		for (int x = 0; x < w; ++x) {
			struct pixel *px = &pixels[i++];
			px->r /= 2;
			px->g /= 2;
			px->b /= 2;
		}
	}


	// Verändertes Bild auf stdout ausgeben
	EXIT_PNG(png_image_write_to_stdio(&bild, stdout, 0, pixels, 0, NULL));

	return EXIT_SUCCESS;
}

Der Einfachheit halber wird hier die Helligkeit nicht genau halbiert. Um das zu bewerkstelligen müsste man die Pixel erst aus dem srgb Farbraum holen, was übrigens meistens nicht passiert http://www.ericbrasseur.org/gamma.html?i=2, dann kann man die erhaltenen r, g und b Werte in Luma, das der Helligkeit entspricht, und der Färbung umwandeln, der Luma Wert kann jetzt halbiert werden, daraufhin kann das Resultat zurück zu r, g und b und dann zu srgb umgewandelt werden.

Wenn das Programm übersetzt wurde, kann man unter Linux im Terminal das Programm ausführen und dabei stdin und stdout jeweils auf eine Datei zeigen lassen, aber nicht auf die gleiche, z.B. ./a.out < bild.png > tmp_streifen.png