enhanced scanline based frame layout detection to fractional values, this allows combining results with color based layout overruling

This commit is contained in:
Thomas Jentzsch 2022-07-14 20:03:45 +02:00
parent 5ad9c0e5ce
commit ffde2e592a
2 changed files with 72 additions and 92 deletions

View File

@ -72,10 +72,13 @@ FrameLayout FrameLayoutDetector::detectedLayout(bool detectPal60, bool detectNts
}
#endif
// Init the layout based on scanline analysis.
FrameLayout layout = myPalFrameSum > myNtscFrameSum ? FrameLayout::pal : FrameLayout::ntsc;
if(detectPal60 || detectNtsc50)
{
// Multiply each hue's count with its NTSC and PAL stats and aggregate results
// If NTSC/PAL results differ significantly, overrule frame result
FrameLayout layout = myPalFrames > myNtscFrames ? FrameLayout::pal : FrameLayout::ntsc;
constexpr std::array<double, NUM_HUES> ntscColorFactor{
0.00000, 0.05683, 0.06220, 0.05505, 0.06162, 0.02874, 0.03532, 0.03716,
0.15568, 0.06471, 0.02886, 0.03224, 0.06903, 0.11478, 0.02632, 0.01675
@ -84,13 +87,13 @@ FrameLayout FrameLayoutDetector::detectedLayout(bool detectPal60, bool detectNts
0.00000, 0.00450, 0.09962, 0.07603, 0.06978, 0.13023, 0.09638, 0.02268,
0.02871, 0.04700, 0.02950, 0.11974, 0.03474, 0.08025, 0.00642, 0.00167
}; // ignore black = 0x00!
// Calculation weights and params (optimum based on sampled ROMs optimized for PAL-60)
// Calculation weights and params (optimum based on sampled ROMs, optimized for PAL-60)
constexpr double POWER_FACTOR = 0.17; // Level the color counts (large values become less relevant)
constexpr uInt32 SUM_DIV = 20; // Skip too small counts
constexpr uInt32 SUM_DIV = 20; // Skip too small counts (could be removed)
constexpr uInt32 MIN_VALID = 3; // Minimum number of different hues with significant counts
constexpr double SUM_FACTOR = 2.0; // Minimum sum difference which triggers a layout change
constexpr double OVERRULE_FACTOR = 2.0; // Minimum sum difference which triggers a layout change
double ntscSum{0}, palSum{0};
double ntscColSum{ 0 }, paCollSum{ 0 };
std::array<double, NUM_HUES> hueSum{ 0 };
double totalHueSum = 0;
uInt32 validHues = 0;
@ -109,25 +112,31 @@ FrameLayout FrameLayoutDetector::detectedLayout(bool detectPal60, bool detectNts
{
if(hueSum[hue] > totalHueSum / SUM_DIV)
validHues++;
ntscSum += hueSum[hue] * ntscColorFactor[hue];
palSum += hueSum[hue] * palColorFactor[hue];
ntscColSum += hueSum[hue] * ntscColorFactor[hue];
paCollSum += hueSum[hue] * palColorFactor[hue];
}
// Correct layout if enough valid hues and significant sum difference
// TODO: Use fractional scanline counts for intermediate values around 285 scanlines, e.g.
// Desert Falcon, Dumbo's Flying Circus, Dungeon, Firefox, Millipede, Popeye, RS Basketball, Star Trek, Stunt Cycle
// Correct the layout if there are enough valid hues and a significant color sum difference.
// The required difference depends on the significance of the scanline analyis.
if(validHues >= MIN_VALID)
{
if(detectPal60 && layout == FrameLayout::ntsc && ntscSum * SUM_FACTOR < palSum)
// Use frame analysis results to scale color overrule factor from 1.0 .. OVERRULE_FACTOR
const double overRuleFactor = 1.0 + (OVERRULE_FACTOR - 1.0) * 2
* (std::max(myNtscFrameSum, myPalFrameSum) / (myNtscFrameSum + myPalFrameSum) - 0.5); // 1.0 .. OVERRULE_FACTOR
//cerr << overRuleFactor << " * PAL:" << paCollSum << "/NTSC:" << ntscColSum << endl;
if(detectPal60 && layout == FrameLayout::ntsc && ntscColSum * overRuleFactor < paCollSum)
{
layout = FrameLayout::pal60;
Logger::debug("Changed layout from NTSC into PAL-60");
Logger::debug("TV format changed from NTSC to PAL-60");
}
// Note: three false positives (Berzerk, Canyon Bomber, Jawbreaker) for NTSC-50 after
// Note: Three false positives (Adventure, Berzerk, Canyon Bomber) for NTSC-50 after
// optimizing for PAL-60
else if(detectNtsc50 && layout == FrameLayout::pal && palSum * SUM_FACTOR < ntscSum)
else if(detectNtsc50 && layout == FrameLayout::pal && paCollSum * overRuleFactor < ntscColSum)
{
layout = FrameLayout::ntsc50;
Logger::debug("Changed layout from PAL into NTSC-50");
Logger::debug("TV format changed from PAL to NTSC-50");
}
}
}
return layout;
@ -143,7 +152,7 @@ FrameLayoutDetector::FrameLayoutDetector()
void FrameLayoutDetector::onReset()
{
myState = State::waitForVsyncStart;
myNtscFrames = myPalFrames = 0;
myNtscFrameSum = myPalFrameSum = 0;
myLinesWaitingForVsyncToStart = 0;
myColorCount.fill(0);
myIsRendering = true;
@ -223,37 +232,14 @@ void FrameLayoutDetector::finalizeFrame()
if (myTotalFrames <= Metrics::initialGarbageFrames) return;
// Calculate the delta between scanline count and the sweet spot for the respective
// frame layouts
const uInt32
deltaNTSC = abs(static_cast<Int32>(myCurrentFrameFinalLines) - static_cast<Int32>(frameLinesNTSC)),
deltaPAL = abs(static_cast<Int32>(myCurrentFrameFinalLines) - static_cast<Int32>(frameLinesPAL));
// Does the scanline count fall into one of our tolerance windows? -> use it
if (std::min(deltaNTSC, deltaPAL) <= Metrics::tvModeDetectionTolerance)
layout(deltaNTSC <= deltaPAL ? FrameLayout::ntsc : FrameLayout::pal);
else if (
// If scanline count is odd and lies between the PAL and NTSC windows we assume
// it is NTSC (it would cause color loss on PAL CRTs)
(myCurrentFrameFinalLines < frameLinesPAL) &&
(myCurrentFrameFinalLines > frameLinesNTSC) &&
(myCurrentFrameFinalLines % 2)
)
layout(FrameLayout::ntsc);
else
// Take the nearest layout if all else fails
layout(deltaNTSC <= deltaPAL ? FrameLayout::ntsc : FrameLayout::pal);
switch (layout()) {
case FrameLayout::ntsc:
++myNtscFrames;
break;
case FrameLayout::pal:
++myPalFrames;
break;
default:
throw runtime_error("cannot happen");
}
// Calculate how close a frame is to PAL and NTSC based on scanlines. An odd scanline count
// results into a penalty of 0.5 for PAL. The result is between 0.0 (<=262 scanlines) and
// 1.0 (>=312) and added to PAL and (inverted) NTSC sums.
constexpr double ODD_PENALTY = 0.5; // guessed value :)
const double palFrame = BSPF::clamp(((myCurrentFrameFinalLines % 2) ? ODD_PENALTY : 1.0)
* static_cast<double>(myCurrentFrameFinalLines - frameLinesNTSC)
/ static_cast<double>(frameLinesPAL - frameLinesNTSC), 0.0, 1.0);
myPalFrameSum += palFrame;
myNtscFrameSum += 1.0 - palFrame;
//cerr << myCurrentFrameFinalLines << ", " << palFrame << ", " << myPalFrameSum << ", " << myNtscFrameSum << endl;
}

View File

@ -95,9 +95,6 @@ class FrameLayoutDetector: public AbstractFrameManager
// (exceeding ideal frame height)
waitForVsync = 100,
// tolerance window around ideal frame size for TV mode detection
tvModeDetectionTolerance = 20,
// these frames will not be considered for detection
initialGarbageFrames = TIAConstants::initialGarbageFrames
};
@ -121,10 +118,8 @@ class FrameLayoutDetector: public AbstractFrameManager
*/
State myState{State::waitForVsyncStart};
/**
* The total number of frames detected as the respective frame layout.
*/
uInt32 myNtscFrames{0}, myPalFrames{0};
// The aggregated likelynesses of respective two frame layouts.
double myNtscFrameSum{0}, myPalFrameSum{0};
/**
* We count the number of scanlines we spend waiting for vsync to be
@ -139,7 +134,6 @@ class FrameLayoutDetector: public AbstractFrameManager
*/
static constexpr int NUM_HUES = 16;
static constexpr int NUM_LUMS = 8;
std::array<uInt64, NUM_HUES * NUM_LUMS> myColorCount{0};
private: