Pure MoonBit 2D local features, binary matching, geometric registration, and diagnostics
moon version --all
moon update
moon fmt
moon check --deny-warn
moon test --deny-warn
moon run cmd/mainimport {
"Myytsjj/moonbit-feature-forge" @forge,
}
fn register(reference : @forge.GrayImage, moving : @forge.GrayImage) -> Unit {
let result = @forge.run_pipeline(
reference,
moving,
@forge.FeatureConfig::default(),
)
println(@forge.format_pipeline_summary(result))
}moon run cmd/mainMoonBit Feature Forge
pipeline=demo size=120x120
features1=26 features2=25 matches=23
affine_inliers=23 homography_inliers=23
affine_ratio=1 homography_rms=0
features1=26 features2=25 matches=23 distance_mean=0 affine_inliers=23 homography_inliers=23moon run cmd/main -- benchmarkGrayImage
│
├── sampling / filters / integral analysis / pyramid
│
├── FAST ──► keypoints ──► ORB/BRIEF descriptors
│ │
│ ├── KNN / ratio / mutual / index matching
│ │
│ └── affine + homography RANSAC
│ │
└── warping / tracking / quality reports ◄┘| Area | Representative entry points |
|---|---|
| Image data | GrayImage::new, sample, sample_bilinear, crop, resize_nearest |
| Preprocessing | box_blur, sobel_gradients, ImagePyramid::build, adaptive_threshold |
| Features | FastConfig, detect_fast_with_config, OrbConfig, compute_orb_descriptors_with_config |
| Matching | MatchPolicy, match_with_policy, mutual_match, DescriptorIndex::query |
| Geometry | estimate_homography, Homography::project, reprojection_stats, ransac_homography |
| Pipeline | FeatureConfig, run_pipeline, analyze_pipeline, build_quality_dashboard |
| Tracking | KeypointGridIndex, associate_keypoints, FeatureTrack, update_tracks |
| Workload | Size | Iterations | Features | Matches | Homography inlier ratio | Total |
|---|---|---|---|---|---|---|
| Synthetic translation | 160×160 | 3 | 32 / 31 | 29 | 1.0 | 26 ms |
moon test --deny-warnpub(all) struct AffineDecomposition {
scale_x : Double
scale_y : Double
rotation : Double
shear : Double
translation_x : Double
translation_y : Double
reflected : Bool
}pub(all) struct AffineFitResult {
transform : AffineTransform
residuals : Array[Double]
mean_error : Double
maximum_error : Double
condition : Double
valid : Bool
}pub(all) struct AffineTransform {
a : Double
b : Double
tx : Double
c : Double
d : Double
ty : Double
}pub(all) struct BenchmarkAggregate {
count : Int
total_iterations : Int
total_features1 : Int
total_features2 : Int
total_matches : Int
mean_features1 : Double
mean_features2 : Double
mean_matches : Double
mean_inlier_ratio : Double
minimum_ms : Double
maximum_ms : Double
mean_ms : Double
}pub(all) struct BenchmarkResult {
width : Int
height : Int
iterations : Int
features1 : Int
features2 : Int
matches : Int
inlier_ratio : Double
total_ms : Double
}fn BenchmarkResult::new(width : Int, height : Int, iterations : Int, features1 : Int, features2 : Int, matches : Int, inlier_ratio : Double, total_ms : Double) -> BenchmarkResultpub(all) struct DescriptorBucketStats {
bucket_count : Int
non_empty_buckets : Int
maximum_bucket : Int
mean_bucket : Double
}pub(all) struct DescriptorQualityReport {
count : Int
bytes : Int
unique : Int
duplicate_fraction : Double
bit_balance : Double
mean_nearest_distance : Double
}pub(all) struct DescriptorSearchReport {
queries : Int
candidates : Int
comparisons : Int
mean_best_distance : Double
exact : Bool
}pub(all) struct DescriptorSignature {
bytes : Int
checksum : Int
popcount : Int
zero_bytes : Int
nonzero_bytes : Int
}pub(all) struct DescriptorStats {
count : Int
bytes : Int
mean_popcount : Double
minimum_popcount : Int
maximum_popcount : Int
bit_balance : Double
}pub(all) struct FastConfig {
threshold : Int
contiguous : Int
use_nms : Bool
max_keypoints : Int
border : Int
min_response : Double
}fn FastConfig::new(threshold : Int, contiguous : Int, use_nms : Bool, max_keypoints : Int, border : Int, min_response : Double) -> FastConfigpub(all) struct FeatureAssociation {
query_idx : Int
train_idx : Int
descriptor_distance : Int
spatial_distance : Double
}pub(all) struct FeatureConfig {
fast : FastConfig
orb : OrbConfig
pyramid_levels : Int
match_k : Int
ratio : Double
use_ratio : Bool
enforce_unique : Bool
ransac_iters : Int
ransac_threshold : Double
seed : Int
}pub(all) struct FeatureQualityReport {
count : Int
density : Double
response_mean : Double
response_variance : Double
spatial_coverage : Double
border_safe : Int
}pub(all) struct FeatureSnapshot {
keypoints : Int
descriptors : Int
descriptor_bytes : Int
mean_response : Double
bounds : Rect
fingerprint : Int
}pub(all) struct FeatureTrack {
id : Int
keypoints : Array[Keypoint]
descriptors : Array[Descriptor]
age : Int
missed : Int
active : Bool
}fn FeatureTrack::append(self : FeatureTrack, keypoint : Keypoint, descriptor : Descriptor) -> FeatureTrackpub(all) struct FloatPoint2 {
x : Double
y : Double
}pub(all) struct GeometryQuality {
count : Int
mean_error : Double
rms_error : Double
maximum_error : Double
passed : Bool
}fn GrayImage::rotate_about(self : GrayImage, angle : Double, center : FloatPoint2, border : BorderMode) -> GrayImagefn GrayImage::sample_bilinear(self : GrayImage, x : Double, y : Double, border : BorderMode) -> Doublepub(all) struct Homography {
h00 : Double
h01 : Double
h02 : Double
h10 : Double
h11 : Double
h12 : Double
h20 : Double
h21 : Double
h22 : Double
}pub(all) struct HomographyRansacResult {
model : Homography
inliers : Array[Match]
inlier_ratio : Double
stats : ReprojectionStats
iterations : Int
seed : Int
}pub(all) struct Keypoint {
x : Int
y : Int
response : Double
angle : Double
octave : Int
size : Int
}fn KeypointGridIndex::build(keypoints : Array[Keypoint], width : Int, height : Int, grid_width : Int, grid_height : Int) -> KeypointGridIndexfn KeypointGridIndex::nearest(self : KeypointGridIndex, center : FloatPoint2, radius : Double) -> Int?fn KeypointGridIndex::query_radius(self : KeypointGridIndex, center : FloatPoint2, radius : Double) -> Array[Int]pub(all) struct KeypointStats {
count : Int
mean_response : Double
minimum_response : Double
maximum_response : Double
centroid : FloatPoint2
bounds : Rect
}pub(all) struct Match {
query_idx : Int
train_idx : Int
distance : Int
}pub(all) struct MatchGraphStats {
match_count : Int
query_nodes : Int
train_nodes : Int
unique_queries : Int
unique_trains : Int
duplicate_queries : Int
duplicate_trains : Int
mean_distance : Double
maximum_distance : Int
}pub(all) struct MatchPolicy {
k : Int
ratio : Double
use_ratio : Bool
enforce_unique : Bool
}fn MatchPolicy::new(k : Int, ratio : Double, use_ratio : Bool, enforce_unique : Bool) -> MatchPolicypub(all) struct MatchQuality {
count : Int
inliers : Int
ratio : Double
mean_residual : Double
maximum_residual : Double
}pub(all) struct ModelComparison {
affine_score : Double
homography_score : Double
preferred : String
affine_inliers : Int
homography_inliers : Int
}pub(all) struct OrbConfig {
patch_radius : Int
descriptor_bits : Int
use_orientation : Bool
}pub(all) struct PatchSearchResult {
x : Int
y : Int
score : Double
evaluated : Int
}pub(all) struct PatchStats {
count : Int
mean : Double
variance : Double
minimum : Int
maximum : Int
energy : Double
}pub(all) struct PhotometricComparison {
overlap_width : Int
overlap_height : Int
mse : Double
mean_difference : Double
foreground_iou : Double
}pub(all) struct PhotometricReport {
width : Int
height : Int
mean : Double
variance : Double
minimum : Int
maximum : Int
nonzero : Int
contrast : Double
}pub(all) struct PipelineQualityGate {
passed : Bool
inlier_ratio : Double
inlier_count : Int
match_count : Int
maximum_error : Double
score : Double
}pub(all) struct PipelineReport {
features1 : KeypointStats
features2 : KeypointStats
descriptors1 : DescriptorStats
descriptors2 : DescriptorStats
matches : MatchGraphStats
affine_quality : MatchQuality
homography_quality : MatchQuality
gate : PipelineQualityGate
}pub(all) struct PipelineResult {
keypoints1 : Array[Keypoint]
keypoints2 : Array[Keypoint]
descriptors1 : Array[Descriptor]
descriptors2 : Array[Descriptor]
matches : Array[Match]
match_stats : MatchStats
affine : RansacResult
homography : HomographyRansacResult
}pub(all) struct Point2 {
x : Int
y : Int
}pub(all) struct PyramidSummary {
levels : Int
original_width : Int
original_height : Int
final_width : Int
final_height : Int
final_scale : Double
}pub(all) struct QualityDashboard {
image1 : PhotometricReport
image2 : PhotometricReport
features1 : FeatureQualityReport
features2 : FeatureQualityReport
descriptors1 : DescriptorQualityReport
descriptors2 : DescriptorQualityReport
match_graph : MatchGraphStats
gate : PipelineQualityGate
fingerprint : Int
}pub(all) struct RansacResult {
transform : AffineTransform
inliers : Array[Match]
inlier_ratio : Double
}pub(all) struct Rect {
x0 : Int
y0 : Int
x1 : Int
y1 : Int
}pub(all) struct RegionStats {
label : Int
area : Int
bounds : Rect
centroid : FloatPoint2
mean : Double
minimum : Int
maximum : Int
}pub(all) struct ReprojectionStats {
count : Int
mean : Double
rms : Double
maximum : Double
}pub(all) struct ResidualSummary {
count : Int
mean : Double
median : Double
rms : Double
maximum : Double
inliers : Int
ratio : Double
}pub(all) struct SnapshotDelta {
keypoints : Int
descriptors : Int
descriptor_bytes : Int
response_delta : Double
same_shape : Bool
}pub(all) struct ThresholdSummary {
threshold : Int
foreground : Int
background : Int
mean_foreground : Double
mean_background : Double
}pub(all) struct ThresholdTrial {
threshold : Int
keypoints : Int
descriptors : Int
mean_response : Double
score : Double
}pub(all) struct TrackSummary {
count : Int
active : Int
total_age : Int
mean_age : Double
mean_path_length : Double
mean_stability : Double
}pub(all) struct TranslationSearchResult {
dx : Int
dy : Int
score : Double
evaluated : Int
}fn affine_model_residual_summary(model : AffineTransform, source : Array[FloatPoint2], target : Array[FloatPoint2], threshold : Double) -> ResidualSummaryfn affine_residual_profile(transform : AffineTransform, source : Array[FloatPoint2], target : Array[FloatPoint2]) -> Array[Double]fn affine_transform_points(transform : AffineTransform, points : Array[FloatPoint2]) -> Array[FloatPoint2]fn associate_keypoints(query_keypoints : Array[Keypoint], query_descriptors : Array[Descriptor], train_keypoints : Array[Keypoint], train_descriptors : Array[Descriptor], maximum_descriptor_distance : Int, maximum_spatial_distance : Double) -> Array[FeatureAssociation]fn association_quality(associations : Array[FeatureAssociation], maximum_descriptor_distance : Int, maximum_spatial_distance : Double) -> Doublefn association_spatial_histogram(associations : Array[FeatureAssociation], bins : Int, maximum : Double) -> Array[Int]fn blend_registered(reference : GrayImage, moving : GrayImage, model : Homography, weight : Double) -> GrayImagefn build_quality_dashboard(image1 : GrayImage, image2 : GrayImage, result : PipelineResult) -> QualityDashboardfn compare_models(affine : RansacResult, homography : HomographyRansacResult, affine_residual : Double, homography_residual : Double) -> ModelComparisonfn descriptor_batch_search_report(index : DescriptorIndex, queries : Array[Descriptor], k : Int) -> DescriptorSearchReportfn descriptor_distance_matrix(query : Array[Descriptor], train : Array[Descriptor]) -> Array[Array[Int]]fn descriptor_distance_quantiles(query : Descriptor, candidates : Array[Descriptor]) -> (Int, Int, Int)fn descriptor_hamming_histogram(query : Array[Descriptor], train : Array[Descriptor], bins : Int) -> Array[Int]fn descriptor_index_batch_query(index : DescriptorIndex, queries : Array[Descriptor], k : Int) -> Array[Array[Match]]fn estimate_affine_from_points(source : Array[FloatPoint2], target : Array[FloatPoint2]) -> AffineTransform?fn evaluate_affine_matches(transform : AffineTransform, matches : Array[Match], keypoints1 : Array[Keypoint], keypoints2 : Array[Keypoint], threshold : Double) -> MatchQualityfn evaluate_homography_matches(model : Homography, matches : Array[Match], keypoints1 : Array[Keypoint], keypoints2 : Array[Keypoint], threshold : Double) -> MatchQualityfn extract_multiscale_features(img : GrayImage, fast_config : FastConfig, orb_config : OrbConfig, level_count : Int) -> (Array[Keypoint], Array[Descriptor])fn feature_quality_report(keypoints : Array[Keypoint], width : Int, height : Int, border : Int) -> FeatureQualityReportfn filter_regions(regions : Array[RegionStats], minimum_area : Int, maximum_area : Int) -> Array[RegionStats]fn filter_tracks_by_velocity(tracks : Array[FeatureTrack], consensus : FloatPoint2, tolerance : Double) -> Array[FeatureTrack]fn full_feature_pipeline(img1 : GrayImage, img2 : GrayImage, fast_threshold : Int, ratio : Double, ransac_threshold : Double) -> RansacResultfn homography_quality_report(model : Homography, source : Array[FloatPoint2], target : Array[FloatPoint2]) -> GeometryQualityfn homography_residual_profile(model : Homography, source : Array[FloatPoint2], target : Array[FloatPoint2]) -> Array[Double]fn initialize_tracks(keypoints : Array[Keypoint], descriptors : Array[Descriptor], start_id : Int) -> Array[FeatureTrack]fn is_degenerate_triangle(first : FloatPoint2, second : FloatPoint2, third : FloatPoint2, tolerance : Double) -> Boolfn keypoint_patch_statistics(img : GrayImage, keypoints : Array[Keypoint], radius : Int) -> Array[PatchStats]fn line_intersection(first : FloatPoint2, second : FloatPoint2, third : FloatPoint2, fourth : FloatPoint2) -> FloatPoint2?fn match_and_filter(desc1 : Array[Descriptor], desc2 : Array[Descriptor], ratio : Double) -> Array[Match]fn match_with_policy(query : Array[Descriptor], train : Array[Descriptor], policy : MatchPolicy) -> Array[Match]fn model_residual_summary(model : Homography, source : Array[FloatPoint2], target : Array[FloatPoint2], threshold : Double) -> ResidualSummaryfn mutual_match(query : Array[Descriptor], train : Array[Descriptor], policy : MatchPolicy) -> Array[Match]fn pipeline_quality_gate(inlier_ratio : Double, inlier_count : Int, match_count : Int, maximum_error : Double) -> PipelineQualityGatefn project_to_segment(point : FloatPoint2, first : FloatPoint2, second : FloatPoint2) -> FloatPoint2fn pyramid_translation_search(reference : GrayImage, moving : GrayImage, levels : Int, radius : Int) -> TranslationSearchResultfn quality_score_from_counts(true_positive : Int, false_positive : Int, false_negative : Int) -> Doublefn ransac_required_iterations(confidence : Double, inlier_ratio : Double, sample_size : Int, maximum : Int) -> Intfn ransac_success_probability(iterations : Int, inlier_ratio : Double, sample_size : Int) -> Doublefn reprojection_stats(model : Homography, source : Array[FloatPoint2], target : Array[FloatPoint2]) -> ReprojectionStatsfn search_patch(image : GrayImage, patch : GrayImage, origin_x : Int, origin_y : Int, radius : Int) -> PatchSearchResultfn search_translation(reference : GrayImage, moving : GrayImage, maximum_dx : Int, maximum_dy : Int) -> TranslationSearchResultfn snapshot_features(keypoints : Array[Keypoint], descriptors : Array[Descriptor]) -> FeatureSnapshotfn stable_tracks(tracks : Array[FeatureTrack], minimum_age : Int, minimum_stability : Double) -> Array[FeatureTrack]fn summarize_match_graph(matches : Array[Match], query_count : Int, train_count : Int) -> MatchGraphStatsfn update_tracks(tracks : Array[FeatureTrack], keypoints : Array[Keypoint], descriptors : Array[Descriptor], maximum_descriptor_distance : Int, maximum_spatial_distance : Double, maximum_missed : Int) -> Array[FeatureTrack]fn warp_affine_image(img : GrayImage, transform : AffineTransform, width : Int, height : Int, options : WarpOptions) -> GrayImagefn warp_homography_image(img : GrayImage, model : Homography, width : Int, height : Int, options : WarpOptions) -> GrayImagefn warp_valid_mask(model : Homography, source_width : Int, source_height : Int, width : Int, height : Int) -> GrayImageInstall
Download zipPure MoonBit 2D local features, binary matching, geometric registration, and diagnostics