mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
commit
6877b3afe6
@ -154,6 +154,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/, \
|
||||
FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/img/,\
|
||||
binary.o \
|
||||
blob.o \
|
||||
clahe.o \
|
||||
draw.o \
|
||||
qrcode.o \
|
||||
apriltag.o \
|
||||
|
||||
@ -24,6 +24,7 @@ SRCS += $(addprefix , \
|
||||
SRCS += $(addprefix img/, \
|
||||
binary.c \
|
||||
blob.c \
|
||||
clahe.c \
|
||||
draw.c \
|
||||
qrcode.c \
|
||||
apriltag.c \
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
#include "imlib.h"
|
||||
|
||||
void imlib_binary(image_t *img, list_t *thresholds, bool invert, bool zero)
|
||||
void imlib_binary(image_t *img, list_t *thresholds, bool invert, bool zero, image_t *mask)
|
||||
{
|
||||
for (list_lnk_t *it = iterator_start_from_head(thresholds); it; it = iterator_next(it)) {
|
||||
color_thresholds_list_lnk_data_t lnk_data;
|
||||
@ -14,22 +14,22 @@ void imlib_binary(image_t *img, list_t *thresholds, bool invert, bool zero)
|
||||
switch(img->bpp) {
|
||||
case IMAGE_BPP_BINARY: {
|
||||
if (!zero) {
|
||||
for (uint32_t *start = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, 0),
|
||||
*end = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, img->h);
|
||||
start < end; start++) {
|
||||
for (int i = 0; i < UINT32_T_BITS; i++) {
|
||||
IMAGE_PUT_BINARY_PIXEL_FAST(start, i,
|
||||
COLOR_THRESHOLD_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(start, i), &lnk_data, invert)
|
||||
? COLOR_BINARY_MAX : COLOR_BINARY_MIN);
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
if (mask && (!image_get_mask_pixel(mask, x, y))) continue;
|
||||
IMAGE_PUT_BINARY_PIXEL_FAST(row_ptr, x,
|
||||
COLOR_THRESHOLD_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x), &lnk_data, invert));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (uint32_t *start = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, 0),
|
||||
*end = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, img->h);
|
||||
start < end; start++) {
|
||||
for (int i = 0; i < UINT32_T_BITS; i++) {
|
||||
if (COLOR_THRESHOLD_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(start, i), &lnk_data, invert))
|
||||
IMAGE_PUT_BINARY_PIXEL_FAST(start, i, COLOR_BINARY_MIN);
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
if (mask && (!image_get_mask_pixel(mask, x, y))) continue;
|
||||
if (COLOR_THRESHOLD_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x), &lnk_data, invert)) {
|
||||
IMAGE_CLEAR_BINARY_PIXEL_FAST(row_ptr, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -37,36 +37,48 @@ void imlib_binary(image_t *img, list_t *thresholds, bool invert, bool zero)
|
||||
}
|
||||
case IMAGE_BPP_GRAYSCALE: {
|
||||
if (!zero) {
|
||||
for (uint8_t *start = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, 0),
|
||||
*end = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, img->h);
|
||||
start < end; start++) {
|
||||
*start = COLOR_THRESHOLD_GRAYSCALE(*start, &lnk_data, invert)
|
||||
? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN;
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
if (mask && (!image_get_mask_pixel(mask, x, y))) continue;
|
||||
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(row_ptr, x,
|
||||
COLOR_THRESHOLD_GRAYSCALE(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x), &lnk_data, invert)
|
||||
? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (uint8_t *start = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, 0),
|
||||
*end = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, img->h);
|
||||
start < end; start++) {
|
||||
if (COLOR_THRESHOLD_GRAYSCALE(*start, &lnk_data, invert))
|
||||
*start = COLOR_GRAYSCALE_BINARY_MIN;
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
if (mask && (!image_get_mask_pixel(mask, x, y))) continue;
|
||||
if (COLOR_THRESHOLD_GRAYSCALE(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x), &lnk_data, invert)) {
|
||||
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(row_ptr, x, COLOR_GRAYSCALE_BINARY_MIN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_RGB565: {
|
||||
if (!zero) {
|
||||
for (uint16_t *start = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, 0),
|
||||
*end = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, img->h);
|
||||
start < end; start++) {
|
||||
*start = COLOR_THRESHOLD_RGB565(*start, &lnk_data, invert)
|
||||
? COLOR_RGB565_BINARY_MAX : COLOR_RGB565_BINARY_MIN;
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
if (mask && (!image_get_mask_pixel(mask, x, y))) continue;
|
||||
IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr, x,
|
||||
COLOR_THRESHOLD_RGB565(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x), &lnk_data, invert)
|
||||
? COLOR_RGB565_BINARY_MAX : COLOR_RGB565_BINARY_MIN);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (uint16_t *start = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, 0),
|
||||
*end = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, img->h);
|
||||
start < end; start++) {
|
||||
if (COLOR_THRESHOLD_RGB565(*start, &lnk_data, invert))
|
||||
*start = COLOR_RGB565_BINARY_MIN;
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
if (mask && (!image_get_mask_pixel(mask, x, y))) continue;
|
||||
if (COLOR_THRESHOLD_RGB565(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x), &lnk_data, invert)) {
|
||||
IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr, x, COLOR_RGB565_BINARY_MIN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
419
src/omv/img/clahe.c
Normal file
419
src/omv/img/clahe.c
Normal file
@ -0,0 +1,419 @@
|
||||
/* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2013-2018 Ibrahim Abdelkader <iabdalkader@openmv.io> & Kwabena W. Agyeman <kwagyeman@openmv.io>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*/
|
||||
|
||||
#include "imlib.h"
|
||||
#define BYTE_IMAGE
|
||||
|
||||
/*
|
||||
* ANSI C code from the article
|
||||
* "Contrast Limited Adaptive Histogram Equalization"
|
||||
* by Karel Zuiderveld, karel@cv.ruu.nl
|
||||
* in "Graphics Gems IV", Academic Press, 1994
|
||||
*
|
||||
*
|
||||
* These functions implement Contrast Limited Adaptive Histogram Equalization.
|
||||
* The main routine (CLAHE) expects an input image that is stored contiguously in
|
||||
* memory; the CLAHE output image overwrites the original input image and has the
|
||||
* same minimum and maximum values (which must be provided by the user).
|
||||
* This implementation assumes that the X- and Y image resolutions are an integer
|
||||
* multiple of the X- and Y sizes of the contextual regions. A check on various other
|
||||
* error conditions is performed.
|
||||
*
|
||||
* #define the symbol BYTE_IMAGE to make this implementation suitable for
|
||||
* 8-bit images. The maximum number of contextual regions can be redefined
|
||||
* by changing uiMAX_REG_X and/or uiMAX_REG_Y; the use of more than 256
|
||||
* contextual regions is not recommended.
|
||||
*
|
||||
* The code is ANSI-C and is also C++ compliant.
|
||||
*
|
||||
* Author: Karel Zuiderveld, Computer Vision Research Group,
|
||||
* Utrecht, The Netherlands (karel@cv.ruu.nl)
|
||||
*/
|
||||
|
||||
#ifdef BYTE_IMAGE
|
||||
typedef unsigned char kz_pixel_t; /* for 8 bit-per-pixel images */
|
||||
#define uiNR_OF_GREY (256)
|
||||
#else
|
||||
typedef unsigned short kz_pixel_t; /* for 12 bit-per-pixel images (default) */
|
||||
# define uiNR_OF_GREY (4096)
|
||||
#endif
|
||||
|
||||
/******** Prototype of CLAHE function. Put this in a separate include file. *****/
|
||||
int CLAHE(kz_pixel_t* pImage, unsigned int uiXRes, unsigned int uiYRes, kz_pixel_t Min,
|
||||
kz_pixel_t Max, unsigned int uiNrX, unsigned int uiNrY,
|
||||
unsigned int uiNrBins, float fCliplimit);
|
||||
|
||||
/*********************** Local prototypes ************************/
|
||||
static void ClipHistogram (unsigned long*, unsigned int, unsigned long);
|
||||
static void MakeHistogram (kz_pixel_t*, unsigned int, unsigned int, unsigned int,
|
||||
unsigned long*, unsigned int, kz_pixel_t*);
|
||||
static void MapHistogram (unsigned long*, kz_pixel_t, kz_pixel_t,
|
||||
unsigned int, unsigned long);
|
||||
static void MakeLut (kz_pixel_t*, kz_pixel_t, kz_pixel_t, unsigned int);
|
||||
static void Interpolate (kz_pixel_t*, int, unsigned long*, unsigned long*,
|
||||
unsigned long*, unsigned long*, unsigned int, unsigned int, kz_pixel_t*);
|
||||
|
||||
/************** Start of actual code **************/
|
||||
const unsigned int uiMAX_REG_X = 16; /* max. # contextual regions in x-direction */
|
||||
const unsigned int uiMAX_REG_Y = 16; /* max. # contextual regions in y-direction */
|
||||
|
||||
/************************** main function CLAHE ******************/
|
||||
int CLAHE (kz_pixel_t* pImage, unsigned int uiXRes, unsigned int uiYRes,
|
||||
kz_pixel_t Min, kz_pixel_t Max, unsigned int uiNrX, unsigned int uiNrY,
|
||||
unsigned int uiNrBins, float fCliplimit)
|
||||
/* pImage - Pointer to the input/output image
|
||||
* uiXRes - Image resolution in the X direction
|
||||
* uiYRes - Image resolution in the Y direction
|
||||
* Min - Minimum greyvalue of input image (also becomes minimum of output image)
|
||||
* Max - Maximum greyvalue of input image (also becomes maximum of output image)
|
||||
* uiNrX - Number of contextial regions in the X direction (min 2, max uiMAX_REG_X)
|
||||
* uiNrY - Number of contextial regions in the Y direction (min 2, max uiMAX_REG_Y)
|
||||
* uiNrBins - Number of greybins for histogram ("dynamic range")
|
||||
* float fCliplimit - Normalized cliplimit (higher values give more contrast)
|
||||
* The number of "effective" greylevels in the output image is set by uiNrBins; selecting
|
||||
* a small value (eg. 128) speeds up processing and still produce an output image of
|
||||
* good quality. The output image will have the same minimum and maximum value as the input
|
||||
* image. A clip limit smaller than 1 results in standard (non-contrast limited) AHE.
|
||||
*/
|
||||
{
|
||||
unsigned int uiX, uiY; /* counters */
|
||||
unsigned int uiXSize, uiYSize, uiSubX, uiSubY; /* size of context. reg. and subimages */
|
||||
unsigned int uiXL, uiXR, uiYU, uiYB; /* auxiliary variables interpolation routine */
|
||||
unsigned long ulClipLimit, ulNrPixels;/* clip limit and region pixel count */
|
||||
kz_pixel_t* pImPointer; /* pointer to image */
|
||||
kz_pixel_t aLUT[uiNR_OF_GREY]; /* lookup table used for scaling of input image */
|
||||
unsigned long* pulHist, *pulMapArray; /* pointer to histogram and mappings*/
|
||||
unsigned long* pulLU, *pulLB, *pulRU, *pulRB; /* auxiliary pointers interpolation */
|
||||
|
||||
if (uiNrX > uiMAX_REG_X) return -1; /* # of regions x-direction too large */
|
||||
if (uiNrY > uiMAX_REG_Y) return -2; /* # of regions y-direction too large */
|
||||
if (uiXRes % uiNrX) return -3; /* x-resolution no multiple of uiNrX */
|
||||
if (uiYRes % uiNrY) return -4; /* y-resolution no multiple of uiNrY */
|
||||
if (Max >= uiNR_OF_GREY) return -5; /* maximum too large */
|
||||
if (Min >= Max) return -6; /* minimum equal or larger than maximum */
|
||||
if (uiNrX < 2 || uiNrY < 2) return -7;/* at least 4 contextual regions required */
|
||||
if (fCliplimit == 1.0) return 0; /* is OK, immediately returns original image. */
|
||||
if (uiNrBins == 0) uiNrBins = 128; /* default value when not specified */
|
||||
|
||||
pulMapArray=(unsigned long *)fb_alloc(sizeof(unsigned long)*uiNrX*uiNrY*uiNrBins);
|
||||
if (pulMapArray == 0) return -8; /* Not enough memory! (try reducing uiNrBins) */
|
||||
|
||||
uiXSize = uiXRes/uiNrX; uiYSize = uiYRes/uiNrY; /* Actual size of contextual regions */
|
||||
ulNrPixels = (unsigned long)uiXSize * (unsigned long)uiYSize;
|
||||
|
||||
if(fCliplimit > 0.0) { /* Calculate actual cliplimit */
|
||||
ulClipLimit = (unsigned long) (fCliplimit * (uiXSize * uiYSize) / uiNrBins);
|
||||
ulClipLimit = (ulClipLimit < 1UL) ? 1UL : ulClipLimit;
|
||||
}
|
||||
else ulClipLimit = 1UL<<14; /* Large value, do not clip (AHE) */
|
||||
MakeLut(aLUT, Min, Max, uiNrBins); /* Make lookup table for mapping of greyvalues */
|
||||
/* Calculate greylevel mappings for each contextual region */
|
||||
for (uiY = 0, pImPointer = pImage; uiY < uiNrY; uiY++) {
|
||||
for (uiX = 0; uiX < uiNrX; uiX++, pImPointer += uiXSize) {
|
||||
pulHist = &pulMapArray[uiNrBins * (uiY * uiNrX + uiX)];
|
||||
MakeHistogram(pImPointer,uiXRes,uiXSize,uiYSize,pulHist,uiNrBins,aLUT);
|
||||
ClipHistogram(pulHist, uiNrBins, ulClipLimit);
|
||||
MapHistogram(pulHist, Min, Max, uiNrBins, ulNrPixels);
|
||||
}
|
||||
pImPointer += (uiYSize - 1) * uiXRes; /* skip lines, set pointer */
|
||||
}
|
||||
|
||||
/* Interpolate greylevel mappings to get CLAHE image */
|
||||
for (pImPointer = pImage, uiY = 0; uiY <= uiNrY; uiY++) {
|
||||
if (uiY == 0) { /* special case: top row */
|
||||
uiSubY = uiYSize >> 1; uiYU = 0; uiYB = 0;
|
||||
}
|
||||
else {
|
||||
if (uiY == uiNrY) { /* special case: bottom row */
|
||||
uiSubY = (uiYSize+1) >> 1; uiYU = uiNrY-1; uiYB = uiYU;
|
||||
}
|
||||
else { /* default values */
|
||||
uiSubY = uiYSize; uiYU = uiY - 1; uiYB = uiYU + 1;
|
||||
}
|
||||
}
|
||||
for (uiX = 0; uiX <= uiNrX; uiX++) {
|
||||
if (uiX == 0) { /* special case: left column */
|
||||
uiSubX = uiXSize >> 1; uiXL = 0; uiXR = 0;
|
||||
}
|
||||
else {
|
||||
if (uiX == uiNrX) { /* special case: right column */
|
||||
uiSubX = (uiXSize+1) >> 1; uiXL = uiNrX - 1; uiXR = uiXL;
|
||||
}
|
||||
else { /* default values */
|
||||
uiSubX = uiXSize; uiXL = uiX - 1; uiXR = uiXL + 1;
|
||||
}
|
||||
}
|
||||
|
||||
pulLU = &pulMapArray[uiNrBins * (uiYU * uiNrX + uiXL)];
|
||||
pulRU = &pulMapArray[uiNrBins * (uiYU * uiNrX + uiXR)];
|
||||
pulLB = &pulMapArray[uiNrBins * (uiYB * uiNrX + uiXL)];
|
||||
pulRB = &pulMapArray[uiNrBins * (uiYB * uiNrX + uiXR)];
|
||||
Interpolate(pImPointer,uiXRes,pulLU,pulRU,pulLB,pulRB,uiSubX,uiSubY,aLUT);
|
||||
pImPointer += uiSubX; /* set pointer on next matrix */
|
||||
}
|
||||
pImPointer += (uiSubY - 1) * uiXRes;
|
||||
}
|
||||
fb_free(); /* free space for histograms */
|
||||
return 0; /* return status OK */
|
||||
}
|
||||
|
||||
void ClipHistogram (unsigned long* pulHistogram, unsigned int
|
||||
uiNrGreylevels, unsigned long ulClipLimit)
|
||||
/* This function performs clipping of the histogram and redistribution of bins.
|
||||
* The histogram is clipped and the number of excess pixels is counted. Afterwards
|
||||
* the excess pixels are equally redistributed across the whole histogram (providing
|
||||
* the bin count is smaller than the cliplimit).
|
||||
*/
|
||||
{
|
||||
unsigned long* pulBinPointer, *pulEndPointer, *pulHisto;
|
||||
unsigned long ulNrExcess, ulUpper, ulBinIncr, ulStepSize, i;
|
||||
long lBinExcess;
|
||||
|
||||
ulNrExcess = 0; pulBinPointer = pulHistogram;
|
||||
for (i = 0; i < uiNrGreylevels; i++) { /* calculate total number of excess pixels */
|
||||
lBinExcess = (long) pulBinPointer[i] - (long) ulClipLimit;
|
||||
if (lBinExcess > 0) ulNrExcess += lBinExcess; /* excess in current bin */
|
||||
};
|
||||
|
||||
/* Second part: clip histogram and redistribute excess pixels in each bin */
|
||||
ulBinIncr = ulNrExcess / uiNrGreylevels; /* average binincrement */
|
||||
ulUpper = ulClipLimit - ulBinIncr; /* Bins larger than ulUpper set to cliplimit */
|
||||
|
||||
for (i = 0; i < uiNrGreylevels; i++) {
|
||||
if (pulHistogram[i] > ulClipLimit) pulHistogram[i] = ulClipLimit; /* clip bin */
|
||||
else {
|
||||
if (pulHistogram[i] > ulUpper) { /* high bin count */
|
||||
ulNrExcess -= pulHistogram[i] - ulUpper; pulHistogram[i]=ulClipLimit;
|
||||
}
|
||||
else { /* low bin count */
|
||||
ulNrExcess -= ulBinIncr; pulHistogram[i] += ulBinIncr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (ulNrExcess) { /* Redistribute remaining excess */
|
||||
pulEndPointer = &pulHistogram[uiNrGreylevels]; pulHisto = pulHistogram;
|
||||
|
||||
while (ulNrExcess && pulHisto < pulEndPointer) {
|
||||
ulStepSize = uiNrGreylevels / ulNrExcess;
|
||||
if (ulStepSize < 1) ulStepSize = 1; /* stepsize at least 1 */
|
||||
for (pulBinPointer=pulHisto; pulBinPointer < pulEndPointer && ulNrExcess;
|
||||
pulBinPointer += ulStepSize) {
|
||||
if (*pulBinPointer < ulClipLimit) {
|
||||
(*pulBinPointer)++; ulNrExcess--; /* reduce excess */
|
||||
}
|
||||
}
|
||||
pulHisto++; /* restart redistributing on other bin location */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MakeHistogram (kz_pixel_t* pImage, unsigned int uiXRes,
|
||||
unsigned int uiSizeX, unsigned int uiSizeY,
|
||||
unsigned long* pulHistogram,
|
||||
unsigned int uiNrGreylevels, kz_pixel_t* pLookupTable)
|
||||
/* This function classifies the greylevels present in the array image into
|
||||
* a greylevel histogram. The pLookupTable specifies the relationship
|
||||
* between the greyvalue of the pixel (typically between 0 and 4095) and
|
||||
* the corresponding bin in the histogram (usually containing only 128 bins).
|
||||
*/
|
||||
{
|
||||
kz_pixel_t* pImagePointer;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < uiNrGreylevels; i++) pulHistogram[i] = 0L; /* clear histogram */
|
||||
|
||||
for (i = 0; i < uiSizeY; i++) {
|
||||
pImagePointer = &pImage[uiSizeX];
|
||||
while (pImage < pImagePointer) pulHistogram[pLookupTable[*pImage++]]++;
|
||||
pImagePointer += uiXRes;
|
||||
pImage = &pImagePointer[-(int)uiSizeX]; /* go to bdeginning of next row */
|
||||
}
|
||||
}
|
||||
|
||||
void MapHistogram (unsigned long* pulHistogram, kz_pixel_t Min, kz_pixel_t Max,
|
||||
unsigned int uiNrGreylevels, unsigned long ulNrOfPixels)
|
||||
/* This function calculates the equalized lookup table (mapping) by
|
||||
* cumulating the input histogram. Note: lookup table is rescaled in range [Min..Max].
|
||||
*/
|
||||
{
|
||||
unsigned int i; unsigned long ulSum = 0;
|
||||
const float fScale = ((float)(Max - Min)) / ulNrOfPixels;
|
||||
const unsigned long ulMin = (unsigned long) Min;
|
||||
|
||||
for (i = 0; i < uiNrGreylevels; i++) {
|
||||
ulSum += pulHistogram[i]; pulHistogram[i]=(unsigned long)(ulMin+ulSum*fScale);
|
||||
if (pulHistogram[i] > Max) pulHistogram[i] = Max;
|
||||
}
|
||||
}
|
||||
|
||||
void MakeLut (kz_pixel_t * pLUT, kz_pixel_t Min, kz_pixel_t Max, unsigned int uiNrBins)
|
||||
/* To speed up histogram clipping, the input image [Min,Max] is scaled down to
|
||||
* [0,uiNrBins-1]. This function calculates the LUT.
|
||||
*/
|
||||
{
|
||||
int i;
|
||||
const kz_pixel_t BinSize = (kz_pixel_t) (1 + (Max - Min) / uiNrBins);
|
||||
|
||||
for (i = Min; i <= Max; i++) pLUT[i] = (i - Min) / BinSize;
|
||||
}
|
||||
|
||||
void Interpolate (kz_pixel_t * pImage, int uiXRes, unsigned long * pulMapLU,
|
||||
unsigned long * pulMapRU, unsigned long * pulMapLB, unsigned long * pulMapRB,
|
||||
unsigned int uiXSize, unsigned int uiYSize, kz_pixel_t * pLUT)
|
||||
/* pImage - pointer to input/output image
|
||||
* uiXRes - resolution of image in x-direction
|
||||
* pulMap* - mappings of greylevels from histograms
|
||||
* uiXSize - uiXSize of image submatrix
|
||||
* uiYSize - uiYSize of image submatrix
|
||||
* pLUT - lookup table containing mapping greyvalues to bins
|
||||
* This function calculates the new greylevel assignments of pixels within a submatrix
|
||||
* of the image with size uiXSize and uiYSize. This is done by a bilinear interpolation
|
||||
* between four different mappings in order to eliminate boundary artifacts.
|
||||
* It uses a division; since division is often an expensive operation, I added code to
|
||||
* perform a logical shift instead when feasible.
|
||||
*/
|
||||
{
|
||||
const unsigned int uiIncr = uiXRes-uiXSize; /* Pointer increment after processing row */
|
||||
kz_pixel_t GreyValue; unsigned int uiNum = uiXSize*uiYSize; /* Normalization factor */
|
||||
|
||||
unsigned int uiXCoef, uiYCoef, uiXInvCoef, uiYInvCoef, uiShift = 0;
|
||||
|
||||
if (uiNum & (uiNum - 1)) /* If uiNum is not a power of two, use division */
|
||||
for (uiYCoef = 0, uiYInvCoef = uiYSize; uiYCoef < uiYSize;
|
||||
uiYCoef++, uiYInvCoef--,pImage+=uiIncr) {
|
||||
for (uiXCoef = 0, uiXInvCoef = uiXSize; uiXCoef < uiXSize;
|
||||
uiXCoef++, uiXInvCoef--) {
|
||||
GreyValue = pLUT[*pImage]; /* get histogram bin value */
|
||||
*pImage++ = (kz_pixel_t ) ((uiYInvCoef * (uiXInvCoef*pulMapLU[GreyValue]
|
||||
+ uiXCoef * pulMapRU[GreyValue])
|
||||
+ uiYCoef * (uiXInvCoef * pulMapLB[GreyValue]
|
||||
+ uiXCoef * pulMapRB[GreyValue])) / uiNum);
|
||||
}
|
||||
}
|
||||
else { /* avoid the division and use a right shift instead */
|
||||
while (uiNum >>= 1) uiShift++; /* Calculate 2log of uiNum */
|
||||
for (uiYCoef = 0, uiYInvCoef = uiYSize; uiYCoef < uiYSize;
|
||||
uiYCoef++, uiYInvCoef--,pImage+=uiIncr) {
|
||||
for (uiXCoef = 0, uiXInvCoef = uiXSize; uiXCoef < uiXSize;
|
||||
uiXCoef++, uiXInvCoef--) {
|
||||
GreyValue = pLUT[*pImage]; /* get histogram bin value */
|
||||
*pImage++ = (kz_pixel_t)((uiYInvCoef* (uiXInvCoef * pulMapLU[GreyValue]
|
||||
+ uiXCoef * pulMapRU[GreyValue])
|
||||
+ uiYCoef * (uiXInvCoef * pulMapLB[GreyValue]
|
||||
+ uiXCoef * pulMapRB[GreyValue])) >> uiShift);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void imlib_clahe_histeq(image_t *img, float clip_limit, image_t *mask)
|
||||
{
|
||||
int xTileSize = IM_MAX(uiMAX_REG_X >> (10 - IM_MIN(IM_LOG2_32(img->w), 10)), 2);
|
||||
int yTileSize = IM_MAX(uiMAX_REG_Y >> (10 - IM_MIN(IM_LOG2_32(img->h), 10)), 2);
|
||||
int pImageW = img->w + ((img->w % xTileSize) ? (xTileSize - (img->w % xTileSize)) : 0);
|
||||
int pImageH = img->h + ((img->h % yTileSize) ? (yTileSize - (img->h % yTileSize)) : 0);
|
||||
int xOffset = (pImageW - img->w) / 2;
|
||||
int yOffset = (pImageH - img->h) / 2;
|
||||
|
||||
image_t temp;
|
||||
temp.w = img->w;
|
||||
temp.h = img->h;
|
||||
temp.bpp = img->bpp;
|
||||
temp.data = fb_alloc0(pImageW * pImageH * sizeof(kz_pixel_t));
|
||||
|
||||
switch(img->bpp) {
|
||||
case IMAGE_BPP_BINARY: {
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint8_t *clahe_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&temp, y + yOffset);
|
||||
uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(clahe_row_ptr, x + xOffset,
|
||||
COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_GRAYSCALE: {
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint8_t *clahe_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&temp, y + yOffset);
|
||||
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(clahe_row_ptr, x + xOffset,
|
||||
IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_RGB565: {
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint8_t *clahe_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&temp, y + yOffset);
|
||||
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(clahe_row_ptr, x + xOffset,
|
||||
COLOR_RGB565_TO_GRAYSCALE(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CLAHE((kz_pixel_t *) temp.data,
|
||||
pImageW, pImageH,
|
||||
COLOR_GRAYSCALE_MIN, COLOR_GRAYSCALE_MAX,
|
||||
xTileSize, yTileSize,
|
||||
COLOR_GRAYSCALE_MAX - COLOR_GRAYSCALE_MIN + 1,
|
||||
clip_limit);
|
||||
|
||||
switch(img->bpp) {
|
||||
case IMAGE_BPP_BINARY: {
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint8_t *clahe_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&temp, y + yOffset);
|
||||
uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
if (mask && (!image_get_mask_pixel(mask, x, y))) continue;
|
||||
IMAGE_PUT_BINARY_PIXEL_FAST(row_ptr, x,
|
||||
COLOR_GRAYSCALE_TO_BINARY(IMAGE_GET_GRAYSCALE_PIXEL_FAST(clahe_row_ptr, x + xOffset)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_GRAYSCALE: {
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint8_t *clahe_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&temp, y + yOffset);
|
||||
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
if (mask && (!image_get_mask_pixel(mask, x, y))) continue;
|
||||
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(row_ptr, x,
|
||||
IMAGE_GET_GRAYSCALE_PIXEL_FAST(clahe_row_ptr, x + xOffset));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_RGB565: {
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint8_t *clahe_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&temp, y + yOffset);
|
||||
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
if (mask && (!image_get_mask_pixel(mask, x, y))) continue;
|
||||
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x);
|
||||
IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr, x,
|
||||
imlib_yuv_to_rgb(IMAGE_GET_GRAYSCALE_PIXEL_FAST(clahe_row_ptr, x + xOffset),
|
||||
COLOR_RGB565_TO_U(pixel),
|
||||
COLOR_RGB565_TO_V(pixel)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fb_free();
|
||||
}
|
||||
@ -26,7 +26,7 @@ void imlib_edge_simple(image_t *src, rectangle_t *roi, int low_thresh, int high_
|
||||
lnk_data.LMin=low_thresh;
|
||||
lnk_data.LMax=high_thresh;
|
||||
list_push_back(&thresholds, &lnk_data);
|
||||
imlib_binary(src, &thresholds, false, false);
|
||||
imlib_binary(src, &thresholds, false, false, NULL);
|
||||
list_free(&thresholds);
|
||||
imlib_erode(src, 1, 2, NULL);
|
||||
}
|
||||
|
||||
@ -6,29 +6,34 @@
|
||||
#include "fsort.h"
|
||||
#include "imlib.h"
|
||||
|
||||
void imlib_histeq(image_t *img)
|
||||
void imlib_histeq(image_t *img, image_t *mask)
|
||||
{
|
||||
switch(img->bpp) {
|
||||
case IMAGE_BPP_BINARY: {
|
||||
int a = img->w * img->h;
|
||||
float s = (COLOR_BINARY_MAX-COLOR_BINARY_MIN) / ((float) a);
|
||||
uint32_t *hist = fb_alloc0((COLOR_BINARY_MAX-COLOR_BINARY_MIN+1)*sizeof(uint32_t));
|
||||
uint32_t *pixels = (uint32_t *) img->data;
|
||||
float s = (COLOR_BINARY_MAX - COLOR_BINARY_MIN) / ((float) a);
|
||||
uint32_t *hist = fb_alloc0((COLOR_BINARY_MAX - COLOR_BINARY_MIN + 1) * sizeof(uint32_t));
|
||||
|
||||
// Compute the image histogram
|
||||
for (int i=0; i<a; i++) {
|
||||
hist[pixels[i]-COLOR_BINARY_MIN] += 1;
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
hist[IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x) - COLOR_BINARY_MIN] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the CDF
|
||||
for (int i=0, sum=0; i<(COLOR_BINARY_MAX-COLOR_BINARY_MIN+1); i++) {
|
||||
for (int i = 0, sum = 0, ii = COLOR_BINARY_MAX - COLOR_BINARY_MIN + 1; i < ii; i++) {
|
||||
sum += hist[i];
|
||||
hist[i] = sum;
|
||||
}
|
||||
|
||||
for (int i=0; i<a; i++) {
|
||||
int pixel = pixels[i];
|
||||
pixels[i] = (s * hist[pixel-COLOR_BINARY_MIN]) + COLOR_BINARY_MIN;
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
if (mask && (!image_get_mask_pixel(mask, x, y))) continue;
|
||||
int pixel = IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x);
|
||||
IMAGE_PUT_BINARY_PIXEL_FAST(row_ptr, x,
|
||||
fast_roundf((s * hist[pixel - COLOR_BINARY_MIN]) + COLOR_BINARY_MIN));
|
||||
}
|
||||
}
|
||||
|
||||
fb_free();
|
||||
@ -36,24 +41,29 @@ void imlib_histeq(image_t *img)
|
||||
}
|
||||
case IMAGE_BPP_GRAYSCALE: {
|
||||
int a = img->w * img->h;
|
||||
float s = (COLOR_GRAYSCALE_MAX-COLOR_GRAYSCALE_MIN) / ((float) a);
|
||||
uint32_t *hist = fb_alloc0((COLOR_GRAYSCALE_MAX-COLOR_GRAYSCALE_MIN+1)*sizeof(uint32_t));
|
||||
uint8_t *pixels = (uint8_t *) img->data;
|
||||
float s = (COLOR_GRAYSCALE_MAX - COLOR_GRAYSCALE_MIN) / ((float) a);
|
||||
uint32_t *hist = fb_alloc0((COLOR_GRAYSCALE_MAX - COLOR_GRAYSCALE_MIN + 1) * sizeof(uint32_t));
|
||||
|
||||
// Compute the image histogram
|
||||
for (int i=0; i<a; i++) {
|
||||
hist[pixels[i]-COLOR_GRAYSCALE_MIN] += 1;
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
hist[IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x) - COLOR_GRAYSCALE_MIN] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the CDF
|
||||
for (int i=0, sum=0; i<(COLOR_GRAYSCALE_MAX-COLOR_GRAYSCALE_MIN+1); i++) {
|
||||
for (int i = 0, sum = 0, ii = COLOR_GRAYSCALE_MAX - COLOR_GRAYSCALE_MIN + 1; i < ii; i++) {
|
||||
sum += hist[i];
|
||||
hist[i] = sum;
|
||||
}
|
||||
|
||||
for (int i=0; i<a; i++) {
|
||||
int pixel = pixels[i];
|
||||
pixels[i] = (s * hist[pixel-COLOR_GRAYSCALE_MIN]) + COLOR_GRAYSCALE_MIN;
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
if (mask && (!image_get_mask_pixel(mask, x, y))) continue;
|
||||
int pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x);
|
||||
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(row_ptr, x,
|
||||
fast_roundf((s * hist[pixel - COLOR_GRAYSCALE_MIN]) + COLOR_GRAYSCALE_MIN));
|
||||
}
|
||||
}
|
||||
|
||||
fb_free();
|
||||
@ -61,26 +71,31 @@ void imlib_histeq(image_t *img)
|
||||
}
|
||||
case IMAGE_BPP_RGB565: {
|
||||
int a = img->w * img->h;
|
||||
float s = (COLOR_Y_MAX-COLOR_Y_MIN) / ((float) a);
|
||||
uint32_t *hist = fb_alloc0((COLOR_Y_MAX-COLOR_Y_MIN+1)*sizeof(uint32_t));
|
||||
uint16_t *pixels = (uint16_t *) img->data;
|
||||
float s = (COLOR_Y_MAX - COLOR_Y_MIN) / ((float) a);
|
||||
uint32_t *hist = fb_alloc0((COLOR_Y_MAX - COLOR_Y_MIN + 1) * sizeof(uint32_t));
|
||||
|
||||
// Compute image histogram
|
||||
for (int i=0; i<a; i++) {
|
||||
hist[COLOR_RGB565_TO_Y(pixels[i])-COLOR_Y_MIN] += 1;
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
hist[COLOR_RGB565_TO_Y(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x)) - COLOR_Y_MIN] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the CDF
|
||||
for (int i=0, sum=0; i<(COLOR_Y_MAX-COLOR_Y_MIN+1); i++) {
|
||||
for (int i = 0, sum = 0, ii = COLOR_Y_MAX - COLOR_Y_MIN + 1; i < ii; i++) {
|
||||
sum += hist[i];
|
||||
hist[i] = sum;
|
||||
}
|
||||
|
||||
for (int i=0; i<a; i++) {
|
||||
int pixel = pixels[i];
|
||||
pixels[i] = imlib_yuv_to_rgb((s * hist[COLOR_RGB565_TO_Y(pixel)-COLOR_Y_MIN]),
|
||||
COLOR_RGB565_TO_U(pixel),
|
||||
COLOR_RGB565_TO_V(pixel));
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
if (mask && (!image_get_mask_pixel(mask, x, y))) continue;
|
||||
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x);
|
||||
IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr, x,
|
||||
imlib_yuv_to_rgb(fast_roundf(s * hist[COLOR_RGB565_TO_Y(pixel) - COLOR_Y_MIN]),
|
||||
COLOR_RGB565_TO_U(pixel),
|
||||
COLOR_RGB565_TO_V(pixel)));
|
||||
}
|
||||
}
|
||||
|
||||
fb_free();
|
||||
@ -1108,9 +1123,9 @@ void imlib_morph(image_t *img, const int ksize, const int *krn, const float m, c
|
||||
}
|
||||
}
|
||||
|
||||
static float gaussian(int x, float sigma)
|
||||
static float gaussian(float x, float sigma)
|
||||
{
|
||||
return fast_expf((x * x) / (-2.0f * sigma * sigma)) / (sigma * 2.506628f); // sqrt(2 * PI)
|
||||
return fast_expf((x * x) / (-2.0f * sigma * sigma)) / (fabsf(sigma) * 2.506628f); // sqrt(2 * PI)
|
||||
}
|
||||
|
||||
static float distance(int x, int y)
|
||||
@ -1129,19 +1144,20 @@ void imlib_bilateral_filter(image_t *img, const int ksize, float color_sigma, fl
|
||||
switch(img->bpp) {
|
||||
case IMAGE_BPP_BINARY: {
|
||||
buf.data = fb_alloc(IMAGE_BINARY_LINE_LEN_BYTES(img) * brows);
|
||||
|
||||
float *gi_lut = fb_alloc((COLOR_BINARY_MAX - COLOR_BINARY_MIN + 1) * sizeof(float));
|
||||
|
||||
float max_color = IM_DIV(1.0f, COLOR_BINARY_MAX - COLOR_BINARY_MIN);
|
||||
for (int i = COLOR_BINARY_MIN; i <= COLOR_BINARY_MAX; i++) {
|
||||
gi_lut[i] = gaussian(i, color_sigma);
|
||||
gi_lut[i] = gaussian(i * max_color, color_sigma);
|
||||
}
|
||||
|
||||
int n = (ksize * 2) + 1;
|
||||
float *gs_lut = fb_alloc(n * n * sizeof(float));
|
||||
|
||||
float max_space = IM_DIV(1.0f, distance(ksize, ksize));
|
||||
for (int y = -ksize; y <= ksize; y++) {
|
||||
for (int x = -ksize; x <= ksize; x++) {
|
||||
gs_lut[(n * (y + ksize)) + (x + ksize)] = gaussian(distance(x, y), space_sigma);
|
||||
gs_lut[(n * (y + ksize)) + (x + ksize)] = gaussian(distance(x, y) * max_space, space_sigma);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1205,19 +1221,20 @@ void imlib_bilateral_filter(image_t *img, const int ksize, float color_sigma, fl
|
||||
}
|
||||
case IMAGE_BPP_GRAYSCALE: {
|
||||
buf.data = fb_alloc(IMAGE_GRAYSCALE_LINE_LEN_BYTES(img) * brows);
|
||||
|
||||
float *gi_lut = fb_alloc((COLOR_GRAYSCALE_MAX - COLOR_GRAYSCALE_MIN + 1) * sizeof(float));
|
||||
|
||||
float max_color = IM_DIV(1.0f, COLOR_GRAYSCALE_MAX - COLOR_GRAYSCALE_MIN);
|
||||
for (int i = COLOR_GRAYSCALE_MIN; i <= COLOR_GRAYSCALE_MAX; i++) {
|
||||
gi_lut[i] = gaussian(i, color_sigma);
|
||||
gi_lut[i] = gaussian(i * max_color, color_sigma);
|
||||
}
|
||||
|
||||
int n = (ksize * 2) + 1;
|
||||
float *gs_lut = fb_alloc(n * n * sizeof(float));
|
||||
|
||||
float max_space = IM_DIV(1.0f, distance(ksize, ksize));
|
||||
for (int y = -ksize; y <= ksize; y++) {
|
||||
for (int x = -ksize; x <= ksize; x++) {
|
||||
gs_lut[(n * (y + ksize)) + (x + ksize)] = gaussian(distance(x, y), space_sigma);
|
||||
gs_lut[(n * (y + ksize)) + (x + ksize)] = gaussian(distance(x, y) * max_space, space_sigma);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1281,29 +1298,32 @@ void imlib_bilateral_filter(image_t *img, const int ksize, float color_sigma, fl
|
||||
}
|
||||
case IMAGE_BPP_RGB565: {
|
||||
buf.data = fb_alloc(IMAGE_RGB565_LINE_LEN_BYTES(img) * brows);
|
||||
|
||||
float *r_gi_lut = fb_alloc((COLOR_R5_MAX - COLOR_R5_MIN + 1) * sizeof(float));
|
||||
float *g_gi_lut = fb_alloc((COLOR_G6_MAX - COLOR_G6_MIN + 1) * sizeof(float));
|
||||
float *b_gi_lut = fb_alloc((COLOR_B5_MAX - COLOR_B5_MIN + 1) * sizeof(float));
|
||||
|
||||
float r_max_color = IM_DIV(1.0f, COLOR_R5_MAX - COLOR_R5_MIN);
|
||||
for (int i = COLOR_R5_MIN; i <= COLOR_R5_MAX; i++) {
|
||||
r_gi_lut[i] = gaussian(i, color_sigma);
|
||||
r_gi_lut[i] = gaussian(i * r_max_color, color_sigma);
|
||||
}
|
||||
|
||||
float g_max_color = IM_DIV(1.0f, COLOR_G6_MAX - COLOR_G6_MIN);
|
||||
for (int i = COLOR_G6_MIN; i <= COLOR_G6_MAX; i++) {
|
||||
g_gi_lut[i] = gaussian(i, color_sigma);
|
||||
g_gi_lut[i] = gaussian(i * g_max_color, color_sigma);
|
||||
}
|
||||
|
||||
float b_max_color = IM_DIV(1.0f, COLOR_B5_MAX - COLOR_B5_MIN);
|
||||
for (int i = COLOR_B5_MIN; i <= COLOR_B5_MAX; i++) {
|
||||
b_gi_lut[i] = gaussian(i, color_sigma);
|
||||
b_gi_lut[i] = gaussian(i * b_max_color, color_sigma);
|
||||
}
|
||||
|
||||
int n = (ksize * 2) + 1;
|
||||
float *gs_lut = fb_alloc(n * n * sizeof(float));
|
||||
|
||||
float max_space = IM_DIV(1.0f, distance(ksize, ksize));
|
||||
for (int y = -ksize; y <= ksize; y++) {
|
||||
for (int x = -ksize; x <= ksize; x++) {
|
||||
gs_lut[(n * (y + ksize)) + (x + ksize)] = gaussian(distance(x, y), space_sigma);
|
||||
gs_lut[(n * (y + ksize)) + (x + ksize)] = gaussian(distance(x, y) * max_space, space_sigma);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1218,7 +1218,7 @@ void imlib_draw_rectangle(image_t *img, int rx, int ry, int rw, int rh, int c, i
|
||||
void imlib_draw_circle(image_t *img, int cx, int cy, int r, int c, int thickness, bool fill);
|
||||
void imlib_draw_string(image_t *img, int x_off, int y_off, const char *str, int c, int scale, int x_spacing, int y_spacing);
|
||||
// Binary Functions
|
||||
void imlib_binary(image_t *img, list_t *thresholds, bool invert, bool zero);
|
||||
void imlib_binary(image_t *img, list_t *thresholds, bool invert, bool zero, image_t *mask);
|
||||
void imlib_invert(image_t *img);
|
||||
void imlib_b_and(image_t *img, const char *path, image_t *other, int scalar, image_t *mask);
|
||||
void imlib_b_nand(image_t *img, const char *path, image_t *other, int scalar, image_t *mask);
|
||||
@ -1234,7 +1234,7 @@ void imlib_top_hat(image_t *img, int ksize, int threshold, image_t *mask);
|
||||
void imlib_black_hat(image_t *img, int ksize, int threshold, image_t *mask);
|
||||
// Math Functions
|
||||
void imlib_negate(image_t *img);
|
||||
void imlib_replace(image_t *img, const char *path, image_t *other, int scalar, bool hmirror, bool vflip);
|
||||
void imlib_replace(image_t *img, const char *path, image_t *other, int scalar, bool hmirror, bool vflip, image_t *mask);
|
||||
void imlib_add(image_t *img, const char *path, image_t *other, int scalar, image_t *mask);
|
||||
void imlib_sub(image_t *img, const char *path, image_t *other, int scalar, bool reverse, image_t *mask);
|
||||
void imlib_mul(image_t *img, const char *path, image_t *other, int scalar, bool invert, image_t *mask);
|
||||
@ -1244,7 +1244,8 @@ void imlib_max(image_t *img, const char *path, image_t *other, int scalar, image
|
||||
void imlib_difference(image_t *img, const char *path, image_t *other, int scalar, image_t *mask);
|
||||
void imlib_blend(image_t *img, const char *path, image_t *other, int scalar, float alpha, image_t *mask);
|
||||
// Filtering Functions
|
||||
void imlib_histeq(image_t *img);
|
||||
void imlib_histeq(image_t *img, image_t *mask);
|
||||
void imlib_clahe_histeq(image_t *img, float clip_limit, image_t *mask);
|
||||
void imlib_mean_filter(image_t *img, const int ksize, bool threshold, int offset, bool invert, image_t *mask);
|
||||
void imlib_median_filter(image_t *img, const int ksize, float percentile, bool threshold, int offset, bool invert, image_t *mask);
|
||||
void imlib_mode_filter(image_t *img, const int ksize, bool threshold, int offset, bool invert, image_t *mask);
|
||||
@ -1257,7 +1258,6 @@ void imlib_logpolar(image_t *img, bool linear, bool reverse);
|
||||
void imlib_remove_shadows(image_t *img, const char *path, image_t *other, int scalar, bool single);
|
||||
void imlib_chrominvar(image_t *img);
|
||||
void imlib_illuminvar(image_t *img);
|
||||
void imlib_histeq(image_t *img);
|
||||
// Lens/Rotation Correction
|
||||
void imlib_lens_corr(image_t *img, float strength, float zoom);
|
||||
void imlib_rotation_corr(image_t *img, float x_rotation, float y_rotation,
|
||||
|
||||
@ -51,41 +51,52 @@ void imlib_negate(image_t *img)
|
||||
|
||||
typedef struct imlib_replace_line_op_state {
|
||||
bool hmirror, vflip;
|
||||
image_t *mask;
|
||||
} imlib_replace_line_op_state_t;
|
||||
|
||||
static void imlib_replace_line_op(image_t *img, int line, void *other, void *data, bool vflipped)
|
||||
{
|
||||
bool hmirror = ((imlib_replace_line_op_state_t *) data)->hmirror;
|
||||
bool vflip = ((imlib_replace_line_op_state_t *) data)->vflip;
|
||||
image_t *mask = ((imlib_replace_line_op_state_t *) data)->mask;
|
||||
|
||||
switch(img->bpp) {
|
||||
case IMAGE_BPP_BINARY: {
|
||||
uint32_t *data = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img,
|
||||
vflip ? (img->h - line - 1) : line);
|
||||
int v_line = vflip ? (img->h - line - 1) : line;
|
||||
uint32_t *data = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, v_line);
|
||||
for (int i = 0, j = img->w; i < j; i++) {
|
||||
int pixel = IMAGE_GET_BINARY_PIXEL_FAST(((uint32_t *) other),
|
||||
hmirror ? (img->w - i - 1) : i);
|
||||
IMAGE_PUT_BINARY_PIXEL_FAST(data, i, pixel);
|
||||
int h_i = hmirror ? (img->w - i - 1) : i;
|
||||
|
||||
if ((!mask) || image_get_mask_pixel(mask, h_i, v_line)) {
|
||||
int pixel = IMAGE_GET_BINARY_PIXEL_FAST(((uint32_t *) other), h_i);
|
||||
IMAGE_PUT_BINARY_PIXEL_FAST(data, i, pixel);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_GRAYSCALE: {
|
||||
uint8_t *data = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img,
|
||||
vflip ? (img->h - line - 1) : line);
|
||||
int v_line = vflip ? (img->h - line - 1) : line;
|
||||
uint8_t *data = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, v_line);
|
||||
for (int i = 0, j = img->w; i < j; i++) {
|
||||
int pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(((uint8_t *) other),
|
||||
hmirror ? (img->w - i - 1) : i);
|
||||
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(data, i, pixel);
|
||||
int h_i = hmirror ? (img->w - i - 1) : i;
|
||||
|
||||
if ((!mask) || image_get_mask_pixel(mask, h_i, v_line)) {
|
||||
int pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(((uint8_t *) other), h_i);
|
||||
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(data, i, pixel);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_RGB565: {
|
||||
uint16_t *data = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img,
|
||||
vflip ? (img->h - line - 1) : line);
|
||||
int v_line = vflip ? (img->h - line - 1) : line;
|
||||
uint16_t *data = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, v_line);
|
||||
for (int i = 0, j = img->w; i < j; i++) {
|
||||
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(((uint16_t *) other),
|
||||
hmirror ? (img->w - i - 1) : i);
|
||||
IMAGE_PUT_RGB565_PIXEL_FAST(data, i, pixel);
|
||||
int h_i = hmirror ? (img->w - i - 1) : i;
|
||||
|
||||
if ((!mask) || image_get_mask_pixel(mask, h_i, v_line)) {
|
||||
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(((uint16_t *) other), h_i);
|
||||
IMAGE_PUT_RGB565_PIXEL_FAST(data, i, pixel);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -95,11 +106,12 @@ static void imlib_replace_line_op(image_t *img, int line, void *other, void *dat
|
||||
}
|
||||
}
|
||||
|
||||
void imlib_replace(image_t *img, const char *path, image_t *other, int scalar, bool hmirror, bool vflip)
|
||||
void imlib_replace(image_t *img, const char *path, image_t *other, int scalar, bool hmirror, bool vflip, image_t *mask)
|
||||
{
|
||||
imlib_replace_line_op_state_t state;
|
||||
state.hmirror = hmirror;
|
||||
state.vflip = vflip;
|
||||
state.mask = mask;
|
||||
imlib_image_operation(img, path, other, scalar, imlib_replace_line_op, &state);
|
||||
}
|
||||
|
||||
|
||||
@ -228,7 +228,7 @@ void imlib_remove_shadows(image_t *img, const char *path, image_t *other, int sc
|
||||
lnk_data.AMax = COLOR_A_MAX;
|
||||
lnk_data.BMax = COLOR_B_MAX;
|
||||
list_push_back(&thresholds, &lnk_data);
|
||||
imlib_binary(&temp_image, &thresholds, false, false);
|
||||
imlib_binary(&temp_image, &thresholds, false, false, NULL);
|
||||
list_free(&thresholds);
|
||||
|
||||
imlib_erode(&temp_image, 3, 30, NULL);
|
||||
|
||||
@ -1199,9 +1199,11 @@ STATIC mp_obj_t py_image_binary(uint n_args, const mp_obj_t *args, mp_map_t *kw_
|
||||
py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_invert), false);
|
||||
bool arg_zero =
|
||||
py_helper_keyword_int(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_zero), false);
|
||||
image_t *arg_msk =
|
||||
py_helper_keyword_to_image_mutable_mask(n_args, args, 4, kw_args);
|
||||
|
||||
fb_alloc_mark();
|
||||
imlib_binary(py_helper_arg_to_image_mutable(args[0]), &arg_thresholds, arg_invert, arg_zero);
|
||||
imlib_binary(py_helper_arg_to_image_mutable(args[0]), &arg_thresholds, arg_invert, arg_zero, arg_msk);
|
||||
fb_alloc_free_till_mark();
|
||||
list_free(&arg_thresholds);
|
||||
return args[0];
|
||||
@ -1486,17 +1488,19 @@ STATIC mp_obj_t py_image_replace(uint n_args, const mp_obj_t *args, mp_map_t *kw
|
||||
py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_hmirror), false);
|
||||
bool arg_vflip =
|
||||
py_helper_keyword_int(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_vflip), false);
|
||||
image_t *arg_msk =
|
||||
py_helper_keyword_to_image_mutable_mask(n_args, args, 4, kw_args);
|
||||
|
||||
fb_alloc_mark();
|
||||
|
||||
if (MP_OBJ_IS_STR(args[1])) {
|
||||
imlib_replace(arg_img, mp_obj_str_get_str(args[1]), NULL, 0, arg_hmirror, arg_vflip);
|
||||
imlib_replace(arg_img, mp_obj_str_get_str(args[1]), NULL, 0, arg_hmirror, arg_vflip, arg_msk);
|
||||
} else if (MP_OBJ_IS_TYPE(args[1], &py_image_type)) {
|
||||
imlib_replace(arg_img, NULL, py_helper_arg_to_image_mutable(args[1]), 0, arg_hmirror, arg_vflip);
|
||||
imlib_replace(arg_img, NULL, py_helper_arg_to_image_mutable(args[1]), 0, arg_hmirror, arg_vflip, arg_msk);
|
||||
} else {
|
||||
imlib_replace(arg_img, NULL, NULL,
|
||||
py_helper_keyword_color(arg_img, n_args, args, 1, NULL, 0),
|
||||
arg_hmirror, arg_vflip);
|
||||
arg_hmirror, arg_vflip, arg_msk);
|
||||
}
|
||||
|
||||
fb_alloc_free_till_mark();
|
||||
@ -1718,14 +1722,23 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_blend_obj, 2, py_image_blend);
|
||||
// Filtering Methods
|
||||
////////////////////
|
||||
|
||||
static mp_obj_t py_image_histeq(mp_obj_t img_obj)
|
||||
static mp_obj_t py_image_histeq(uint n_args, const mp_obj_t *args, mp_map_t *kw_args)
|
||||
{
|
||||
image_t *arg_img =
|
||||
py_helper_arg_to_image_mutable(args[0]);
|
||||
bool arg_adaptive =
|
||||
py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_adaptive), false);
|
||||
float arg_clip_limit =
|
||||
py_helper_keyword_float(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_clip_limit), -1);
|
||||
image_t *arg_msk =
|
||||
py_helper_keyword_to_image_mutable_mask(n_args, args, 3, kw_args);
|
||||
|
||||
fb_alloc_mark();
|
||||
imlib_histeq(py_helper_arg_to_image_mutable(img_obj));
|
||||
if (arg_adaptive) imlib_clahe_histeq(arg_img, arg_clip_limit, arg_msk); else imlib_histeq(arg_img, arg_msk);
|
||||
fb_alloc_free_till_mark();
|
||||
return img_obj;
|
||||
return args[0];
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_histeq_obj, py_image_histeq);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_histeq_obj, 1, py_image_histeq);
|
||||
|
||||
STATIC mp_obj_t py_image_mean(uint n_args, const mp_obj_t *args, mp_map_t *kw_args)
|
||||
{
|
||||
@ -1988,10 +2001,10 @@ STATIC mp_obj_t py_image_bilateral(uint n_args, const mp_obj_t *args, mp_map_t *
|
||||
int arg_ksize =
|
||||
py_helper_arg_to_ksize(args[1]);
|
||||
float arg_color_sigma =
|
||||
py_helper_keyword_float(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_color_sigma), 6);
|
||||
py_helper_keyword_float(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_color_sigma), 0.1);
|
||||
PY_ASSERT_TRUE_MSG((0 <= arg_color_sigma), "Error: 0 <= color_sigma!");
|
||||
float arg_space_sigma =
|
||||
py_helper_keyword_float(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_space_sigma), 6);
|
||||
py_helper_keyword_float(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_space_sigma), 1);
|
||||
PY_ASSERT_TRUE_MSG((0 <= arg_space_sigma), "Error: 0 <= space_sigma!");
|
||||
bool arg_threshold =
|
||||
py_helper_keyword_int(n_args, args, 4, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_threshold), false);
|
||||
|
||||
@ -364,6 +364,7 @@ Q(draw_keypoints)
|
||||
Q(binary)
|
||||
Q(invert)
|
||||
Q(zero)
|
||||
Q(mask)
|
||||
|
||||
// Invert
|
||||
// duplicate Q(invert)
|
||||
@ -371,7 +372,7 @@ Q(zero)
|
||||
// And
|
||||
Q(and)
|
||||
Q(b_and)
|
||||
Q(mask)
|
||||
// duplicate Q(mask)
|
||||
|
||||
// Nand
|
||||
Q(nand)
|
||||
@ -435,6 +436,7 @@ Q(negate)
|
||||
Q(replace)
|
||||
Q(hmirror)
|
||||
Q(vflip)
|
||||
// duplicate Q(mask)
|
||||
|
||||
// Add Op
|
||||
Q(add)
|
||||
@ -474,6 +476,9 @@ Q(blend)
|
||||
|
||||
// Histogram Equalization
|
||||
Q(histeq)
|
||||
Q(adaptive)
|
||||
Q(clip_limit)
|
||||
// duplicate Q(mask)
|
||||
|
||||
// Mean
|
||||
Q(mean)
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
# Adaptive Histogram Equalization
|
||||
#
|
||||
# This example shows off how to use adaptive histogram equalization to improve
|
||||
# the contrast in the image. Adaptive histogram equalization splits the image
|
||||
# into regions and then equalizes the histogram in those regions to improve
|
||||
# the image contrast versus a global histogram equalization. Additionally,
|
||||
# you may specify a clip limit to prevent the contrast from going wild.
|
||||
|
||||
import sensor, image, time
|
||||
|
||||
sensor.reset()
|
||||
sensor.set_pixformat(sensor.RGB565)
|
||||
sensor.set_framesize(sensor.QQVGA)
|
||||
sensor.skip_frames(time = 2000)
|
||||
clock = time.clock()
|
||||
|
||||
while(True):
|
||||
clock.tick()
|
||||
|
||||
# A clip_limit of < 0 gives you normal adaptive histogram equalization
|
||||
# which may result in huge amounts of contrast noise...
|
||||
|
||||
# A clip_limit of 1 does nothing. For best results go slightly higher
|
||||
# than 1 like below. The higher you go the closer you get back to
|
||||
# standard adaptive histogram equalization with huge contrast swings.
|
||||
|
||||
img = sensor.snapshot().histeq(adaptive=True, clip_limit=3)
|
||||
|
||||
print(clock.fps())
|
||||
@ -23,7 +23,7 @@ while(True):
|
||||
# A larger value is less strict.
|
||||
|
||||
# Run the kernel on every pixel of the image.
|
||||
img.bilateral(3, color_sigma=5, space_sigma=5)
|
||||
img.bilateral(3, color_sigma=0.1, space_sigma=1)
|
||||
|
||||
# Note that the bilateral filter can introduce image defects if you set
|
||||
# color_sigma/space_sigma to aggresively. Increase the sigma values until
|
||||
|
||||
@ -23,7 +23,7 @@ while(True):
|
||||
# A larger value is less strict.
|
||||
|
||||
# Run the kernel on every pixel of the image.
|
||||
img.bilateral(3, color_sigma=20, space_sigma=20)
|
||||
img.bilateral(3, color_sigma=0.1, space_sigma=1)
|
||||
|
||||
# Note that the bilateral filter can introduce image defects if you set
|
||||
# color_sigma/space_sigma to aggresively. Increase the sigma values until
|
||||
|
||||
19
usr/examples/04-Image-Filters/histogram_equalization.py
Normal file
19
usr/examples/04-Image-Filters/histogram_equalization.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Histogram Equalization
|
||||
#
|
||||
# This example shows off how to use histogram equalization to improve
|
||||
# the contrast in the image.
|
||||
|
||||
import sensor, image, time
|
||||
|
||||
sensor.reset()
|
||||
sensor.set_pixformat(sensor.RGB565)
|
||||
sensor.set_framesize(sensor.QQVGA)
|
||||
sensor.skip_frames(time = 2000)
|
||||
clock = time.clock()
|
||||
|
||||
while(True):
|
||||
clock.tick()
|
||||
|
||||
img = sensor.snapshot().histeq()
|
||||
|
||||
print(clock.fps())
|
||||
Loading…
Reference in New Issue
Block a user