0.2.2 GAMMA

This commit is contained in:
rob tillaart 2022-07-25 15:25:14 +02:00
parent 76c77a286d
commit 2510474149
15 changed files with 347 additions and 135 deletions

View File

@ -31,7 +31,8 @@ The library has a **setGamma(float gamma)** function that allows an application
to change the gamma value runtime.
This allows adjustments that a fixed table does not have.
The class can be used to dump the internal table e.g. to place in PROGMEM.
The class provides **dump()** to create a table e.g. to place in PROGMEM.
Since 0.2.2 the library also has **dumpArray()** to generate a C-style array.
Note: tested on UNO and ESP32 only.
@ -42,38 +43,50 @@ Note: tested on UNO and ESP32 only.
- **GAMMA(uint16_t size = 32)** constructor, gets the size of the internal
array as parameter. The default for size = 32 as this is a good balance between performance
and size of the internal array. The size parameter must be in {2, 4, 8, 16, 32, 64, 128, 256 }.
- **begin()** The array is initialized with a gamma == 2.8 which is an often used value.
**begin()** must be called before any other function.
- **setGamma(float gamma)** calculates and fills the array with new values.
This can be done runtime so runtime adjustment of gamma mapping.
This function takes relative quite some time.
and size of the internal array.
The size parameter must be in {2, 4, 8, 16, 32, 64, 128, 256 }.
- **~GAMMA()** destructor.
- **void begin()** The internal array is allocated and initialized with a gamma == 2.8.
This is an often used value to adjust light to human eye responses.
Note that **begin()** must be called before any other function.
- **void setGamma(float gamma)** calculates and fills the array with new values.
This can be done runtime so runtime adjustment of gamma mapping is possible.
This calculation are relative expensive and takes quite some time (depending on size).
If the array already is calculated for gamma, the calculation will be skipped.
The parameter **gamma** must be > 0. The value 1 gives an 1:1 mapping.
- **getGamma()** returns the set gamma value.
- **operator \[\]** allows the GAMMA object to be accessed as an array.
like ```x = G[40];``` Makes it easy to switch with a real array.
- **float getGamma()** returns the set gamma value.
- **uint8_t operator \[\]** allows the GAMMA object to be accessed as an array.
like ```x = G[40];``` Makes it easy to switch with a real array.
The value returned is in the range 0 .. 255, so the user may need to scale it e.g. to 0.0 - 1.0
### Development functions
- **size()** returns size of the internal array.
- **distinct()** returns the number of distinct values in the table.
Especially with larger internal tables rhere will be duplicate numbers in the table.
- **dump()** dumps the internal table to Serial. Can be useful to create
an array in RAM, PROGMEM or wherever.
- **uint16_t size()** returns size of the internal array.
This is always a power of 2.
- **uint16_t distinct()** returns the number of distinct values in the table.
Especially with larger internal tables there will be duplicate numbers in the table.
- **void dump(Stream \*str = &Serial)** dumps the internal table to a stream, default Serial.
Useful to create an array in RAM, PROGMEM, EEPROM, in a file or wherever.
- **void dumpArray(Stream \*str = &Serial)** dumps the internal table to a stream, default Serial, as a C-style array. See example.
## Operation
See example
See example.
## Future ideas
- improve documentation
- test other platforms
- look for optimizations
- look for optimizations
- getter \[\]
- setGamma -> pow() is expensive
- improvements (0.3.0)
- return bool => begin() + setGamma() + dump()?
- check \_table != NULL in functions
- add gamma<=0 check in setGamma()
- uint16 version?
- **dumpAsArray()** - generate a C style array
-

View File

@ -2,7 +2,6 @@
// FILE: GammaErrorAnalysis.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// DATE: 2020-08-08
#include "gamma.h"
@ -24,7 +23,10 @@ volatile int x;
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println(__FILE__);
Serial.print("GAMMA_LIB_VERSION: ");
Serial.println(GAMMA_LIB_VERSION);
gt1.begin();
gt2.begin();

View File

@ -2,7 +2,6 @@
// FILE: gammaPerformance.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// DATE: 2020-08-08
#include "gamma.h"
@ -21,7 +20,10 @@ volatile int x;
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println(__FILE__);
Serial.print("GAMMA_LIB_VERSION: ");
Serial.println(GAMMA_LIB_VERSION);
gt1.begin();
gt2.begin();
@ -99,3 +101,4 @@ void loop()
// -- END OF FILE --

View File

@ -0,0 +1,34 @@
Arduino UNO
IDE 1.8.19
GammaPerformance.ino
GAMMA_LIB_VERSION: 0.2.1
timing in microseconds
SETGAMMA
SIZE TIME TIME per element
257 85868 334.12
129 42712 331.10
65 21200 326.15
33 10448 316.61
17 5084 299.06
SETGAMMA II
SIZE TIME TIME per element
257 12 0.05
129 8 0.06
65 4 0.06
33 8 0.24
17 8 0.47
GET[]
SIZE TIME TIME per element
257 356 1.39
129 876 3.42
65 1116 4.36
33 1328 5.19
17 1524 5.95
done...

View File

@ -0,0 +1,34 @@
Arduino UNO
IDE 1.8.19
GammaPerformance.ino
GAMMA_LIB_VERSION: 0.2.2
timing in microseconds
SETGAMMA
SIZE TIME TIME per element
257 85868 334.12
129 42708 331.07
65 21200 326.15
33 10456 316.85
17 5084 299.06
SETGAMMA II
SIZE TIME TIME per element
257 16 0.06
129 8 0.06
65 8 0.12
33 4 0.12
17 8 0.47
GET[]
SIZE TIME TIME per element
257 360 1.41
129 872 3.41
65 1124 4.39
33 1332 5.20
17 1520 5.94
done...

View File

@ -1,9 +1,7 @@
//
// FILE: gammaFast.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.2.0
// PURPOSE: demo
// DATE: 2020-08-08
#include "gamma.h"
@ -42,6 +40,10 @@ int fastGamma(uint8_t idx)
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println(__FILE__);
Serial.print("GAMMA_LIB_VERSION: ");
Serial.println(GAMMA_LIB_VERSION);
gt1.begin();
gt1.setGamma(2.8);

View File

@ -2,7 +2,6 @@
// FILE: gammaTest.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// DATE: 2020-08-08
#include "gamma.h"
@ -18,6 +17,10 @@ GAMMA gt5(16);
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println(__FILE__);
Serial.print("GAMMA_LIB_VERSION: ");
Serial.println(GAMMA_LIB_VERSION);
gt1.begin();
gt2.begin();

View File

@ -2,7 +2,6 @@
// FILE: gammaTest2.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo setGamma
// DATE: 2020-08-08
// Use Arduino Plotter to see the different curves.
@ -15,7 +14,10 @@ GAMMA gt; // uses default 32 size
void setup()
{
Serial.begin(115200);
Serial.println("gammaTest2.ino");
Serial.println();
Serial.println(__FILE__);
Serial.print("GAMMA_LIB_VERSION: ");
Serial.println(GAMMA_LIB_VERSION);
gt.begin();

View File

@ -0,0 +1,52 @@
//
// FILE: gamma_dump_array.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
#include "gamma.h"
GAMMA gt1(256);
GAMMA gt2(128);
GAMMA gt3(64);
GAMMA gt4(32); // default
GAMMA gt5(16);
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println(__FILE__);
Serial.print("GAMMA_LIB_VERSION: ");
Serial.println(GAMMA_LIB_VERSION);
gt1.begin();
gt2.begin();
gt3.begin();
gt4.begin();
gt5.begin();
gt1.setGamma(2.8);
gt2.setGamma(2.8);
gt3.setGamma(2.8);
gt4.setGamma(2.8);
gt5.setGamma(2.8);
gt1.dumpArray();
gt2.dumpArray();
gt3.dumpArray();
gt4.dumpArray();
gt5.dumpArray();
Serial.println("\ndone...\n");
}
void loop()
{
}
// -- END OF FILE --

150
libraries/GAMMA/gamma.cpp Normal file
View File

@ -0,0 +1,150 @@
//
// FILE: gamma.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.2.2
// DATE: 2020-08-08
// PURPOSE: Arduino Library to efficiently hold a gamma lookup table
// 0.1.0 2020-08-08 initial release
// 0.1.1 2020-12-24 Arduino-CI + unit test
//
// 0.2.0 2021-11-02 update build-CI, badges
// add begin() - fixes ESP32 crash.
// 0.2.1 2021-12-18 update library.json, license,
// add constants, minor edits.
// 0.2.2 2022-07-25 split in .h and .cpp
// add Stream parameter to dump()
// add dumpArray(Stream)
// fix distinct()
#include "gamma.h"
GAMMA::GAMMA(uint16_t size)
{
_shift = 7;
// force power of 2; get shift & mask right
for (uint16_t s = 2; s <= GAMMA_MAX_SIZE; s <<= 1)
{
if (size <= s)
{
_size = s;
break;
}
_shift--;
}
_mask = (1 << _shift) - 1;
_interval = GAMMA_MAX_SIZE / _size;
};
GAMMA::~GAMMA()
{
if (_table) free(_table);
};
void GAMMA::begin()
{
if (_table == NULL)
{
_table = (uint8_t *)malloc(_size + 1);
}
setGamma(2.8);
};
void GAMMA::setGamma(float gamma)
{
if (_gamma != gamma)
{
yield(); // keep ESP happy
_gamma = gamma;
// marginally faster
// uint16_t iv = _interval;
// _table[0] = 0;
// for (uint16_t i = 1; i < _size; i++)
// {
// float x = log(i * iv) + log(1.0 / 255);
// _table[i] = exp(x * _gamma) * 255 + 0.5;
// }
// REFERENCE
for (uint16_t i = 0; i < _size; i++)
{
_table[i] = pow(i * _interval * (1.0/ 255.0), _gamma) * 255 + 0.5;
}
_table[_size] = 255; // anchor for interpolation..
}
};
float GAMMA::getGamma()
{
return _gamma;
};
uint8_t GAMMA::operator[] (uint8_t index)
{
if (_interval == 1) return _table[index];
// else interpolate
uint8_t i = index >> _shift;
uint8_t m = index & _mask;
// exact element shortcut
if ( m == 0 ) return _table[i];
// interpolation
uint16_t delta = _table[i+1] - _table[i];
delta = (delta * m + _interval/2) >> _shift; // == /_interval;
return _table[i] + delta;
};
uint16_t GAMMA::size()
{
return _size + 1;
};
uint16_t GAMMA::distinct()
{
uint8_t last = _table[0];
uint16_t count = 1;
for (uint16_t i = 1; i <= _size; i++)
{
if (_table[i] == last) continue;
last = _table[i];
count++;
}
return count;
};
void GAMMA::dump(Stream *str)
{
for (uint16_t i = 0; i <= _size; i++)
{
str->println(_table[i]);
}
};
void GAMMA::dumpArray(Stream *str)
{
str->println();
str->print("uint8_t gamma[");
str->print(_size + 1);
str->print("] = {");
for (uint16_t i = 0; i <= _size; i++)
{
if (i % 8 == 0) str->print("\n ");
str->print(_table[i]);
if (i < _size) str->print(", ");
}
str->print("\n };\n\n");
};
// -- END OF FILE --

View File

@ -2,21 +2,14 @@
//
// FILE: gamma.h
// AUTHOR: Rob Tillaart
// VERSION: 0.2.1
// VERSION: 0.2.2
// DATE: 2020-08-08
// PURPOSE: Arduino Library to efficiently hold a gamma lookup table
// 0.1.0 2020-08-08 initial release
// 0.1.1 2020-12-24 arduino-ci + unit test
// 0.2.0 2021-11-02 update build-CI, badges
// add begin() - fixes ESP32 crash.
// 0.2.1 2021-12-18 update library.json, license,
// add constants, minor edits.
#include "Arduino.h"
#define GAMMA_LIB_VERSION (F("0.2.1"))
#define GAMMA_LIB_VERSION (F("0.2.2"))
#define GAMMA_DEFAULT_SIZE 32
#define GAMMA_MAX_SIZE 256
@ -27,103 +20,26 @@ class GAMMA
public:
GAMMA(uint16_t size = GAMMA_DEFAULT_SIZE)
{
_shift = 7;
// force power of 2; get shift & mask right
for (uint16_t s = 2; s <= GAMMA_MAX_SIZE; s <<= 1)
{
if (size <= s)
{
_size = s;
break;
}
_shift--;
}
_mask = (1 << _shift) - 1;
_interval = GAMMA_MAX_SIZE / _size;
}
GAMMA(uint16_t size = GAMMA_DEFAULT_SIZE);
~GAMMA();
// allocates memory
// sets default gamma = 2.8
void begin();
~GAMMA()
{
if (_table) free(_table);
}
// CORE
void setGamma(float gamma);
float getGamma();
// access values with index operator
uint8_t operator[] (uint8_t index);
// META INFO
uint16_t size();
uint16_t distinct();
void begin()
{
if (_table == NULL)
{
_table = (uint8_t *)malloc(_size + 1);
}
setGamma(2.8);
}
void setGamma(float gamma)
{
if (_gamma != gamma)
{
yield(); // keep ESP happy
_gamma = gamma;
for (uint16_t i = 0; i < _size; i++)
{
_table[i] = pow(i * _interval * (1.0 / 255), _gamma) * 255 + 0.5;
}
_table[_size] = 255; // anchor for interpolation..
}
}
float getGamma()
{
return _gamma;
};
uint8_t operator[] (uint8_t idx)
{
if (_interval == 1) return _table[idx];
// else interpolate
uint8_t i = idx >> _shift;
uint8_t m = idx & _mask;
// exact element shortcut
if ( m == 0 ) return _table[i];
// interpolation
uint16_t delta = _table[i+1] - _table[i];
delta = (delta * m + _interval/2) >> _shift; // == /_interval;
return _table[i] + delta;
}
uint16_t size()
{
return _size + 1;
};
int distinct()
{
int last = _table[0];
int cnt = 1;
for (uint16_t i = 1; i < _size; i++)
{
if (_table[i] == last) continue;
last = _table[i];
cnt++;
}
return cnt;
}
void dump()
{
for (uint16_t i = 0; i < _size; i++)
{
Serial.println(_table[i]);
}
}
// DEBUG
void dump(Stream *str = &Serial);
void dumpArray(Stream *str = &Serial);
private:

View File

@ -10,6 +10,7 @@ getGamma KEYWORD2
size KEYWORD2
distinct KEYWORD2
dump KEYWORD2
dumpArray KEYWORD2
# Constants (LITERAL1)

View File

@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/GAMMA.git"
},
"version": "0.2.1",
"version": "0.2.2",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*",

View File

@ -1,5 +1,5 @@
name=GAMMA
version=0.2.1
version=0.2.2
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino Library for the GAMMA function

View File

@ -52,7 +52,7 @@ unittest(test_constructor)
gt0.begin();
assertEqual(33, gt0.size());
assertEqualFloat(2.8, gt0.getGamma(), 0.0001);
assertEqual(28, gt0.distinct());
assertEqual(29, gt0.distinct());
GAMMA gt1(256);
gt1.begin();
@ -64,31 +64,31 @@ unittest(test_constructor)
gt2.begin();
assertEqual(129, gt2.size());
assertEqualFloat(2.8, gt2.getGamma(), 0.0001);
assertEqual(97, gt2.distinct());
assertEqual(98, gt2.distinct());
GAMMA gt3(64);
gt3.begin();
assertEqual(65, gt3.size());
assertEqualFloat(2.8, gt3.getGamma(), 0.0001);
assertEqual(53, gt3.distinct());
assertEqual(54, gt3.distinct());
GAMMA gt4(32); // default
gt4.begin();
assertEqual(33, gt4.size());
assertEqualFloat(2.8, gt4.getGamma(), 0.0001);
assertEqual(28, gt4.distinct());
assertEqual(29, gt4.distinct());
GAMMA gt5(16);
gt5.begin();
assertEqual(17, gt5.size());
assertEqualFloat(2.8, gt5.getGamma(), 0.0001);
assertEqual(15, gt5.distinct());
assertEqual(16, gt5.distinct());
GAMMA gt6(8);
gt6.begin();
assertEqual(9, gt6.size());
assertEqualFloat(2.8, gt6.getGamma(), 0.0001);
assertEqual(8, gt6.distinct());
assertEqual(9, gt6.distinct());
}