aboutsummaryrefslogtreecommitdiff
path: root/libcrystfel/src/render.c
diff options
context:
space:
mode:
authorThomas White <taw@physics.org>2011-11-15 13:59:17 +0100
committerThomas White <taw@physics.org>2012-02-22 15:27:40 +0100
commit25c3d29ed7701cadbb3813878f25b633a7cd7c2d (patch)
tree2efd3bd84ee6948543b0bc89053f7654047b8542 /libcrystfel/src/render.c
parentfb9df2f18def2d0b8fbdbc854c8a8c10e39ce6d9 (diff)
Move indexing and rendering to libcrystfel
Diffstat (limited to 'libcrystfel/src/render.c')
-rw-r--r--libcrystfel/src/render.c442
1 files changed, 442 insertions, 0 deletions
diff --git a/libcrystfel/src/render.c b/libcrystfel/src/render.c
new file mode 100644
index 00000000..dbaca2fd
--- /dev/null
+++ b/libcrystfel/src/render.c
@@ -0,0 +1,442 @@
+/*
+ * render.c
+ *
+ * Render a high dynamic range buffer in some sensible way
+ *
+ * (c) 2006-2011 Thomas White <taw@physics.org>
+ *
+ * Part of CrystFEL - crystallography with a FEL
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+#ifdef HAVE_GTK
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#endif
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdint.h>
+
+#ifdef HAVE_TIFF
+#include <tiffio.h>
+#endif
+
+
+#include "hdf5-file.h"
+#include "render.h"
+#include "peaks.h"
+#include "filters.h"
+#include "utils.h"
+
+
+static void render_rgb(double val, double max,
+ double *rp, double *gp, double *bp)
+{
+ int s;
+ double p;
+ double r, g, b;
+
+ s = val / (max/6);
+ p = fmod(val, max/6.0);
+ p /= (max/6.0);
+
+ r = 0.0; g = 0.0; b = 0.0;
+
+ if ( (val < 0.0) ) {
+ s = 0;
+ p = 0;
+ }
+ if ( (val > max) ) {
+ s = 6;
+ }
+ switch ( s ) {
+ case 0 : { /* Black to blue */
+ r = 0; g = 0; b = p;
+ break;
+ }
+ case 1 : { /* Blue to pink */
+ r = p; g = 0; b = 1.0;
+ break;
+ }
+ case 2 : { /* Pink to red */
+ r = 1.0; g = 0; b = (1.0-p)*1.0;
+ break;
+ }
+ case 3 : { /* Red to Orange */
+ r = 1.0; g = 0.5*p; b = 0;
+ break;
+ }
+ case 4 : { /* Orange to Yellow */
+ r = 1.0; g = 0.5 + 0.5*p; b = 0;
+ break;
+ }
+ case 5 : { /* Yellow to White */
+ r = 1.0; g = 1.0; b = 1.0*p;
+ break;
+ }
+ case 6 : { /* Pixel has hit the maximum value */
+ r = 1.0; g = 1.0; b = 1.0;
+ break;
+ }
+ }
+
+ *rp = r;
+ *gp = g;
+ *bp = b;
+}
+
+
+static void render_ratio(double val, double max,
+ double *rp, double *gp, double *bp)
+{
+ if ( val <= 1.0 ) {
+ render_rgb(val, 2.0, rp, gp, bp);
+ } else {
+ /* Your homework is to simplify this expression */
+ val = ((val-1.0)/(max-1.0)) * (max/2.0) + max/2.0;
+ render_rgb(val, max, rp, gp, bp);
+ }
+}
+
+
+static void render_mono(double val, double max,
+ double *rp, double *gp, double *bp)
+{
+ double p;
+ p = val / max;
+ if ( val < 0.0 ) p = 0.0;
+ if ( val > max ) p = 1.0;
+ *rp = p;
+ *gp = p;
+ *bp = p;
+}
+
+
+static void render_invmono(double val, double max,
+ double *rp, double *gp, double *bp)
+{
+ double p;
+ p = val / max;
+ p = 1.0 - p;
+ if ( val < 0.0 ) p = 1.0;
+ if ( val > max ) p = 0.0;
+ *rp = p;
+ *gp = p;
+ *bp = p;
+}
+
+
+void render_scale(double val, double max, int scale,
+ double *rp, double *gp, double *bp)
+{
+ switch ( scale ) {
+ case SCALE_COLOUR :
+ render_rgb(val, max, rp, gp, bp);
+ break;
+ case SCALE_MONO :
+ render_mono(val, max, rp, gp, bp);
+ break;
+ case SCALE_INVMONO :
+ render_invmono(val, max, rp, gp, bp);
+ break;
+ case SCALE_RATIO :
+ render_ratio(val, max, rp, gp, bp);
+ break;
+ }
+}
+
+
+#ifdef HAVE_GTK
+
+static float *get_binned_panel(struct image *image, int binning,
+ int min_fs, int max_fs, int min_ss, int max_ss)
+{
+ float *data;
+ int x, y;
+ int w, h;
+ int fw;
+ float *in;
+
+ fw = image->width;
+ in = image->data;
+
+ /* Some pixels might get discarded */
+ w = (max_fs - min_fs + 1) / binning;
+ h = (max_ss - min_ss + 1) / binning;
+
+ data = malloc(w*h*sizeof(float));
+
+ for ( x=0; x<w; x++ ) {
+ for ( y=0; y<h; y++ ) {
+
+ double total;
+ size_t xb, yb;
+
+ total = 0;
+ for ( xb=0; xb<binning; xb++ ) {
+ for ( yb=0; yb<binning; yb++ ) {
+
+ double v;
+ v = in[binning*x+xb+min_fs + (binning*y+yb+min_ss)*fw];
+ total += v;
+
+ }
+ }
+
+ data[x+w*y] = total / ((double)binning * (double)binning);
+
+ }
+ }
+
+ return data;
+}
+
+
+/* NB This function is shared between render_get_image() and
+ * render_get_colour_scale() */
+static void render_free_data(guchar *data, gpointer p)
+{
+ free(data);
+}
+
+
+static GdkPixbuf *render_panel(struct image *image,
+ int binning, int scale, double boost,
+ int min_fs, int max_fs, int min_ss, int max_ss)
+{
+ int w, h;
+ guchar *data;
+ float *hdr;
+ int x, y;
+ double max;
+ int pw, ph;
+ int i;
+
+ /* Calculate panel width and height
+ * (add one because min and max are inclusive) */
+ pw = max_fs - min_fs + 1;
+ ph = max_ss - min_ss + 1;
+ w = pw / binning;
+ h = ph / binning;
+
+ /* High dynamic range version */
+ max = 0.0;
+ for ( i=0; i<image->width*image->height; i++ ) {
+ if ( image->data[i] > max ) max = image->data[i];
+ }
+ hdr = get_binned_panel(image, binning, min_fs, max_fs, min_ss, max_ss);
+ if ( hdr == NULL ) return NULL;
+
+ /* Rendered (colourful) version */
+ data = malloc(3*w*h);
+ if ( data == NULL ) {
+ free(hdr);
+ return NULL;
+ }
+
+ max /= boost;
+ if ( max <= 6 ) { max = 10; }
+ /* These x,y coordinates are measured relative to the bottom-left
+ * corner */
+ for ( y=0; y<h; y++ ) {
+ for ( x=0; x<w; x++ ) {
+
+ double val;
+ double r, g, b;
+
+ val = hdr[x+w*y];
+ render_scale(val, max, scale, &r, &g, &b);
+
+ /* Stuff inside square brackets makes this pixel go to
+ * the expected location in the pixbuf (which measures
+ * from the top-left corner */
+ data[3*( x+w*y )+0] = 255*r;
+ data[3*( x+w*y )+1] = 255*g;
+ data[3*( x+w*y )+2] = 255*b;
+
+ }
+ }
+
+ /* Finished with this */
+ free(hdr);
+
+ /* Create the pixbuf from the 8-bit display data */
+ return gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, FALSE, 8,
+ w, h, w*3, render_free_data, NULL);
+
+}
+
+
+/* Render an image into multiple pixbufs according to geometry */
+GdkPixbuf **render_panels(struct image *image,
+ int binning, int scale, double boost,
+ int *n_pixbufs)
+{
+ int i;
+ int np = image->det->n_panels;
+ GdkPixbuf **pixbufs;
+
+ pixbufs = calloc(np, sizeof(GdkPixbuf*));
+ if ( pixbufs == NULL ) {
+ *n_pixbufs = 0;
+ return NULL;
+ }
+
+ for ( i=0; i<np; i++ ) {
+
+ pixbufs[i] = render_panel(image, binning, scale, boost,
+ image->det->panels[i].min_fs,
+ image->det->panels[i].max_fs,
+ image->det->panels[i].min_ss,
+ image->det->panels[i].max_ss);
+
+ }
+
+ *n_pixbufs = np;
+
+ return pixbufs;
+}
+
+
+GdkPixbuf *render_get_colour_scale(size_t w, size_t h, int scale)
+{
+ guchar *data;
+ size_t x, y;
+ int max;
+
+ data = malloc(3*w*h);
+ if ( data == NULL ) return NULL;
+
+ max = h;
+
+ for ( y=0; y<h; y++ ) {
+
+ double r, g, b;
+ int val;
+
+ val = y;
+
+ render_scale(val, max, scale, &r, &g, &b);
+
+ data[3*( 0+w*(h-1-y) )+0] = 0;
+ data[3*( 0+w*(h-1-y) )+1] = 0;
+ data[3*( 0+w*(h-1-y) )+2] = 0;
+ for ( x=1; x<w; x++ ) {
+ data[3*( x+w*(h-1-y) )+0] = 255*r;
+ data[3*( x+w*(h-1-y) )+1] = 255*g;
+ data[3*( x+w*(h-1-y) )+2] = 255*b;
+ }
+
+ }
+
+ return gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, FALSE, 8,
+ w, h, w*3, render_free_data, NULL);
+}
+
+
+int render_tiff_fp(struct image *image, const char *filename)
+{
+#ifdef HAVE_TIFF
+ TIFF *th;
+ float *line;
+ int y;
+
+ th = TIFFOpen(filename, "w");
+ if ( th == NULL ) return 1;
+
+ TIFFSetField(th, TIFFTAG_IMAGEWIDTH, image->width);
+ TIFFSetField(th, TIFFTAG_IMAGELENGTH, image->height);
+ TIFFSetField(th, TIFFTAG_SAMPLESPERPIXEL, 1);
+ TIFFSetField(th, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP);
+ TIFFSetField(th, TIFFTAG_BITSPERSAMPLE, 32);
+ TIFFSetField(th, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
+ TIFFSetField(th, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(th, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(th, TIFFTAG_ROWSPERSTRIP,
+ TIFFDefaultStripSize(th, image->width*4));
+
+ line = _TIFFmalloc(TIFFScanlineSize(th));
+ for ( y=0; y<image->height; y++ ) {
+ memcpy(line, &image->data[(image->height-1-y)*image->width],
+ image->width*4);
+ TIFFWriteScanline(th, line, y, 0);
+ }
+ _TIFFfree(line);
+
+ TIFFClose(th);
+
+#else
+ STATUS("No TIFF support.\n");
+#endif
+ return 0;
+}
+
+
+int render_tiff_int16(struct image *image, const char *filename, double boost)
+{
+#ifdef HAVE_TIFF
+ TIFF *th;
+ int16_t *line;
+ int x, y;
+ double max;
+
+ th = TIFFOpen(filename, "w");
+ if ( th == NULL ) return 1;
+
+ TIFFSetField(th, TIFFTAG_IMAGEWIDTH, image->width);
+ TIFFSetField(th, TIFFTAG_IMAGELENGTH, image->height);
+ TIFFSetField(th, TIFFTAG_SAMPLESPERPIXEL, 1);
+ TIFFSetField(th, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT); /* (signed) */
+ TIFFSetField(th, TIFFTAG_BITSPERSAMPLE, 16);
+ TIFFSetField(th, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
+ TIFFSetField(th, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(th, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(th, TIFFTAG_ROWSPERSTRIP,
+ TIFFDefaultStripSize(th, image->width*4));
+
+ line = _TIFFmalloc(TIFFScanlineSize(th));
+ max = 0.0;
+ for ( y=0; y<image->height; y++ ) {
+ for ( x=0;x<image->width; x++ ) {
+ double val;
+ val = image->data[x+image->height*y];
+ if ( val > max ) max = val;
+ }
+ }
+ max /= 32767.0;
+
+ for ( y=0; y<image->height; y++ ) {
+ for ( x=0;x<image->width; x++ ) {
+
+ double val;
+
+ val = image->data[x+(image->height-1-y)*image->width];
+ val *= ((double)boost/max);
+
+ /* Clamp to 16-bit range,
+ * and work round inability of most readers to deal
+ * with signed integers. */
+ val += 1000.0;
+ if ( val > 32767.0 ) val = 32767.0;
+ if ( val < 0.0 ) val = 0.0;
+
+ line[x] = val;
+ }
+
+ TIFFWriteScanline(th, line, y, 0);
+ }
+ _TIFFfree(line);
+
+ TIFFClose(th);
+#else
+ STATUS("No TIFF support.\n");
+#endif
+ return 0;
+}
+
+#endif /* HAVE_GTK */