You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
169 lines
5.4 KiB
169 lines
5.4 KiB
// SPDX-FileCopyrightText: 2020 Foundation Devices, Inc. <hello@foundationdevices.com>
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
|
|
#include <assert.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "image_conversion.h"
|
|
|
|
#define INVERT_IMAGE
|
|
|
|
// This is designed only for resizing smaller and is a low-quality resize,
|
|
// but it should be quite fast.
|
|
void resize_by_nearest_neighbor(
|
|
uint8_t *grayscale, uint32_t gray_width, uint32_t gray_height, uint16_t y_start,
|
|
uint8_t *mono, uint32_t mono_width, uint32_t mono_height)
|
|
{
|
|
float step = (float)gray_width / (float)mono_width;
|
|
// printf("gray_width=%lu gray_height=%lu mono_width=%lu mono_height=%lu y_start=%u step=%f\n", gray_width, gray_height, mono_width, mono_height, y_start, step);
|
|
// float src_y = y_start;
|
|
// float src_x = 0;
|
|
uint32_t mono_span = mono_width >> 3;
|
|
|
|
// Clear the mono buffer
|
|
#ifdef INVERT_IMAGE
|
|
memset(mono, 0xFF, (mono_width * mono_height) >> 3);
|
|
#else
|
|
memset(mono, 0x00, (mono_width * mono_height) >> 3);
|
|
#endif
|
|
|
|
for (uint32_t y = 0; y < mono_height; y++)
|
|
{
|
|
for (uint32_t x = 0; x < mono_width; x++)
|
|
{
|
|
uint32_t offset = ((uint32_t)((float)(y + y_start)* step) * (uint32_t)gray_width) + (uint32_t)((float)x * step);
|
|
uint8_t gray = grayscale[offset];
|
|
|
|
// if (x < 5) {
|
|
// printf("[%02lx]=%02x ", offset, gray);
|
|
// }
|
|
|
|
// Mask the value in it
|
|
if (gray > 64)
|
|
{
|
|
uint32_t mono_offset = (y * mono_span) + (x >> 3);
|
|
uint8_t *p_byte = &mono[mono_offset];
|
|
|
|
uint8_t bit = x % 8;
|
|
#ifdef INVERT_IMAGE
|
|
*p_byte ^= 1 << (7 - bit);
|
|
#else
|
|
*p_byte |= 1 << (7 - bit);
|
|
#endif
|
|
}
|
|
// src_x += step;
|
|
}
|
|
// printf("\n");
|
|
|
|
// src_y += step;
|
|
}
|
|
}
|
|
|
|
/*
|
|
// This is designed only for resizing smaller and is a low-quality resize,
|
|
// but it should be quite fast.
|
|
void resize_by_nearest_neighbor(
|
|
uint8_t *grayscale, uint32_t gray_width, uint32_t gray_height, uint16_t y_start,
|
|
uint8_t *mono, uint32_t mono_width, uint32_t mono_height)
|
|
{
|
|
float step = (float)gray_width / (float)mono_width;
|
|
// uint32_t mono_span = mono_width / 8;
|
|
|
|
// Clear the mono buffer
|
|
#ifdef INVERT_IMAGE
|
|
memset(mono, 0xFF, (mono_width * mono_height) >> 3);
|
|
#else
|
|
memset(mono, 0x00, (mono_width * mono_height) >> 3);
|
|
#endif
|
|
|
|
for (uint32_t y = 0; y < mono_height; y++)
|
|
{
|
|
uint32_t src_y = y + y_start;
|
|
uint32_t src_offset = (uint32_t)((float)(src_y * step)) * (uint32_t)gray_width;
|
|
|
|
for (uint32_t x = 0; x < mono_width; x++)
|
|
{
|
|
uint8_t gray = grayscale[src_offset];
|
|
|
|
if (gray > 128)
|
|
{
|
|
// Draw the pixel as white
|
|
uint32_t mono_offset = (y << 5) - (y << 1) + (x >> 3); // NOTE: Hardcoded for a span of 30 bytes (240 pixels)
|
|
// uint32_t mono_offset = (y * mono_span) + (x >> 3);
|
|
uint8_t *p_byte = &mono[mono_offset];
|
|
|
|
uint8_t bit = x % 8;
|
|
#ifdef INVERT_IMAGE
|
|
*p_byte ^= 1 << (7 - bit);
|
|
#else
|
|
*p_byte |= 1 << (7 - bit);
|
|
#endif
|
|
}
|
|
src_offset += (uint32_t)step;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
void convert_rgb565_to_grayscale_and_mono(
|
|
uint16_t *rgb565,
|
|
uint8_t *grayscale,
|
|
uint32_t gray_width,
|
|
uint32_t gray_height,
|
|
uint8_t *mono,
|
|
uint32_t mono_width,
|
|
uint32_t mono_height)
|
|
{
|
|
uint32_t src_row_span = gray_height; // 1 uint16_t (2 bytes) per pixel
|
|
// uint32_t gray_row_span = gray_width; // 1 byte per pixel
|
|
// uint32_t mono_row_span = mono_width / 8; // 1 bit per pixel
|
|
assert(mono_width % 8 == 0);
|
|
|
|
// uint32_t mono_start_x = (gray_width - mono_width) / 2;
|
|
|
|
// #define PRINT_IMAGE
|
|
|
|
// Intentionally using width with y and height with x since image sensor is rotated vs. grayscale buffer
|
|
for (uint32_t y = 0; y < gray_width; y++)
|
|
{
|
|
for (uint32_t x = 0; x < gray_height; x++)
|
|
{
|
|
uint16_t pixel = rgb565[(y * src_row_span) + x];
|
|
|
|
uint16_t r = (pixel & 0xF800) >> 8;
|
|
// uint16_t g = (pixel & 0x07E0) >> 3;
|
|
// uint16_t b = pixel & 0x001F << 3;
|
|
// uint16_t sum = r + g + b;
|
|
// uint16_t element = (sum * 341) >> 10; // Equivalent to dividing by 3
|
|
uint8_t gray = (uint8_t) r;
|
|
|
|
// #define OPTIMIZATIONS
|
|
// #ifdef OPTIMIZATIONS
|
|
// uint16_t r = (pixel & 0xF800) >> 8;
|
|
// uint16_t g = (pixel & 0x07E0) >> 3;
|
|
// uint16_t b = pixel & 0x001F << 3;
|
|
// uint16_t sum = r + g + b;
|
|
// uint16_t element = (sum * 341) >> 10; // Equivalent to dividing by 3
|
|
// uint8_t gray = (uint8_t) element;
|
|
// #else
|
|
// uint16_t r = (pixel & 0xF800) >> 11;
|
|
// uint16_t g = (pixel & 0x07E0) >> 5;
|
|
// uint16_t b = pixel & 0x001F;
|
|
// uint16_t sum = (r << 1) + g + (b << 1);
|
|
// uint16_t element = sum / 3;
|
|
// uint8_t gray = element << 2;
|
|
// #endif
|
|
|
|
// Rotate coordinates for grayscale image and set pixel
|
|
uint32_t dest_y = gray_height - x;
|
|
uint32_t dest_x = y;
|
|
grayscale[dest_y * gray_width + dest_x] = gray;
|
|
}
|
|
}
|
|
|
|
resize_by_nearest_neighbor(grayscale, gray_width, gray_height, 33, mono, mono_width, mono_height);
|
|
}
|
|
|