- PNG-Dateien in C++
- Gaußscher Glättungs Kernel
- WebCam mit OpenCV
Dieses kurze Beispiel führt den Umgang mit der libpng vor. Es werden mehrere Dinge gezeigt:
- Eine PNG-Datei wird geladen und gleichzeitig in das Format RGBA/8Bit umgewandet. Damit wird der Transformations-Code einfacher.
- Jedes Pixel wird in einen Grauwert konvertiert (per gewichtetem Mittel).
- Es wird ein Histogram über die Grauwerte erstellt (und auf die Konsole ausgegeben)
- Wenn der Grauwert eines Pixels größer als ein Schwellenwert ist (den ich aus dem Histogram abgelesen habe) wird das Pixel in fast weiß umgewandelt, anderenfalls in fast schwarz
- Abschliessend wird das Bild als RGBA/8Bit gespeichert
#include <stdlib.h>
#include <stdio.h>
#include <png.h>
void read_image(
const char *filename,
int& width,
int& height,
png_bytep*& image)
{
FILE *fp = fopen(filename, "rb");
png_byte color_type;
png_byte bit_depth;
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info = png_create_info_struct(png);
(void)setjmp(png_jmpbuf(png));
png_init_io(png, fp);
png_read_info(png, info);
width = png_get_image_width(png, info);
height = png_get_image_height(png, info);
color_type = png_get_color_type(png, info);
bit_depth = png_get_bit_depth(png, info);
// Lese alle Color-Typen einheitlich im 8Bit-Tiefe, RGBA-Format
// Siehe http://www.libpng.org/pub/png/libpng-manual.txt
// BEGIN-TRANSFORM
if(bit_depth == 16)
{
png_set_strip_16(png);
}
if(color_type == PNG_COLOR_TYPE_PALETTE)
{
png_set_palette_to_rgb(png);
}
// PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth.
if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
{
png_set_expand_gray_1_2_4_to_8(png);
}
if(png_get_valid(png, info, PNG_INFO_tRNS))
{
png_set_tRNS_to_alpha(png);
}
// Manche color-types haben keinen Alpha-Chanel => mit 0xff auffuellen
if( color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_PALETTE)
{
png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
}
if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
{
png_set_gray_to_rgb(png);
}
png_read_update_info(png, info);
// END-TRANSFORM-TRANSFORM
//image = (png_bytep*)malloc(sizeof(png_bytep) * height);
image = new png_bytep[height]; // (png_bytep*)malloc(sizeof(png_bytep) * height);
for(int y = 0; y < height; y++)
{
//image[y] = (png_byte*)malloc(png_get_rowbytes(png, info));
image[y] = new png_byte[png_get_rowbytes(png, info)];
}
png_read_image(png, image);
fclose(fp);
png_destroy_read_struct(&png, &info, NULL);
}
void write_png_file(const char *filename, int width, int height, png_bytep*& image)
{
int y;
FILE *fp = fopen(filename, "wb");
if(!fp)
{
fprintf(stderr, "kann %s nicht erzeugen [%s:%d]\n", filename, __FILE__, __LINE__);
exit(1);
}
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info = png_create_info_struct(png);
setjmp(png_jmpbuf(png));
png_init_io(png, fp);
// Output is 8bit depth, RGBA format.
png_set_IHDR(
png,
info,
width, height,
8,
PNG_COLOR_TYPE_RGBA,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT
);
png_write_info(png, info);
png_write_image(png, image);
png_write_end(png, NULL);
fclose(fp);
png_destroy_write_struct(&png, &info);
}
void build_histogram(int width, int height, png_bytep* image, int histogram[])
{
for(int y = 0; y < height; y++)
{
png_bytep row = image[y];
for(int x = 0; x < width; x++)
{
png_bytep px = &(row[x * 4]);
int r = px[0]; int g = px[1]; int b = px[2];
int grey = b*0.0722+g*0.7152+r*0.2126;
histogram[grey]++;
}
}
}
void transform_image(int width, int height, png_bytep* image, int treshold)
{
for(int y = 0; y < height; y++)
{
png_bytep row = image[y];
for(int x = 0; x < width; x++)
{
png_bytep px = &(row[x * 4]);
int grey = px[2]*0.0722+px[1]*0.7152+px[0]*0.2126;
px[0] = px[1] = px[2] = (grey > treshold ? 200 : 50);
}
}
}
void aufraeumen(int width, int height, png_bytep*& image)
{
for(int i = 0; i< height; i++)
{
delete [] image[i];
}
delete [] image;
}
int main(int, char**)
{
int width;
int height;
png_bytep* image;
read_image("image.png", width, height, image);
int histogram[256] = {0};
build_histogram(width, height, image, histogram);
transform_image(width, height, image, 34);
write_png_file("sw.png", width, height, image);
aufraeumen(width, height, image);
// Histogrammdatei speichern und in Libreoffice oder Excel anschauen
FILE* fp = fopen("histogramm.csv", "w");
for(int i = 0; i < 256; i++)
{
fprintf(fp, "%d\n", histogram[i]);
}
fclose(fp);
return 0;
}
Das Beispiel kann mit einem Standard-Makefile übersetzt werden.
Im Histogramm kann man sehen, dass der größte Teil der Punkte schwarz bzw. fast schwarz ist. Alle Graustuffen ab 1/3-Helligkeit kommen seltener als 200x vor.

Selbstverständlich kann man ein beliebiges Bild laden. Ich habe ein selbstgerechnetes Mandelbrot-Bildchen verwendet.

Ein Blick schneller Blick mit den Augen kommt tatsächlich ohne viel Rechnerei auf das selbe Resultat: Fast schwarz. Aber was das Auge nicht sogut kann ist zu transformieren:
