TV: improve color calculation algorithm

This commit is contained in:
Christopher Mosher 2019-04-05 15:48:21 -04:00
parent 06fc2e6bce
commit 0313881ac0
6 changed files with 81 additions and 48 deletions

View File

@ -396,12 +396,12 @@ CB AnalogTV::get_cb(int lineno)
static std::map<CB,IQ> cacheCB;
const double AnalogTV::IQ_OFFSET_DEGREES = 33;
const double AnalogTV::IQ_OFFSET_DEGREES = 33; //NTSC spec. 33 IQ is rotated 33 degrees
const double AnalogTV::IQ_OFFSET_RADIANS = AnalogTV::IQ_OFFSET_DEGREES * 3.1415927 / 180;
const double AnalogTV::TINT_I = -cos(AnalogTV::IQ_OFFSET_RADIANS);
const double AnalogTV::TINT_Q = +sin(AnalogTV::IQ_OFFSET_RADIANS);
const double AnalogTV::COLOR_THRESH(1.4);
const double AnalogTV::COLOR_THRESH(sqrt(2));
IQ AnalogTV::get_iq_factor(const CB& cb)
{
@ -440,20 +440,14 @@ const int AnalogTV::IQINTOFF(130);
void AnalogTV::ntsc_to_yiq(const int isignal, const int siglen, const IQ& iq_factor, int yiq[]) {
FilterLuma filterY;
FilterChroma filterI;
FilterChroma filterQ;
FilterChromaI filterI;
FilterChromaQ filterQ;
for (int off = 0; off < siglen; ++off) {
const int sig = this->signal[isignal + off];
const int y = filterY.next(sig);
int i;
int q;
if (y < -2) {
i = 0;
q = 0;
} else {
i = filterI.next(sig * iq_factor.get(off & 3));
q = filterQ.next(sig * iq_factor.get((off + 3) & 3));
}
const int i = filterI.next(sig) * iq_factor.get(off & 3);
const int q = filterQ.next(sig) * iq_factor.get((off + 3) & 3);
yiq[off] = (((q+IQINTOFF)&0xff) << 16) | (((i+IQINTOFF)&0xff) << 8) | ((y+IQINTOFF)&0xff);
}

View File

@ -19,25 +19,58 @@
/*
Generated by the utility at http://www-users.cs.york.ac.uk/~fisher/mkfilter
3rd order low-pass Butterworth filter at 1500000 Hz.
(sample rate 14318182 Hz)
2nd order band-pass Butterworth filters (sample rate 14318182 Hz)
*/
#define GAIN 4.910093226e+01
FilterChroma::FilterChroma() {
xv[0]=xv[1]=xv[2]=xv[3]=0;
yv[0]=yv[1]=yv[2]=yv[3]=0;
/*
pass 2079545 Hz through 5079545 Hz.
*/
#define GAINI 4.501156488e+00
FilterChromaI::FilterChromaI() {
xv[0]=xv[1]=xv[2]=xv[3]=xv[4]=0;
yv[0]=yv[1]=yv[2]=yv[3]=yv[4]=0;
}
FilterChroma::~FilterChroma() {
FilterChromaI::~FilterChromaI() {
}
double FilterChroma::next(const double v) {
xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3];
xv[3] = v / GAIN;
yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3];
yv[3] = (xv[0] + xv[3]) + 3 * (xv[1] + xv[2])
+ ( 0.2608994296 * yv[0]) + ( -1.1262209208 * yv[1])
+ ( 1.7023917944 * yv[2]);
return yv[3];
double FilterChromaI::next(const double v) {
xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4];
xv[4] = v / GAINI;
yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[3] = yv[4];
yv[4] = (xv[0] + xv[4]) - 2 * xv[2]
+ ( -0.1873928488 * yv[0]) + ( 0.0000001868 * yv[1])
+ ( -0.2987323434 * yv[2]) + ( 0.0000006376 * yv[3]);
return yv[4];
}
/*
pass 2979545 Hz through 4179545 Hz.
*/
#define GAINQ 2.000943914e+01
FilterChromaQ::FilterChromaQ() {
xv[0]=xv[1]=xv[2]=xv[3]=xv[4]=0;
yv[0]=yv[1]=yv[2]=yv[3]=yv[4]=0;
}
FilterChromaQ::~FilterChromaQ() {
}
double FilterChromaQ::next(const double v) {
xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4];
xv[4] = v / GAINQ;
yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[3] = yv[4];
yv[4] = (xv[0] + xv[4]) - 2 * xv[2]
+ ( -0.4755965671 * yv[0]) + ( 0.0000005060 * yv[1])
+ ( -1.2756909139 * yv[2]) + ( 0.0000007444 * yv[3]);
return yv[4];
}

View File

@ -18,14 +18,25 @@
#ifndef FILTERCHROMA
#define FILTERCHROMA
class FilterChroma {
class FilterChromaI {
private:
double xv[4];
double yv[4];
double xv[5];
double yv[5];
public:
FilterChroma();
~FilterChroma();
FilterChromaI();
~FilterChromaI();
double next(const double v);
};
class FilterChromaQ {
private:
double xv[5];
double yv[5];
public:
FilterChromaQ();
~FilterChromaQ();
double next(const double v);
};

View File

@ -19,14 +19,14 @@
/*
Generated by the utility at http://www-users.cs.york.ac.uk/~fisher/mkfilter
3rd order low-pass Butterworth filter at 4500000 Hz with extra zero at 3500000 Hz.
1st order low-pass Butterworth filter at 2079545 Hz with extra zero at 3579545 Hz
(sample rate 14318182 Hz)
*/
#define GAIN 6.715664173
#define GAIN 6.074790079e+00
FilterLuma::FilterLuma() {
xv[0]=xv[1]=xv[2]=xv[3]=xv[4]=xv[5]=0;
xv[0]=xv[1]=xv[2]=xv[3]=0;
yv[0]=yv[1]=yv[2]=yv[3]=0;
}
@ -34,11 +34,10 @@ FilterLuma::~FilterLuma() {
}
double FilterLuma::next(const double v) {
xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4]; xv[4] = xv[5];
xv[5] = v / GAIN;
xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3];
xv[3] = v / GAIN;
yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3];
yv[3] = (xv[0] + xv[5]) + 2.9302009676 * (xv[1] + xv[4]) + 3.7906029028 * (xv[2] + xv[3])
+ ( -0.0757751449 * yv[0]) + ( -0.4803383261 * yv[1])
+ ( -0.7432283875 * yv[2]);
yv[3] = (xv[0] + xv[3]) + 0.9999995612 * (xv[1] + xv[2])
+ ( 0.3415411775 * yv[2]);
return yv[3];
}

View File

@ -20,7 +20,7 @@
class FilterLuma {
private:
double xv[6];
double xv[4];
double yv[4];
public:

View File

@ -294,12 +294,8 @@ signed char inline PictureGenerator::vbl(const int hcycle)
// Note that this color burst signal only affects "old TV" mode;
// the other color modes use A2ColorsObserved.
const signed char PictureGenerator::lutCB[] =
{
0,
-AppleNTSC::CB_LEVEL,
0,
+AppleNTSC::CB_LEVEL,
};
{ 0, -AppleNTSC::CB_LEVEL, 0, +AppleNTSC::CB_LEVEL };
//{ +AppleNTSC::CB_LEVEL/2, -AppleNTSC::CB_LEVEL/2, -AppleNTSC::CB_LEVEL/2, +AppleNTSC::CB_LEVEL/2 };
signed char inline PictureGenerator::hbl(const int hcycle)
{