rust-droid 0.1.1

A powerful UI automation framework for Android.
Documentation
use crate::common::rect::Rect as DroidRect;
use crate::error::{DroidError, Result};
use image::{DynamicImage, GenericImageView};
use imageproc::template_matching::{self, MatchTemplateMethod};
use std::path::Path;

#[derive(Debug, Clone, Copy)]
pub struct MatchResult {
    pub rect: DroidRect,
    pub confidence: f32,
}

pub fn find_template(
    haystack: &DynamicImage,
    needle: &DynamicImage,
    threshold: f32,
    needle_path: &Path,
    search_rect: Option<DroidRect>,
) -> Result<MatchResult> {
    log::debug!(
        "Searching for template {:?} with threshold {:.2} inside region {:?}",
        needle_path,
        threshold,
        search_rect
    );

    let needle_gray = needle.to_luma8();
    let haystack_gray_full = haystack.to_luma8();

    let (haystack_to_search, offset_x, offset_y) = if let Some(rect) = search_rect {
        let cropped_view = haystack_gray_full.view(rect.x, rect.y, rect.width, rect.height);
        (cropped_view.to_image(), rect.x, rect.y)
    } else {
        (haystack_gray_full, 0, 0)
    };

    let result = template_matching::match_template_parallel(
        &haystack_to_search,
        &needle_gray,
        MatchTemplateMethod::CrossCorrelationNormalized,
    );

    let extremes = imageproc::template_matching::find_extremes(&result);
    let best_match_value = extremes.max_value;
    let mut best_match_location = extremes.max_value_location;

    best_match_location.0 += offset_x;
    best_match_location.1 += offset_y;

    log::trace!(
        "Best match found with confidence {:.4} at absolute point ({}, {})",
        best_match_value,
        best_match_location.0,
        best_match_location.1
    );

    if best_match_value >= threshold {
        let (needle_width, needle_height) = needle.dimensions();
        let result_rect = DroidRect::new(
            best_match_location.0,
            best_match_location.1,
            needle_width,
            needle_height,
        );

        let match_result = MatchResult {
            rect: result_rect,
            confidence: best_match_value,
        };

        log::debug!("Match found: {:?}", match_result);
        Ok(match_result)
    } else {
        log::warn!(
            "No match found for {:?}. Best confidence was {:.4}, which is below threshold {:.4}",
            needle_path,
            best_match_value,
            threshold
        );
        Err(DroidError::ImageNotFound(needle_path.to_path_buf()))
    }
}