Files
ssocr/imgproc.c
T
Erik Auerswald 94ce321060 lazily adapt threshold to image
Instead of adapting the threshold to the image before executing
commands, adapt the threshold just before it is needed.

This allows to avoid theshold adaptation when -p, --process-only
is used with only the "grayscale" and/or "mirror" commands.

This also prepares the code to allow introduction of a new option
to avoid adapting the threshold to the original image before the
"crop" command is applied.
2025-03-23 18:54:13 +01:00

1124 lines
34 KiB
C

/* Seven Segment Optical Character Recognition Image Processing Functions */
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Copyright (C) 2004-2025 Erik Auerswald <auerswal@unix-ag.uni-kl.de> */
/* ImLib2 Header */
#include <X11/Xlib.h> /* needed by Imlib2.h */
#include <Imlib2.h>
/* standard things */
#include <stdio.h> /* puts, printf, BUFSIZ, perror, FILE */
#include <stdlib.h> /* exit */
/* string manipulation */
#include <string.h> /* strcasecmp, strcmp, strrchr */
/* trigonometry */
#include <math.h> /* sin, cos, M_PI */
#ifndef M_PI /* sometimes, M_PI is not defined */
#define M_PI 3.14159265358979323846
#endif
/* my headers */
#include "defines.h" /* defines */
#include "imgproc.h" /* image processing */
#include "help.h" /* online help */
/* global variables */
extern int ssocr_foreground;
extern int ssocr_background;
/* functions */
/*** image processing ***/
/* set foreground color */
void set_fg_color(int color)
{
ssocr_foreground = color;
}
/* set background color */
void set_bg_color(int color)
{
ssocr_background = color;
}
/* set imlib color */
void ssocr_set_color(fg_bg_t color)
{
switch(color) {
case FG:
imlib_context_set_color(ssocr_foreground, ssocr_foreground,
ssocr_foreground, 255);
break;
case BG:
imlib_context_set_color(ssocr_background, ssocr_background,
ssocr_background, 255);
break;
default:
fprintf(stderr, "%s: error: ssocr_set_color(): unknown color %d\n",
PROG, color);
exit(99);
break;
}
}
/* draw a fore- or background pixel */
void draw_fg_bg_pixel(Imlib_Image *image, int x, int y, fg_bg_t color)
{
Imlib_Image *current_image; /* save current image */
current_image = imlib_context_get_image();
imlib_context_set_image(*image);
ssocr_set_color(color);
imlib_image_draw_pixel(x,y,0);
imlib_context_set_image(current_image);
}
/* draw a foreground pixel */
void draw_fg_pixel(Imlib_Image *image, int x, int y)
{
draw_fg_bg_pixel(image, x, y, FG);
}
/* draw a background pixel */
void draw_bg_pixel(Imlib_Image *image, int x, int y)
{
draw_fg_bg_pixel(image, x, y, BG);
}
/* draw a pixel of a given color */
void draw_color_pixel(Imlib_Image *image, int x, int y, Imlib_Color color)
{
Imlib_Image *current_image; /* save current image */
current_image = imlib_context_get_image();
imlib_context_set_image(*image);
imlib_context_set_color(color.red, color.green, color.blue, color.alpha);
imlib_image_draw_pixel(x, y, 0);
imlib_context_set_image(current_image);
}
/* check if a pixel is set regarding current foreground/background colors */
int is_pixel_set(int value, double threshold)
{
switch(ssocr_foreground) {
case SSOCR_BLACK:
if(value < threshold/100.0*MAXRGB) {
return 1;
} else {
return 0;
}
break;
case SSOCR_WHITE:
if(value >= threshold/100.0*MAXRGB) {
return 1;
} else {
return 0;
}
break;
default:
fprintf(stderr, "%s: error: is_pixel_set(): foreground color neither"
" black nor white\n", PROG);
exit(99);
break;
}
}
/* set pixels that have at least mask pixels around it set (including the
* examined pixel itself) to black (foreground), all other pixels to white
* (background) */
Imlib_Image set_pixels_filter(Imlib_Image *source_image, double thresh,
luminance_t lt, int mask)
{
Imlib_Image new_image; /* construct filtered image here */
Imlib_Image current_image; /* save image pointer */
int height, width; /* image dimensions */
int x,y,i,j; /* iteration variables */
int set_pixel; /* should pixel be set or not? */
Imlib_Color color;
int lum; /* luminance value of pixel */
/* save pointer to current image */
current_image = imlib_context_get_image();
/* create a new image */
imlib_context_set_image(*source_image);
height = imlib_image_get_height();
width = imlib_image_get_width();
new_image = imlib_clone_image();
/* check for every pixel if it should be set in filtered image */
for(x=0; x<width; x++) {
for(y=0; y<height; y++) {
set_pixel=0;
for(i=x-1; i<=x+1; i++) {
for(j=y-1; j<=y+1; j++) {
if(i>=0 && i<width && j>=0 && j<height) { /* i,j inside image? */
imlib_image_query_pixel(i, j, &color);
lum = get_lum(&color, lt);
if(is_pixel_set(lum, thresh)) {
set_pixel++;
}
}
}
}
/* set pixel if at least mask pixels around it are set */
if(set_pixel >= mask) {
draw_fg_pixel(&new_image, x, y);
} else {
draw_bg_pixel(&new_image, x, y);
}
}
}
/* restore image from before function call */
imlib_context_set_image(current_image);
/* return filtered image */
return new_image;
}
Imlib_Image set_pixels_filter_iter(Imlib_Image *source_image, double thresh,
luminance_t lt, int mask, int iter)
{
int i;
Imlib_Image temp_image1, temp_image2;
imlib_context_set_image(*source_image);
temp_image1 = temp_image2 = imlib_clone_image();
for(i=0; i<iter; i++) {
temp_image2 = set_pixels_filter(&temp_image1, thresh, lt, mask);
imlib_context_set_image(temp_image1);
imlib_free_image();
temp_image1 = temp_image2;
}
return temp_image2;
}
Imlib_Image dilation(Imlib_Image *source_image, double thresh, luminance_t lt,
int n)
{
return set_pixels_filter_iter(source_image, thresh, lt, 1, n);
}
Imlib_Image erosion(Imlib_Image *source_image, double thresh, luminance_t lt,
int n)
{
return set_pixels_filter_iter(source_image, thresh, lt, 9, n);
}
Imlib_Image closing(Imlib_Image *source_image, double thresh, luminance_t lt,
int n)
{
Imlib_Image temp_image, return_image;
/* dilation n times */
temp_image = dilation(source_image, thresh, lt, n);
/* erosion n times */
return_image = erosion(&temp_image, thresh, lt, n);
imlib_context_set_image(temp_image);
imlib_free_image();
return return_image;
}
Imlib_Image opening(Imlib_Image *source_image, double thresh, luminance_t lt,
int n)
{
Imlib_Image temp_image, return_image;
/* erosion n times */
temp_image = erosion(source_image, thresh, lt, n);
/* dilation n times */
return_image = dilation(&temp_image, thresh, lt, n);
imlib_context_set_image(temp_image);
imlib_free_image();
return return_image;
}
/* set pixels with (brightness) value lower than threshold that have more than
* mask pixels around it set (including the examined pixel itself) to black
* (foreground), set pixels with (brightness) value lower than threshold that
* less or equal pixels around it set to white (background) */
Imlib_Image keep_pixels_filter(Imlib_Image *source_image, double thresh,
luminance_t lt, int mask)
{
Imlib_Image new_image; /* construct filtered image here */
Imlib_Image current_image; /* save image pointer */
int height, width; /* image dimensions */
int x,y,i,j; /* iteration variables */
int set_pixel; /* should pixel be set or not? */
Imlib_Color color;
int lum; /* luminance value of pixel */
/* save pointer to current image */
current_image = imlib_context_get_image();
/* create a new image */
imlib_context_set_image(*source_image);
height = imlib_image_get_height();
width = imlib_image_get_width();
new_image = imlib_clone_image();
/* check for every pixel if it should be set in filtered image */
for(x=0; x<width; x++) {
for(y=0; y<height; y++) {
set_pixel=0;
imlib_image_query_pixel(x, y, &color);
lum = get_lum(&color, lt);
if(is_pixel_set(lum, thresh)) { /* only test neighbors of set pixels */
for(i=x-1; i<=x+1; i++) {
for(j=y-1; j<=y+1; j++) {
if(i>=0 && i<width && j>=0 && j<height) { /* i,j inside image? */
imlib_image_query_pixel(i, j, &color);
lum = get_lum(&color, lt);
if(is_pixel_set(lum, thresh)) {
set_pixel++;
}
}
}
}
}
/* set pixel if at least mask pixels around it are set */
/* mask = 1 keeps all pixels */
if(set_pixel > mask) {
draw_fg_pixel(&new_image, x, y);
} else {
draw_bg_pixel(&new_image, x, y);
}
}
}
/* restore image from before function call */
imlib_context_set_image(current_image);
/* return filtered image */
return new_image;
}
Imlib_Image remove_isolated(Imlib_Image *source_image, double thresh,
luminance_t lt)
{
return keep_pixels_filter(source_image, thresh, lt, 1);
}
/* gray stretching, i.e. lum<t1 => lum=0, lum>t2 => lum=100,
* else lum=((lum-t1)*MAXRGB)/(t2-t1) */
Imlib_Image gray_stretch(Imlib_Image *source_image, double t1, double t2,
luminance_t lt)
{
Imlib_Image new_image; /* construct filtered image here */
Imlib_Image current_image; /* save image pointer */
int height, width; /* image dimensions */
int x,y; /* iteration variables */
Imlib_Color color;
int lum; /* luminance value of pixel */
/* do nothing if t1>=t2 */
if(t1 >= t2) {
fprintf(stderr, "%s: error: gray_stretch(): t1=%.2f >= t2=%.2f\n",
PROG, t1, t2);
exit(99);
}
/* check if 0 < t1,t2 < MAXRGB */
if(t1 <= 0.0) {
fprintf(stderr, "%s: error: gray_stretch(): t1=%.2f <= 0.0\n", PROG, t1);
exit(99);
}
if(t2 >= MAXRGB) {
fprintf(stderr, "%s: error: gray_stretch(): t2=%.2f >= %d.0\n",
PROG, t2, MAXRGB);
exit(99);
}
/* save pointer to current image */
current_image = imlib_context_get_image();
/* create a new image */
imlib_context_set_image(*source_image);
height = imlib_image_get_height();
width = imlib_image_get_width();
new_image = imlib_clone_image();
/* gray stretch image */
for(x=0; x<width; x++) {
for(y=0; y<height; y++) {
imlib_image_query_pixel(x, y, &color);
lum = get_lum(&color, lt);
imlib_context_set_image(new_image);
if(lum<=t1) {
imlib_context_set_color(0, 0, 0, color.alpha);
} else if(lum>=t2) {
imlib_context_set_color(MAXRGB, MAXRGB, MAXRGB, color.alpha);
} else {
imlib_context_set_color(clip(((lum-t1)*255)/(t2-t1),0,255),
clip(((lum-t1)*255)/(t2-t1),0,255),
clip(((lum-t1)*255)/(t2-t1),0,255),
color.alpha);
}
imlib_image_draw_pixel(x, y, 0);
imlib_context_set_image(*source_image);
}
}
/* restore image from before function call */
imlib_context_set_image(current_image);
/* return filtered image */
return new_image;
}
/* use dynamic (aka adaptive) local thresholding to create monochrome image */
/* ww and wh are the width and height of the rectangle used to find the
* threshold value */
Imlib_Image dynamic_threshold(Imlib_Image *source_image,double t,luminance_t lt,
int ww, int wh)
{
Imlib_Image new_image; /* construct filtered image here */
Imlib_Image current_image; /* save image pointer */
int height, width; /* image dimensions */
int x,y; /* iteration variables */
Imlib_Color color;
int lum;
double thresh;
/* save pointer to current image */
current_image = imlib_context_get_image();
/* create a new image */
imlib_context_set_image(*source_image);
height = imlib_image_get_height();
width = imlib_image_get_width();
new_image = imlib_clone_image();
/* check for every pixel if it should be set in filtered image */
for(x=0; x<width; x++) {
for(y=0; y<height; y++) {
imlib_image_query_pixel(x, y, &color);
lum = get_lum(&color, lt);
thresh = get_threshold(source_image, t/100.0, lt, x-ww/2, y-ww/2, ww, wh);
if(is_pixel_set(lum, thresh)) {
draw_fg_pixel(&new_image, x, y);
} else {
draw_bg_pixel(&new_image, x, y);
}
}
}
/* restore image from before function call */
imlib_context_set_image(current_image);
/* return filtered image */
return new_image;
}
/* use simple thresholding to generate monochrome image */
Imlib_Image make_mono(Imlib_Image *source_image, double thresh, luminance_t lt)
{
Imlib_Image new_image; /* construct filtered image here */
Imlib_Image current_image; /* save image pointer */
int height, width; /* image dimensions */
int x,y; /* iteration variables */
Imlib_Color color;
int lum; /* pixel luminance */
/* save pointer to current image */
current_image = imlib_context_get_image();
/* create a new image */
imlib_context_set_image(*source_image);
height = imlib_image_get_height();
width = imlib_image_get_width();
new_image = imlib_clone_image();
/* check for every pixel if it should be set in filtered image */
for(x=0; x<width; x++) {
for(y=0; y<height; y++) {
imlib_image_query_pixel(x, y, &color);
lum = get_lum(&color, lt);
if(is_pixel_set(lum, thresh)) {
draw_fg_pixel(&new_image, x, y);
} else {
draw_bg_pixel(&new_image, x, y);
}
}
}
/* restore image from before function call */
imlib_context_set_image(current_image);
/* return filtered image */
return new_image;
}
/* adapt threshold to image values values */
double adapt_threshold(Imlib_Image *image, double thresh, luminance_t lt,
unsigned int flags, int force_update)
{
double t = thresh;
static int is_adapted = 0;
if(is_adapted && !force_update) {
fprintf(stderr, "threshold is already adjusted to image\n");
} else if(!(flags & ABSOLUTE_THRESHOLD)) {
if(flags & DEBUG_OUTPUT)
fprintf(stderr, "adjusting threshold to image: %f ->", t);
t = get_threshold(image, thresh/100.0, lt, 0, 0, -1, -1);
if(flags & DEBUG_OUTPUT)
fprintf(stderr, " %f\n", t);
if(flags & DO_ITERATIVE_THRESHOLD) {
if(flags & DEBUG_OUTPUT)
fprintf(stderr, "doing iterative_thresholding: %f ->", t);
t = iterative_threshold(image, t, lt);
if(flags & DEBUG_OUTPUT)
fprintf(stderr, " %f\n", t);
}
is_adapted = 1;
}
if((flags & VERBOSE) || (flags & DEBUG_OUTPUT)) {
fprintf(stderr, "using threshold %.2f\n", t);
}
return t;
}
/* compute dynamic threshold value from the rectangle (x,y),(x+w,y+h) of
* source_image */
double get_threshold(Imlib_Image *source_image, double fraction, luminance_t lt,
int x, int y, int w, int h)
{
Imlib_Image current_image; /* save image pointer */
int height, width; /* image dimensions */
int xi,yi; /* iteration variables */
Imlib_Color color;
int lum; /* luminance of pixel */
double minval=(double)MAXRGB, maxval=0.0;
/* save pointer to current image */
current_image = imlib_context_get_image();
/* get image dimensions */
imlib_context_set_image(*source_image);
height = imlib_image_get_height();
width = imlib_image_get_width();
/* special value -1 for width or height means image width/height */
if(w == -1) w = width;
if(h == -1) h = width;
/* assure valid coordinates */
if(x+w > width) x = width-w;
if(y+h > height) y = height-h;
if(x<0) x=0;
if(y<0) y=0;
/* find the threshold value to differentiate between dark and light */
for(xi=0; (xi<w) && (xi<width); xi++) {
for(yi=0; (yi<h) && (yi<height); yi++) {
imlib_image_query_pixel(x+xi, y+yi, &color);
lum = get_lum(&color, lt);
if(lum < minval) minval = lum;
if(lum > maxval) maxval = lum;
}
}
/* restore image from before function call */
imlib_context_set_image(current_image);
return (minval + fraction * (maxval - minval)) * 100 / MAXRGB;
}
/* determine threshold by an iterative method */
double iterative_threshold(Imlib_Image *source_image, double thresh,
luminance_t lt)
{
Imlib_Image current_image; /* save image pointer */
int height, width; /* image dimensions */
int xi,yi; /* iteration variables */
Imlib_Color color;
int lum; /* luminance of pixel */
unsigned int size_white, size_black; /* size of black and white groups */
unsigned long int sum_white, sum_black; /* sum of black and white groups */
unsigned int avg_white, avg_black; /* average values of black and white */
double old_thresh; /* old threshold computed by last iteration step */
double new_thresh; /* new threshold computed by current iteration step */
int thresh_lum; /* luminance value of threshold */
/* normalize threshold (was given as a percentage) */
new_thresh = thresh / 100.0;
/* save pointer to current image */
current_image = imlib_context_get_image();
/* get image dimensions */
imlib_context_set_image(*source_image);
height = imlib_image_get_height();
width = imlib_image_get_width();
/* find the threshold value to differentiate between dark and light */
do {
thresh_lum = MAXRGB * new_thresh;
old_thresh = new_thresh;
size_black = sum_black = size_white = sum_white = 0;
for(xi=0; xi<width; xi++) {
for(yi=0; yi<height; yi++) {
imlib_image_query_pixel(xi, yi, &color);
lum = get_lum(&color, lt);
if(lum <= thresh_lum) {
size_black++;
sum_black += lum;
} else {
size_white++;
sum_white += lum;
}
}
}
if(!size_white) {
fprintf(stderr, "%s: iterative_threshold(): error: no white pixels\n",
PROG);
imlib_context_set_image(current_image);
return thresh;
}
if(!size_black) {
fprintf(stderr, "%s: iterative_threshold(): error: no black pixels\n",
PROG);
imlib_context_set_image(current_image);
return thresh;
}
avg_white = sum_white / size_white;
avg_black = sum_black / size_black;
new_thresh = (avg_white + avg_black) / (2.0 * MAXRGB);
} while(fabs(new_thresh - old_thresh) > EPSILON);
/* restore image from before function call */
imlib_context_set_image(current_image);
return new_thresh * 100;
}
/* get minimum and maximum lum values */
void get_minmaxval(Imlib_Image *source_image, luminance_t lt,
double *min, double *max)
{
Imlib_Image current_image; /* save image pointer */
int w, h; /* image dimensions */
int xi,yi; /* iteration variables */
Imlib_Color color; /* Imlib2 RGBA color structure */
int lum = 0;
*min = MAXRGB;
*max = 0;
/* save pointer to current image */
current_image = imlib_context_get_image();
/* get image dimensions */
imlib_context_set_image(*source_image);
h = imlib_image_get_height();
w = imlib_image_get_width();
/* find the minimum value in the image */
for(xi=0; xi<w; xi++) {
for(yi=0; yi<h; yi++) {
imlib_image_query_pixel(xi, yi, &color);
lum = clip(get_lum(&color, lt),0,255);
if(lum < *min) *min = lum;
if(lum > *max) *max = lum;
}
}
/* restore image from before function call */
imlib_context_set_image(current_image);
}
/* draw a white (background) border around image, overwriting image contents
* beneath border*/
Imlib_Image white_border(Imlib_Image *source_image, int bdwidth)
{
Imlib_Image new_image; /* construct filtered image here */
Imlib_Image current_image; /* save image pointer */
int height, width; /* image dimensions */
int x,y; /* coordinates of upper left corner of rectangles */
/* save pointer to current image */
current_image = imlib_context_get_image();
/* create a new image */
imlib_context_set_image(*source_image);
height = imlib_image_get_height();
width = imlib_image_get_width();
new_image = imlib_clone_image();
/* assure border width has a legal value */
if(bdwidth > width/2) bdwidth = width/2;
if(bdwidth > height/2) bdwidth = height/2;
/* draw white (background) rectangle around new image */
for(x=0, y=0; x<bdwidth; x++, y++) {
imlib_context_set_image(new_image);
ssocr_set_color(BG);
imlib_image_draw_rectangle(x, y, width-2*x, height-2*y);
}
/* restore image from before function call */
imlib_context_set_image(current_image);
/* return filtered image */
return new_image;
}
/* shear the image
* the top line is unchanged
* the bottom line is moved offset pixels to the right
* the other lines are moved yPos*offset/(height-1) pixels to the right
* white pixels are inserted at the left side */
Imlib_Image shear(Imlib_Image *source_image, int offset)
{
Imlib_Image new_image; /* construct filtered image here */
Imlib_Image current_image; /* save image pointer */
int height, width; /* image dimensions */
int x,y; /* iteration variables */
int shift; /* current shift-width */
Imlib_Color color_return; /* for imlib_query_pixel() */
/* save pointer to current image */
current_image = imlib_context_get_image();
/* create a new image */
imlib_context_set_image(*source_image);
height = imlib_image_get_height();
width = imlib_image_get_width();
new_image = imlib_clone_image();
/* move every line to the right */
for(y=1; y<height; y++) {
shift = y * offset / (height-1);
/* copy pixels */
for(x=width-1; x>=shift; x--) {
imlib_image_query_pixel(x-shift, y, &color_return);
draw_color_pixel(&new_image, x, y, color_return);
}
/* fill with background */
for(x=0; x<shift; x++) {
draw_bg_pixel(&new_image, x, y);
}
}
/* restore image from before function call */
imlib_context_set_image(current_image);
/* return filtered image */
return new_image;
}
/* rotate the image */
Imlib_Image rotate(Imlib_Image *source_image, double theta)
{
Imlib_Image new_image; /* construct filtered image here */
Imlib_Image current_image; /* save image pointer */
int height, width; /* image dimensions */
int x,y; /* iteration variables / target coordinates */
int sx,sy; /* source coordinates */
Imlib_Color c; /* for imlib_query_pixel() */
/* save pointer to current image */
current_image = imlib_context_get_image();
/* create a new image */
imlib_context_set_image(*source_image);
height = imlib_image_get_height();
width = imlib_image_get_width();
new_image = imlib_clone_image();
/* convert theta from degrees to radians */
theta = theta / 360 * 2.0 * M_PI;
/* create rotated image
* (some parts of the original image will be lost) */
for(x = 0; x < width; x++) {
for(y = 0; y < height; y++) {
sx = (x-width/2) * cos(theta) + (y-height/2) * sin(theta) + width/2;
sy = (y-height/2) * cos(theta) - (x-width/2) * sin(theta) + height/2;
if((sx >= 0) && (sx <= width) && (sy >= 0) && (sy <= height)) {
imlib_image_query_pixel(sx, sy, &c);
draw_color_pixel(&new_image, x, y, c);
} else {
draw_bg_pixel(&new_image, x, y);
}
}
}
/* restore image from before function call */
imlib_context_set_image(current_image);
/* return filtered image */
return new_image;
}
/* mirror image horizontally or vertically */
Imlib_Image mirror(Imlib_Image *source_image, direction_t direction)
{
Imlib_Image new_image; /* construct filtered image here */
Imlib_Image current_image; /* save image pointer */
int height, width; /* image dimensions */
int x,y; /* iteration variables / target coordinates */
Imlib_Color c; /* for imlib_query_pixel() */
/* save pointer to current image */
current_image = imlib_context_get_image();
/* create a new image */
imlib_context_set_image(*source_image);
height = imlib_image_get_height();
width = imlib_image_get_width();
new_image = imlib_clone_image();
/* create mirrored image */
if(direction == HORIZONTAL) {
for(x = width-1; x>=0; x--) {
for(y = 0; y < height; y++) {
imlib_image_query_pixel(width - 1 - x, y, &c);
draw_color_pixel(&new_image, x, y, c);
}
}
} else if(direction == VERTICAL) {
for(x = 0; x < width; x++) {
for(y = height-1; y >= 0; y--) {
imlib_image_query_pixel(x, height - 1 - y, &c);
draw_color_pixel(&new_image, x, y, c);
}
}
}
/* restore image from before function call */
imlib_context_set_image(current_image);
/* return filtered image */
return new_image;
}
/* turn image to grayscale */
Imlib_Image grayscale(Imlib_Image *source_image, luminance_t lt)
{
Imlib_Image new_image; /* construct grayscale image here */
Imlib_Image current_image; /* save image pointer */
int height, width; /* image dimensions */
int x,y; /* iteration variables */
Imlib_Color color; /* Imlib2 color structure */
int lum=0;
/* save pointer to current image */
current_image = imlib_context_get_image();
/* create a new image */
imlib_context_set_image(*source_image);
height = imlib_image_get_height();
width = imlib_image_get_width();
new_image = imlib_clone_image();
/* transform image to grayscale */
for(x=0; x<width; x++) {
for(y=0; y<height; y++) {
imlib_context_set_image(*source_image);
imlib_image_query_pixel(x, y, &color);
imlib_context_set_image(new_image);
lum = clip(get_lum(&color, lt),0,255);
imlib_context_set_color(lum, lum, lum, color.alpha);
imlib_image_draw_pixel(x, y, 0);
}
}
/* restore image from before function call */
imlib_context_set_image(current_image);
/* return filtered image */
return new_image;
}
/* use simple thresholding to generate an inverted monochrome image */
Imlib_Image invert(Imlib_Image *source_image, double thresh, luminance_t lt)
{
Imlib_Image new_image; /* construct filtered image here */
Imlib_Image current_image; /* save image pointer */
int height, width; /* image dimensions */
int x,y; /* iteration variables */
Imlib_Color color;
int lum; /* pixel luminance */
/* save pointer to current image */
current_image = imlib_context_get_image();
/* create a new image */
imlib_context_set_image(*source_image);
height = imlib_image_get_height();
width = imlib_image_get_width();
new_image = imlib_clone_image();
/* check for every pixel if it should be set in filtered image */
for(x=0; x<width; x++) {
for(y=0; y<height; y++) {
imlib_image_query_pixel(x, y, &color);
lum = get_lum(&color, lt);
if(is_pixel_set(lum, thresh)) {
draw_bg_pixel(&new_image, x, y);
} else {
draw_fg_pixel(&new_image, x, y);
}
}
}
/* restore image from before function call */
imlib_context_set_image(current_image);
/* return filtered image */
return new_image;
}
/* crop image */
Imlib_Image crop(Imlib_Image *source_image, int x, int y, int w, int h)
{
Imlib_Image new_image; /* construct filtered image here */
Imlib_Image current_image; /* save image pointer */
int width, height; /* source image dimensions */
/* save pointer to current image */
current_image = imlib_context_get_image();
/* get width and height of source image */
imlib_context_set_image(*source_image);
width = imlib_image_get_width();
height = imlib_image_get_height();
/* get sane values */
if(x < 0) x = 0;
if(y < 0) y = 0;
if(x >= width) x = width - 1;
if(y >= height) y = height - 1;
if(x + w > width) w = width - x;
if(y + h > height) h = height - x;
/* create the new image */
imlib_context_set_image(*source_image);
new_image = imlib_create_cropped_image(x, y, w, h);
/* restore image from before function call */
imlib_context_set_image(current_image);
/* return filtered image */
return new_image;
}
/* compute luminance from RGB values */
int get_lum(Imlib_Color *color, luminance_t lt)
{
switch(lt) {
case REC709: return get_lum_709(color);
case REC601: return get_lum_601(color);
case LINEAR: return get_lum_lin(color);
case MINIMUM: return get_lum_min(color);
case MAXIMUM: return get_lum_max(color);
case RED: return get_lum_red(color);
case GREEN: return get_lum_green(color);
case BLUE: return get_lum_blue(color);
default:
fprintf(stderr, "%s: error: get_lum(): unknown transfer function"
" no. %d\n", PROG, lt);
exit(99);
}
}
/* compute luminance Y_709 from linear RGB values */
int get_lum_709(Imlib_Color *color)
{
return 0.2125*color->red + 0.7154*color->green + 0.0721*color->blue;
}
/* compute luminance Y_601 from gamma corrected (non-linear) RGB values */
int get_lum_601(Imlib_Color *color)
{
return 0.299*color->red + 0.587*color->green + 0.114*color->blue;
}
/* compute luminance Y = (R+G+B)/3 */
int get_lum_lin(Imlib_Color *color)
{
return (color->red + color->green + color->blue) / 3;
}
/* compute luminance Y = min(R,G,B) as used in GNU Ocrad 0.14 */
int get_lum_min(Imlib_Color *color)
{
return (color->red < color->green) ?
((color->red < color->blue) ? color->red : color->blue) :
((color->green < color->blue) ? color->green : color->blue);
}
/* compute luminance Y = max(R,G,B) */
int get_lum_max(Imlib_Color *color)
{
return (color->red > color->green) ?
((color->red > color->blue) ? color->red : color->blue) :
((color->green > color->blue) ? color->green : color->blue);
}
/* compute luminance Y = R */
int get_lum_red(Imlib_Color *color)
{
return color->red;
}
/* compute luminance Y = G */
int get_lum_green(Imlib_Color *color)
{
return color->green;
}
/* compute luminance Y = B */
int get_lum_blue(Imlib_Color *color)
{
return color->blue;
}
/* clip value thus that it is in the given interval [min,max] */
int clip(int value, int min, int max)
{
return (value < min) ? min : ((value > max) ? max : value);
}
/* save image to file */
void save_image(const char *image_type, Imlib_Image *image, const char *fmt,
const char *filename, unsigned int flags)
{
const char *tmp;
Imlib_Image *current_image;
Imlib_Load_Error save_error=0;
const char *const stdout_file = "/proc/self/fd/1";
current_image = imlib_context_get_image();
imlib_context_set_image(image);
/* interpret - as STDOUT */
if(strcmp("-", filename) == 0)
filename = stdout_file;
/* get file format for image */
if(fmt) { /* use provided format string */
tmp = fmt;
} else { /* use file name extension */
tmp = strrchr(filename, '.');
if(tmp)
tmp++;
}
if(tmp) {
if(flags & VERBOSE)
fprintf(stderr, "using %s format for %s image\n", tmp, image_type);
imlib_image_set_format(tmp);
} else { /* use png as default */
if(flags & VERBOSE)
fprintf(stderr, "using png format for %s image\n", image_type);
imlib_image_set_format("png");
}
/* write image to disk */
if(flags & VERBOSE)
fprintf(stderr, "writing %s image to file %s\n", image_type, filename);
imlib_save_image_with_error_return(filename, &save_error);
if(save_error && save_error != IMLIB_LOAD_ERROR_NONE) {
fprintf(stderr, "%s: error saving image file %s\n", PROG, filename);
report_imlib_error(save_error);
}
imlib_context_set_image(current_image);
}
/* parse KEYWORD from --luminace option */
luminance_t parse_lum(char *keyword)
{
if(strcasecmp(keyword, "help") == 0) {
print_lum_help();
exit(42);
} else if(strcasecmp(keyword, "rec601") == 0) {
return REC601;
} else if(strcasecmp(keyword, "rec709") == 0) {
return REC709;
} else if(strcasecmp(keyword, "linear") == 0) {
return LINEAR;
} else if(strcasecmp(keyword, "minimum") == 0) {
return MINIMUM;
} else if(strcasecmp(keyword, "maximum") == 0) {
return MAXIMUM;
} else if(strcasecmp(keyword, "red") == 0) {
return RED;
} else if(strcasecmp(keyword, "green") == 0) {
return GREEN;
} else if(strcasecmp(keyword, "blue") == 0) {
return BLUE;
} else {
return LUM_PARSE_ERROR;
}
}
/* report Imlib2 load/save error to stderr */
void report_imlib_error(Imlib_Load_Error error)
{
fputs(PROG ": Imlib2 error code: ",stderr);
switch (error) {
case IMLIB_LOAD_ERROR_NONE:
fputs("IMLIB_LOAD_ERROR_NONE\n", stderr);
break;
case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST:
fputs("IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST\n", stderr);
break;
case IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY:
fputs("IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY\n", stderr);
break;
case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ:
fputs("IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ\n", stderr);
break;
case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT:
fputs("IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT\n", stderr);
break;
case IMLIB_LOAD_ERROR_PATH_TOO_LONG:
fputs("IMLIB_LOAD_ERROR_PATH_TOO_LONG\n", stderr);
break;
case IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT:
fputs("IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT\n", stderr);
break;
case IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY:
fputs("IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY\n", stderr);
break;
case IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE:
fputs("IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE\n", stderr);
break;
case IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS:
fputs("IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS\n", stderr);
break;
case IMLIB_LOAD_ERROR_OUT_OF_MEMORY:
fputs("IMLIB_LOAD_ERROR_OUT_OF_MEMORY\n", stderr);
break;
case IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS:
fputs("IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS\n", stderr);
break;
case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE:
fputs("IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE\n", stderr);
break;
case IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE:
fputs("IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE\n", stderr);
break;
case IMLIB_LOAD_ERROR_UNKNOWN:
fputs("IMLIB_LOAD_ERROR_UNKNOWN\n", stderr);
break;
default:
fprintf(stderr, "unknown error code %d, please report\n", error);
break;
}
}