ai
3 мин
26 сентября 2026 г.
Источник: Dev.to AI Feed

Correlated objects demo

Max Kleiner
Max Kleiner
RSS AI Ingest
Correlated objects demo

Below is a self-contained maXbox / Object Pascal demo that implements contextual scoring for correlated objects. It does not require a neural network or image library: it assumes that a detector has already produced object candidates such a...

Below is a self-contained maXbox / Object Pascal demo that implements contextual scoring for correlated objects. It does not require a neural network or image library: it assumes that a detector has already produced object candidates such as car, fence, road, or rail, each with a local confidence and a bounding box. The program then builds pairwise spatial relations and iteratively updates each candidate’s probability using learned-like correlation rules. maXbox is an Object Pascal scripting environment based on PascalScript, intended for experimenting with algorithms and executable scripts in one application.[blogs.embarcadero][sourceforge] What it demonstrates The program models: A local detector confidence for every candidate. Pairwise relations based on: distance between bounding-box centers, relative horizontal/vertical position, overlap, relative size. Correlation rules such as: road → car is supportive if the car lies on/near the road, car → fence is mildly supportive only at short range, rail → train is strongly supportive when aligned and close, fence → car is weaker than car → fence in this sample. Iterative belief propagation-like rescoring: Each candidate receives messages from nearby candidates. A context message changes the candidate’s original detector log-odds. The local classifier remains the principal source of evidence. The relation is deliberately directional: Auto → Zaun and Zaun → Auto may have different weights. This reflects the fact that conditional probabilities are asymmetric: maXbox source code Paste the following into a new maXbox script and run it. It prints the initial and context-adjusted confidences to the console/log. program CorrelatedObjectsDemo; const MAX_OBJECTS = 12; MAX_ITERATIONS = 7; type TObjectClass = ( ocUnknown, ocCar, ocFence, ocRoad, ocRail, ocTrain, ocVegetation ); TObjectCandidate = record Name: string; ClassId: TObjectClass; LocalProbability: Double; Probability: Double; NewProbability: Double; X: Double; Y: Double; W: Double; H: Double; end; function ClassName(AClass: TObjectClass): string; begin case AClass of ocCar: Result:= 'Car'; ocFence: Result:= 'Fence'; ocRoad: Result:= 'Road'; ocRail: Result:= 'Rail'; ocTrain: Result:= 'Train'; ocVegetation: Result:= 'Vegetation'; else Result:= 'Unknown'; end; end; function Clamp(Value, LowValue, HighValue: Double): Double; begin Result:= Value; if Result HighValue then Result:= HighValue; end; function Sigmoid(Value: Double): Double; begin if Value > 30.0 then begin Result:= 1.0; Exit; end; if Value LeftEdge then LeftEdge:= B.X; RightEdge:= A.X + A.W; if B.X + B.W TopEdge then TopEdge:= B.Y; BottomEdge:= A.Y + A.H; if B.Y + B.H = MaxDistance then Result:= 0.0 else Result:= 1.0 - D / MaxDistance; end; function IsBInFrontOfA(const A, B: TObjectCandidate): Boolean; begin { Image coordinates: greater Y means lower in the picture. B is considered "in front" if it is lower than A. } Result:= CenterY(B) > CenterY(A); end; function RelationQuality(const Target, Evidence: TObjectCandidate): Double; var NearValue, HOverlap, VOverlap: Double; begin NearValue:= Nearness(Target, Evidence, 180.0); HOverlap:= HorizontalOverlap(Target, Evidence); VOverlap:= VerticalOverlap(Target, Evidence); Result:= NearValue; { Special geometries can strengthen a relation. } if (Target.ClassId = ocCar) and (Evidence.ClassId = ocRoad) then Result:= Clamp(0.55 * NearValue + 0.45 * HOverlap, 0.0, 1.0); if (Target.ClassId = ocTrain) and (Evidence.ClassId = ocRail) then Result:= Clamp(0.55 * NearValue + 0.45 * HOverlap, 0.0, 1.0); if (Target.ClassId = ocFence) and (Evidence.ClassId = ocCar) then begin Result:= 0.70 * NearValue; if IsBInFrontOfA(Target, Evidence) then Result:= Result + 0.15; Result:= Clamp(Result, 0.0, 1.0); end; if (Target.ClassId = ocCar) and (Evidence.ClassId = ocFence) then Result:= Clamp(0.60 * NearValue + 0.20 * VOverlap, 0.0, 1.0); end; function CorrelationWeight(TargetClass, EvidenceClass: TObjectClass): Double; begin Result:= 0.0; { Directional contextual weights. Positive = evidence supports the target hypothesis. Negative = evidence weakens the target hypothesis. } if (TargetClass = ocCar) and (EvidenceClass = ocRoad) then Result:= 1.30; if (TargetClass = ocRoad) and (EvidenceClass = ocCar) then Result:= 0.35; if (TargetClass = ocFence) and (EvidenceClass = ocCar) then Result:= 0.65; if (TargetClass = ocCar) and (EvidenceClass = ocFence) then Result:= 0.25; if (TargetClass = ocTrain) and (EvidenceClass = ocRail) then Result:= 1.60; if (TargetClass = ocRail) and (EvidenceClass = ocTrain) then Result:= 0.55; if (TargetClass = ocTrain) and (EvidenceClass = ocRoad) then Result:= -0.55; if (TargetClass = ocRail) and (EvidenceClass = ocVegetation) then Result:= -0.20; end; procedure PrintObjects(const Title: string; const Objects: array of TObjectCandidate; Count: Integer); var I: Integer; begin Writeln(''); Writeln(Title); Writeln('-------------------------------------------------------------'); for I:= 0 to Count - 1 do begin Writeln( Objects[I].Name + ' class=' + ClassName(Objects[I].ClassId) + ' local=' + FloatToStrF(Objects[I].LocalProbability, ffFixed, 8, 3) + ' final=' + FloatToStrF(Objects[I].Probability, ffFixed, 8, 3) + ' box=(' + FloatToStrF(Objects[I].X, ffFixed, 8, 0) + ',' + FloatToStrF(Objects[I].Y, ffFixed, 8, 0) + ',' + FloatToStrF(Objects[I].W, ffFixed, 8, 0) + ',' + FloatToStrF(Objects[I].H, ffFixed, 8, 0) + ')' ); end; end; procedure ExplainRelations(const Objects: array of TObjectCandidate; Count: Integer); var I, J: Integer; Weight, Quality, Message: Double; begin Writeln(''); Writeln('Relevant contextual relations'); Writeln('-------------------------------------------------------------'); for I:= 0 to Count - 1 do begin for J:= 0 to Count - 1 do begin if I J then begin Weight:= CorrelationWeight(Objects[I].ClassId, Objects[J].ClassId); if Weight 0.0 then begin Quality:= RelationQuality(Objects[I], Objects[J]); Message:= Weight * Quality * Objects[J].Probability; if Abs(Message) > 0.02 then Writeln( Objects[J].Name + ' -> ' + Objects[I].Name + ' weight=' + FloatToStrF(Weight, ffFixed, 8, 2) + ' relation=' + FloatToStrF(Quality, ffFixed, 8, 2) + ' evidence=' + FloatToStrF(Objects[J].Probability, ffFixed, 8, 2) + ' message=' + FloatToStrF(Message, ffFixed, 8, 3) ); end; end; end; end; end; type TCand_Objects = array[0..MAX_OBJECTS - 1] of TObjectCandidate; const CONTEXT_STRENGTH = 0.85; DAMPING = 0.55; procedure UpdateProbabilities(var Objects: TCand_Objects; {array of TObjectCandidate;} Count, Iterations: Integer); var I, J, Step: Integer; ContextMessage, Weight, Quality: Double; BaseLogit, UpdatedLogit: Double; begin for Step:= 1 to Iterations do begin for I:= 0 to Count - 1 do begin ContextMessage := 0.0; for J:= 0 to Count - 1 do begin if I J then begin Weight := CorrelationWeight( Objects[I].ClassId, Objects[J].ClassId ); if Weight 0.0 then begin Quality:= RelationQuality(Objects[I], Objects[J]); { Strong evidence has more influence. The relation quality goes to zero for distant or geometrically implausible neighbours. } ContextMessage:= ContextMessage + Weight * Quality * Objects[J].Probability; end; end; end; { The local detector remains the anchor. Context adjusts its log-odds rather than replacing it. } BaseLogit:= Logit(Objects[I].LocalProbability); UpdatedLogit:= BaseLogit + CONTEXT_STRENGTH * ContextMessage; Objects[I].NewProbability := Sigmoid(UpdatedLogit); end; { Damping avoids unstable oscillation during iterations. } for I:= 0 to Count - 1 do Objects[I].Probability := DAMPING * Objects[I].NewProbability + (1.0 - DAMPING) * Objects[I].Probability; Writeln(''); Writeln('Iteration ' + IntToStr(Step)); for I:= 0 to Count - 1 do Writeln( Objects[I].Name + ': ' + FloatToStrF(Objects[I].Probability, ffFixed, 8, 3) ); end; end; //type TCand_Objects = array[0..MAX_OBJECTS - 1] of TObjectCandidate; var Objects: TCand_Objects; //array[0..MAX_OBJECTS - 1] of TObjectCandidate; Count, I: Integer; begin //@main Writeln('Correlated Objects / maXbox Contextual Rescoring Demo'); Writeln('============================================================='); Count:= 6; { Candidate 0: weak local fence detection. } Objects[0].Name:= 'FenceCandidate'; Objects[0].ClassId := ocFence; Objects[0].LocalProbability:= 0.48; Objects[0].X:= 120; Objects[0].Y:= 130; Objects[0].W:= 150; Objects[0].H:= 18; { Candidate 1: a strong car detection in front of the fence. } Objects[1].Name:= 'CarCandidate'; Objects[1].ClassId := ocCar; Objects[1].LocalProbability := 0.93; Objects[1].X:= 160; Objects[1].Y:= 160; Objects[1].W:= 54; Objects[1].H:= 30; { Candidate 2: road context supporting the car. } Objects[2].Name:= 'RoadCandidate'; Objects[2].ClassId:= ocRoad; Objects[2].LocalProbability:= 0.89; Objects[2].X:= 75; Objects[2].Y:= 150; Objects[2].W:= 280; Objects[2].H:= 105; { Candidate 3: weak train hypothesis. } Objects[3].Name:= 'TrainCandidate'; Objects[3].ClassId:= ocTrain; Objects[3].LocalProbability:= 0.41; Objects[3].X:= 440; Objects[3].Y:= 310; Objects[3].W:= 155; Objects[3].H:= 42; { Candidate 4: a strong rail detection aligned with train. } Objects[4].Name:= 'RailCandidate'; Objects[4].ClassId:= ocRail; Objects[4].LocalProbability:= 0.92; Objects[4].X:= 400; Objects[4].Y:= 325; Objects[4].W:= 270; Objects[4].H:= 20; { Candidate 5: unrelated vegetation. } Objects[5].Name:= 'VegetationCandidate'; Objects[5].ClassId:= ocVegetation; Objects[5].LocalProbability := 0.86; Objects[5].X:= 720; Objects[5].Y:= 100; Objects[5].W:= 130; Objects[5].H:= 190; { Initialize final probability from local detector probability. } //var I: Integer; for I:= 0 to Count - 1 do begin Objects[I].Probability:= Objects[I].LocalProbability; Objects[I].NewProbability:= Objects[I].LocalProbability; end; PrintObjects('Initial detector output', Objects, Count); ExplainRelations(Objects, Count); UpdateProbabilities(Objects, Count, MAX_ITERATIONS); PrintObjects('Final probabilities after contextual rescoring',Objects,Count); Writeln(''); Writeln('Interpretation:'); Writeln('- FenceCandidate is strengthened by the nearby confident car.'); Writeln('- CarCandidate is strongly supported by the overlapping road.'); Writeln('- TrainCandidate is strengthened by the nearby aligned rail.'); Writeln('- Distant vegetation has almost no contextual influence.'); end. How the algorithm works For each candidate 𝑖 i, the code starts from its local detector probability: . It transforms that into log-odds: Then it adds weighted context messages from all other candidates ​ u ) is the directional semantic correlation weight, supplied by CorrelationWeight. q ) is RelationQuality, a value from 0 to 1 derived from distance, overlap, and selected geometrical tests. p is the current confidence of the evidence object. The context-adjusted probability is: A practical caveat This is a compact context-rescoring model, not a trained neural net. It is useful for validating the principle, testing a rule set, or post-processing detections from a separate model such as YOLO. For a production model, you would normally choose one of these approaches: Train an object detector or segmenter that learns context implicitly from large receptive fields and attention. Attach a graph neural network or relation module to detected objects; relation weights are learned end-to-end. Use a CRF over pixels/regions when boundary-aware semantic segmentation is the main task. Retain an explicit rule-based post-processor like this when relations must be inspectable, auditable, and easily adapted to a specific GEOINT or infrastructure domain. pilotfiber.dl.sourceforge.net

Хотите внедрить ИИ в ваш бренд?

Спроектируем и развернем автономных агентов и современный цифровой стек под ваши задачи.

Рассчитать проект