mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-27 21:58:08 -05:00
Further improve special/splatted calc
This commit is contained in:
@@ -269,7 +269,9 @@ sequenceDiagram
|
||||
a grey X, which a blown-out backdrop turns near-white while a wash stays
|
||||
tinted at every pulse phase (a big dark weapon render such as the
|
||||
Nautilus drum dilutes that tint, but a diluted wash still reads pale,
|
||||
which a tinted splat never does), and a ready read
|
||||
which a tinted splat never does; a pale backdrop showing through a
|
||||
splat's translucent plate tints it like a wash, but the X's grey strokes
|
||||
over the dark squid still give it away), and a ready read
|
||||
must also see a washed (ink-poor) body: pale backdrop, a ship's hull or
|
||||
the lead banner leaking past an icon edge fakes the shoulder glow, and a ready the shoulder glow does
|
||||
not corroborate needs the body's ink gone rather than merely paled,
|
||||
|
||||
@@ -46,6 +46,8 @@ import type { DetectedEvent, Detector, GateResult } from "../types";
|
||||
import {
|
||||
badgeRoi,
|
||||
CARD_LAYOUTS,
|
||||
CROSS_BLEED_MIN_FRACTION,
|
||||
CROSS_BLEED_MIN_LAPLACIAN,
|
||||
CROSS_MIN_FRACTION,
|
||||
CROSS_MIN_LAPLACIAN,
|
||||
CROSS_SATURATION_MIN,
|
||||
@@ -182,6 +184,17 @@ function minTopCorner(
|
||||
return { mean: Math.min(...means), saturation: satSum / n };
|
||||
}
|
||||
|
||||
/** the cross probe's saturated strokes are crisp enough to be the X, not scene bleed */
|
||||
function crossedOut(crossFraction: number, crossLap: number): boolean {
|
||||
if (crossFraction < CROSS_MIN_FRACTION) return false;
|
||||
return (
|
||||
crossLap >=
|
||||
(crossFraction > CROSS_BLEED_MIN_FRACTION
|
||||
? CROSS_BLEED_MIN_LAPLACIAN
|
||||
: CROSS_MIN_LAPLACIAN)
|
||||
);
|
||||
}
|
||||
|
||||
/** fraction of the probe that is saturated-and-bright (cross-out strokes) */
|
||||
function saturatedFraction(hsv: Mat, roi: Roi): number {
|
||||
const m = copyRoi(hsv, roi);
|
||||
@@ -452,9 +465,7 @@ export function createMinimapDetector(
|
||||
}
|
||||
const crossFraction = saturatedFraction(hsv, layout.cross);
|
||||
const crossLap = meanBrightness(lap, layout.cross);
|
||||
const occluded =
|
||||
crossFraction >= CROSS_MIN_FRACTION &&
|
||||
crossLap >= CROSS_MIN_LAPLACIAN;
|
||||
const occluded = crossedOut(crossFraction, crossLap);
|
||||
const corner = minTopCorner(gray, hsv, layout.weapon);
|
||||
const lightSurface =
|
||||
corner.mean >= SPECIAL_READY_MIN_CORNER_MEAN &&
|
||||
@@ -623,8 +634,7 @@ export function createMinimapDetector(
|
||||
}
|
||||
const crossFraction = saturatedFraction(hsv, layout.cross);
|
||||
const crossLap = meanBrightness(lap, layout.cross);
|
||||
const occluded =
|
||||
crossFraction >= CROSS_MIN_FRACTION && crossLap >= CROSS_MIN_LAPLACIAN;
|
||||
const occluded = crossedOut(crossFraction, crossLap);
|
||||
const corner = minTopCorner(gray, hsv, layout.weapon);
|
||||
const lightSurface =
|
||||
corner.mean >= SPECIAL_READY_MIN_CORNER_MEAN &&
|
||||
@@ -643,8 +653,7 @@ export function createMinimapDetector(
|
||||
}
|
||||
const crossFraction = saturatedFraction(hsv, enemyCrossRoi(cy));
|
||||
const crossLap = meanBrightness(lap, enemyCrossRoi(cy));
|
||||
const occluded =
|
||||
crossFraction >= CROSS_MIN_FRACTION && crossLap >= CROSS_MIN_LAPLACIAN;
|
||||
const occluded = crossedOut(crossFraction, crossLap);
|
||||
// light camo rows: pick template variant by corner brightness, raise ink threshold past it
|
||||
const corner = minTopCorner(gray, hsv, weaponRoi);
|
||||
const lightSurface =
|
||||
|
||||
@@ -113,11 +113,20 @@ export const CROSS_VALUE_MIN = 110;
|
||||
export const CROSS_MIN_FRACTION = 0.08;
|
||||
/**
|
||||
* Saturation alone is not a cross-out: the POV map screen bleeds team ink
|
||||
* through the cards (phantom fractions 0.61-0.65 on Um'ami, everyone alive).
|
||||
* The X is crisp while bleed is softened: mean |Laplacian| struck >=98 vs bleed
|
||||
* <=34 (clean cards read up to ~62 of line art at fraction ~0).
|
||||
* through the cards (phantom fractions 0.56-0.70 at mean |Laplacian| 22-42 on
|
||||
* the Um'ami and Triton cup VoDs, everyone alive), while the X is crisp
|
||||
* (clean cards read up to ~62 of line art, but at fraction ~0). The X is drawn
|
||||
* in the card owner's pale team tint, so over a bright team-colored scene
|
||||
* (Triton cup VoD, yellow team) its strokes soften: 21 struck cards and rows
|
||||
* there read 41-65 at fractions 0.11-0.34. Bleed saturates far more of the
|
||||
* probe, so past CROSS_BLEED_MIN_FRACTION only the crisp floor counts — the
|
||||
* two soft struck reads up there (0.56 at 55, 0.66 at 33) stay lost. Margins
|
||||
* are THIN (bleed 0.56 at 41.6 vs struck 0.15 at 41.5) — re-measure before
|
||||
* moving any.
|
||||
*/
|
||||
export const CROSS_MIN_LAPLACIAN = 65;
|
||||
export const CROSS_MIN_LAPLACIAN = 40;
|
||||
export const CROSS_BLEED_MIN_FRACTION = 0.5;
|
||||
export const CROSS_BLEED_MIN_LAPLACIAN = 65;
|
||||
|
||||
/** Sub-tile silhouettes matched shape-only (specials.ts) to split near-tied main-weapon icons. */
|
||||
export const SUB_TILE_TEMPLATE_SIZES = [24, 27, 30, 33, 36] as const;
|
||||
|
||||
@@ -33,6 +33,8 @@ import {
|
||||
STATUS_COMB_GAP_HALF_WIDTH,
|
||||
STATUS_COMB_MAX_SHIFT,
|
||||
STATUS_COMB_SIDE_SPANS,
|
||||
STATUS_CROSSED_MIN_BODY_DARK,
|
||||
STATUS_CROSSED_MIN_BODY_GREY,
|
||||
STATUS_DARK_MAX_VALUE,
|
||||
STATUS_DEAD_MAX_BODY_INK,
|
||||
STATUS_DEAD_MAX_SHOULDER_GLOW,
|
||||
@@ -447,7 +449,8 @@ function sideDecisiveness(reads: SlotRead[]): number {
|
||||
* backdrop turns the plate near-white, or the trough dims the wash under both
|
||||
* ready floors. Where a big dark weapon render dilutes the wash's tint as low
|
||||
* as a splat over a bright tinted backdrop reads, the splat's grey X strokes
|
||||
* still tell them apart. Only unsaturated glow counts as the wash's
|
||||
* still tell them apart, and they veto a tinted body outright when the dark
|
||||
* squid shows under them (a splat over a pale tinted backdrop). Only unsaturated glow counts as the wash's
|
||||
* (STATUS_GLOW_MAX_SPREAD): bright team ink lights the shoulder on its own
|
||||
* once the ink is light enough (orange clears the glow floor, lime does not).
|
||||
* The wash also replaces the body's ink, so an ink-heavy body means backdrop
|
||||
@@ -471,8 +474,11 @@ function classifySlot(
|
||||
// the wash glows pale on every layout; raw brightness is team ink or backdrop
|
||||
const washGlow = shoulderPaleGlow;
|
||||
const inkPoor = bodyInk <= STATUS_DEAD_MAX_BODY_INK;
|
||||
const crossed =
|
||||
bodyGrey >= STATUS_CROSSED_MIN_BODY_GREY &&
|
||||
bodyDark >= STATUS_CROSSED_MIN_BODY_DARK;
|
||||
const tinted =
|
||||
bodyTint >= STATUS_WASH_MIN_BODY_TINT ||
|
||||
(bodyTint >= STATUS_WASH_MIN_BODY_TINT && !crossed) ||
|
||||
(bodyTint >= STATUS_UNCROSSED_WASH_MIN_BODY_TINT &&
|
||||
bodyGrey <= STATUS_UNCROSSED_WASH_MAX_BODY_GREY &&
|
||||
bodyDark <= STATUS_UNCROSSED_WASH_MAX_BODY_DARK);
|
||||
|
||||
@@ -247,6 +247,19 @@ export const STATUS_UNCROSSED_WASH_MIN_BODY_TINT = 0.2;
|
||||
export const STATUS_UNCROSSED_WASH_MAX_BODY_GREY = 0.15;
|
||||
export const STATUS_UNCROSSED_WASH_MAX_BODY_DARK = 0.45;
|
||||
|
||||
/**
|
||||
* ...and a body the X does cross is never a wash, however tinted: a pale teal
|
||||
* or sky backdrop through the translucent plate lifts a splat's tint to
|
||||
* 0.30-0.46 (Triton cup VoD, 21 such splats over the whole VoD). Neither
|
||||
* stroke class alone splits them from washes: grey or two-tone weapon renders
|
||||
* put washes at grey up to 0.26, and dark renders (Nautilus drum, frying pan)
|
||||
* at dark up to 0.35. Together they do: those splats read grey >=0.21 at dark
|
||||
* >=0.17, while every wash reading grey >=0.2 reads dark <=0.16. Margins are
|
||||
* THIN on dark (0.16 vs 0.17) — re-measure before moving either.
|
||||
*/
|
||||
export const STATUS_CROSSED_MIN_BODY_GREY = 0.2;
|
||||
export const STATUS_CROSSED_MIN_BODY_DARK = 0.17;
|
||||
|
||||
/** Special ready: shoulder glow past this (attested >=0.40 vs <=0.06). */
|
||||
export const STATUS_READY_MIN_SHOULDER_GLOW = 0.25;
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"event": "Minimap",
|
||||
"data": {
|
||||
"spectator": false,
|
||||
"teammates": [
|
||||
{ "self": false, "dead": false },
|
||||
{ "self": false, "dead": false },
|
||||
{ "self": false, "dead": false },
|
||||
{ "self": true, "dead": true }
|
||||
],
|
||||
"enemies": [
|
||||
{ "dead": false },
|
||||
{ "dead": false },
|
||||
{ "dead": false },
|
||||
{ "dead": false }
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Triton cup VoD (v2811381928, 720p S3 POV), Museum d'Alfonsino SZ at 1:21, t=1782.25. The POV player (have faith, .52 Gal) was splatted at 1:22 and this is the respawn map fading in. The self card is struck through, but the X's pale strokes over a light yellow backdrop are soft on this frame: the cross probe reads saturated 0.33, while its crispness reads 49 |Laplacian|, under the 65 floor. The card read as alive, and that one read split the player's death on the timeline into a band too short to be real, which was debounced away. Half a second later the same card reads 81. Names, weapons and abilities are soft mid-fade and are not asserted."
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "narrow-right",
|
||||
"cast": null,
|
||||
"time": 206,
|
||||
"special": [
|
||||
[true, false, false, false],
|
||||
[false, false, false, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, true, false, false],
|
||||
[false, false, false, false]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"skipFields": ["layout"],
|
||||
"notes": "Triton cup VoD (v2811381928, 720p S3 POV), Museum d'Alfonsino SZ at 3:26, t=1657. Left slot 1 (Custom Blaster) is splatted from 3:27 to 3:24: a dark squid under a grey X, with a pale teal backdrop showing through the translucent plate. That backdrop tints the ink-poor body to 0.33-0.45, past the 0.30 wash floor, so the splat read as special-ready on two of its four reads. The remaining lone dead read was then debounced away, and the match lost one of the player's 8 deaths. The X strokes still show: grey 0.23-0.36, where every attested wash at that tint reads <=0.20. Left slot 0 holds a real wash. The strip sits at an in-between pitch here (left apexes ~[590,670,753,833]), so the layout is not asserted."
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
Reference in New Issue
Block a user