summaryrefslogtreecommitdiff
path: root/image.c
diff options
context:
space:
mode:
Diffstat (limited to 'image.c')
-rw-r--r--image.c202
1 files changed, 202 insertions, 0 deletions
diff --git a/image.c b/image.c
index 9ab12c9..ac5387a 100644
--- a/image.c
+++ b/image.c
@@ -28,6 +28,9 @@
#include "image.h"
#include "utils.h"
+typedef uint8_t u8x16 __attribute__((vector_size(16)));
+typedef uint16_t u16x8 __attribute__((vector_size(16)));
+
static void
image_free(struct image *image, void *user_data)
{
@@ -71,6 +74,205 @@ image_unref(struct image *image)
}
static void
+image_histogram_sgrbg8(const struct image *image, uint32_t histogram[3][256])
+{
+ assert(image->width % 2 == 0);
+ assert(image->height % 2 == 0);
+ assert(image->stride == image->width);
+ memset(histogram, 0, 3 * 256 * sizeof(uint32_t));
+ const uint8_t *p = image->pixels;
+ for (int i = 0; i < image->height; i += 2) {
+ for (int j = 0; j < image->width; j += 2) {
+ uint8_t g = *p++;
+ histogram[1][g] += 2;
+ uint8_t r = *p++;
+ histogram[0][r] += 4;
+ }
+ for (int j = 0; j < image->width; j += 2) {
+ uint8_t b = *p++;
+ histogram[2][b] += 4;
+ uint8_t g = *p++;
+ histogram[1][g] += 2;
+ }
+ }
+}
+
+static void
+image_histogram_xrgb32(const struct image *image, uint32_t histogram[3][256])
+{
+ assert(image->stride == image->width * 4);
+ memset(histogram, 0, 3 * 256 * sizeof(uint32_t));
+ const uint8_t *p = image->pixels;
+ const uint8_t *pend = image->pixels + image->height * image->stride;
+ while (p != pend) {
+ uint8_t b = *p++;
+ uint8_t g = *p++;
+ uint8_t r = *p++;
+ p++;
+ histogram[0][r]++;
+ histogram[1][g]++;
+ histogram[2][b]++;
+ }
+}
+
+void
+image_histogram(const struct image *image, uint32_t histogram[3][256])
+{
+ switch (image->format) {
+ case IMAGE_FORMAT_SGRBG8:
+ image_histogram_sgrbg8(image, histogram);
+ break;
+ case IMAGE_FORMAT_XBGR32:
+ image_histogram_xrgb32(image, histogram);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+void
+image_white_balance_calibrate(const struct image *image,
+ struct image_white_balance_reference *reference)
+{
+ uint32_t histogram[3][256];
+ image_histogram(image, histogram);
+ const uint32_t top_ignore = image->width * image->height / 1000;
+ uint8_t top[3];
+ for (int i = 0; i < 3; i++) {
+ uint32_t acc = 0;
+ uint8_t j;
+ for (j = 255; acc < top_ignore && j > 0; j--) {
+ acc += histogram[i][j];
+ }
+ top[i] = j;
+ }
+ reference->r = top[0];
+ reference->g = top[1];
+ reference->b = top[2];
+}
+
+static void
+image_white_balance_factors(
+ const struct image_white_balance_reference *reference,
+ uint16_t *fr, uint16_t *fg, uint16_t *fb)
+{
+ uint8_t wr = reference->r;
+ uint8_t wg = reference->g;
+ uint8_t wb = reference->b;
+ if (wr > wg && wr > wb) {
+ *fr = 256;
+ *fg = 256 * wr / wg;
+ *fb = 256 * wr / wb;
+ } else if (wg > wr && wg > wb) {
+ *fr = 256 * wg / wr;
+ *fg = 256;
+ *fb = 256 * wg / wb;
+ } else {
+ *fr = 256 * wb / wr;
+ *fg = 256 * wb / wg;
+ *fb = 256;
+ }
+}
+
+static void
+image_white_balance_sgrbg8(struct image *image,
+ const struct image_white_balance_reference *reference)
+{
+ assert(image->width % 16 == 0);
+ assert(image->height % 2 == 0);
+ assert(image->stride == image->width);
+ uint16_t fr, fg, fb;
+ image_white_balance_factors(reference, &fr, &fg, &fb);
+ u8x16 *p = (u8x16 *) image->pixels;
+ const u8x16 zero = { 0 };
+ const u8x16 ex0 = { 0, 16, 1, 17, 2, 18, 3, 19,
+ 4, 20, 5, 21, 6, 22, 7, 23 };
+ const u8x16 ex1 = { 8, 24, 9, 25, 10, 26, 11, 27,
+ 12, 28, 13, 29, 14, 30, 15, 31 };
+ const u16x8 mulo = { fg, fr, fg, fr, fg, fr, fg, fr };
+ const u16x8 mule = { fb, fg, fb, fg, fb, fg, fb, fg };
+ const u8x16 imp = { 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30 };
+ for (int i = 0; i < image->height; i += 2) {
+ for (int j = 0; j < image->width; j += 16) {
+ u8x16 pix4 = *p;
+ u16x8 pix2_0 = (u16x8) __builtin_shuffle(pix4, zero, ex0);
+ u16x8 pix2_0_ovf = __builtin_ia32_pmulhuw128(pix2_0, mulo)
+ != (u16x8) zero;
+ pix2_0 = ((pix2_0 * mulo) | pix2_0_ovf) / 256;
+ u16x8 pix2_1 = (u16x8) __builtin_shuffle(pix4, zero, ex1);
+ u16x8 pix2_1_ovf = __builtin_ia32_pmulhuw128(pix2_1, mulo)
+ != (u16x8) zero;
+ pix2_1 = ((pix2_1 * mulo) | pix2_1_ovf) / 256;
+ pix4 = __builtin_shuffle((u8x16) pix2_0, (u8x16) pix2_1, imp);
+ *p++ = pix4;
+ }
+ for (int j = 0; j < image->width; j += 16) {
+ u8x16 pix4 = *p;
+ u16x8 pix2_0 = (u16x8) __builtin_shuffle(pix4, zero, ex0);
+ u16x8 pix2_0_ovf = __builtin_ia32_pmulhuw128(pix2_0, mule)
+ != (u16x8) zero;
+ pix2_0 = ((pix2_0 * mule) | pix2_0_ovf) / 256;
+ u16x8 pix2_1 = (u16x8) __builtin_shuffle(pix4, zero, ex1);
+ u16x8 pix2_1_ovf = __builtin_ia32_pmulhuw128(pix2_1, mule)
+ != (u16x8) zero;
+ pix2_1 = ((pix2_1 * mule) | pix2_1_ovf) / 256;
+ pix4 = __builtin_shuffle((u8x16) pix2_0, (u8x16) pix2_1, imp);
+ *p++ = pix4;
+ }
+ }
+}
+
+static void
+image_white_balance_xbgr32(struct image *image,
+ const struct image_white_balance_reference *reference)
+{
+ assert(image->width % 4 == 0);
+ assert(image->stride == image->width * 4);
+ uint16_t fr, fg, fb;
+ image_white_balance_factors(reference, &fr, &fg, &fb);
+ u8x16 *p = (u8x16 *) image->pixels;
+ u8x16 *pend = (u8x16 *) (image->pixels + image->height * image->stride);
+ const u8x16 zero = { 0 };
+ const u8x16 ex0 = { 0, 16, 1, 17, 2, 18, 3, 19,
+ 4, 20, 5, 21, 6, 22, 7, 23 };
+ const u8x16 ex1 = { 8, 24, 9, 25, 10, 26, 11, 27,
+ 12, 28, 13, 29, 14, 30, 15, 31 };
+ const u16x8 mul = { fb, fg, fr, 255, fb, fg, fr, 255 };
+ const u8x16 imp = { 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30 };
+ while (p != pend) {
+ u8x16 pix4 = *p;
+ u16x8 pix2_0 = (u16x8) __builtin_shuffle(pix4, zero, ex0);
+ u16x8 pix2_0_ovf = __builtin_ia32_pmulhuw128(pix2_0, mul)
+ != (u16x8) zero;
+ pix2_0 = ((pix2_0 * mul) | pix2_0_ovf) / 256;
+ u16x8 pix2_1 = (u16x8) __builtin_shuffle(pix4, zero, ex1);
+ u16x8 pix2_1_ovf = __builtin_ia32_pmulhuw128(pix2_1, mul)
+ != (u16x8) zero;
+ pix2_1 = ((pix2_1 * mul) | pix2_1_ovf) / 256;
+ pix4 = __builtin_shuffle((u8x16) pix2_0, (u8x16) pix2_1, imp);
+ *p++ = pix4;
+ }
+}
+
+void
+image_white_balance(struct image *image,
+ const struct image_white_balance_reference *reference)
+{
+ switch (image->format) {
+ case IMAGE_FORMAT_SGRBG8:
+ image_white_balance_sgrbg8(image, reference);
+ break;
+ case IMAGE_FORMAT_XBGR32:
+ image_white_balance_xbgr32(image, reference);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void
image_convert_to_xbgr32_from_sgrbg8(struct image *dst,
const struct image *src)
{