Files
rustautogui/tests/tmpl_match_tests.rs
9e2d31ad6d V2.5.0 - Opencl + more github actions + code refactor + tests + custom threshold + lite + dev
* opencl first buggy implementation

* more work on opencl. Buggy atm

* WORKING ALGORITHM ATM but slow

* working opencl code with implemented 2 step fully

* first reworks. addition of new fields in struct and some cleanup

* more changes. added a struct for memory buffers, creation of it, new functions for gui dedicated opencl template match

* more changes, no errors

* added to main functions in lib. Not tested yet

* first tests done, code working. Needs more optimisation

* some testing prints, code retested after fix in time measurement. Looks promising

* revert back

* tested optimisations reimplemented and working

* a bit more optimisations

* added opencl feature

* fmt

* added change ocl state, graceful exits

* windows fix

* small optimisation

* macos fix

* different variations of tests

* currently a fine solution to auto determine treshold of fast segmentation

* derivative activation only in opencl mode

* readme update

* add v2 algorithm

* added table of contents to readme

* update changelog (no date included yet)

* moved all the additional v2 buffers  under GpuMemoryPointers struct

* additional work on v2

* unifying v1 and v2 kernels + other data

* working on adding devices info

* more work done on detecting devices, creating kernels and moving kernels to precalculated

* working state of code. Implemented different OCL things, Kernel building outside of OCL Kernel storage, most of logic for V2.

* added v2 into logic. remaining to create standalone lib.rs functions

* various fixes

* added precision to buffer passed to GPU and updated

* adding more matchmodes and covering

* cleaning up code

* moving some enums to structs

* changes on lots of stuff, including changing device for OCL

* implemented lite feature

* changes on lite code, removed problems

* renamed template match

* cleaned unnecesary conditions

* fixed issue with memory not cleaned up on v2

* readme update and fix on matchmode

* fix for region on ocl matching

* fixes for ltie mode

* added opencl example

* fix for v2 opencl

* fix on opencl device choice to not take cpu

* lite fixes on macos

* linux fixes

* windows fix

* moved files to CORE folder

* full code restructure

* full code restructure

* started with testing

* more tests

* additional mac tests

* linux additions

* linux fixes + cargo.toml editing

* more fixes

* clippy fixes

* github action update

---------

Co-authored-by: DavorMar <[email protected]>
2025-04-27 14:00:11 +02:00

304 lines
10 KiB
Rust

// run with cargo test --tests --release -- --nocapture
#[cfg(feature = "dev")]
pub mod tmpl_match_tests {
use rustautogui::core::template_match::open_cl::OclVersion;
use rustautogui::core::template_match::*;
use rustautogui::data::{opencl::KernelStorage, opencl::*, PreparedData};
use rustautogui::imgtools;
#[test]
fn testing_speeds() {
let image_paths = vec![
"tests/testing_images/algorithm_tests/Darts_main.png",
"tests/testing_images/algorithm_tests/Darts_main.png",
"tests/testing_images/algorithm_tests/Darts_main.png",
"tests/testing_images/algorithm_tests/Socket_main.png",
"tests/testing_images/algorithm_tests/Socket_main.png",
"tests/testing_images/algorithm_tests/Socket_main.png",
"tests/testing_images/algorithm_tests/Split_main.png",
"tests/testing_images/algorithm_tests/Split_main.png",
// "tests/testing_images/algorithm_tests/Split_main.png",
"tests/testing_images/algorithm_tests/Split_main.png",
"tests/testing_images/algorithm_tests/Split_main.png",
];
let template_paths = vec![
"tests/testing_images/algorithm_tests/Darts_template1.png",
"tests/testing_images/algorithm_tests/Darts_template2.png",
"tests/testing_images/algorithm_tests/Darts_template3.png",
"tests/testing_images/algorithm_tests/Socket_template1.png",
"tests/testing_images/algorithm_tests/Socket_template2.png",
"tests/testing_images/algorithm_tests/Socket_template3.png",
"tests/testing_images/algorithm_tests/Split_template1.png",
"tests/testing_images/algorithm_tests/Split_template2.png",
// "tests/testing_images/algorithm_tests/Split_template3.png",
"tests/testing_images/algorithm_tests/Split_template4.png",
"tests/testing_images/algorithm_tests/Split_template5.png",
];
let target_positions: Vec<(i32, i32)> = vec![
(206, 1),
(60, 270),
(454, 31),
(197, 345),
(81, 825),
(359, 666),
(969, 688),
(713, 1389),
(1273, 1667),
(41, 53),
];
#[cfg(not(feature = "lite"))]
for ((img_val, tmpl_val), target_position) in
image_paths.iter().zip(template_paths).zip(target_positions)
{
testing_run(*img_val, tmpl_val, target_position);
}
}
fn segmented_run(
template: &image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
main_image: &image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
target_positions: (i32, i32),
template_path: &str,
custom: bool,
) {
let mut threshold = None;
let mut insert_str = String::new();
if custom {
threshold = Some(0.5);
insert_str.push_str("custom");
}
let template_data = segmented_ncc::prepare_template_picture(&template, &false, threshold);
let template_data = match template_data {
PreparedData::Segmented(data) => data,
_ => panic!(),
};
let mut dur = 0.0;
let mut locations: Vec<(u32, u32, f32)> = Vec::new();
if template_path == "testing_images2/Split_template3.png" {
} else {
let start = std::time::Instant::now();
locations =
segmented_ncc::fast_ncc_template_match(&main_image, 0.95, &template_data, &false);
dur = start.elapsed().as_secs_f32();
}
let mut first_location = (0, 0, 0.0);
if locations.len() > 0 {
first_location = locations[0];
println!(
"Segmented {insert_str}: Location found at {}, {} and corr {}, time: {} ",
first_location.0, first_location.1, locations[0].2, dur
);
}
assert!(
first_location.0 == target_positions.0 as u32
&& first_location.1 == target_positions.1 as u32
);
}
#[cfg(feature = "opencl")]
fn ocl_run(
template: &image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
main_image: &image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
target_positions: (i32, i32),
custom: bool,
ocl_v: OclVersion,
image_width: u32,
image_height: u32,
template_width: u32,
template_height: u32,
) {
use rustautogui::RustAutoGui;
let data = RustAutoGui::dev_setup_opencl(Some(1)).unwrap();
let mut threshold = None;
let mut insert_str = String::new();
let mut v_string = String::new();
if custom {
threshold = Some(0.7);
insert_str.push_str("custom");
}
match ocl_v {
OclVersion::V1 => v_string.push('1'),
OclVersion::V2 => v_string.push('2'),
}
// Create a context for that device
let queue = data.ocl_queue;
let program = data.ocl_program;
//////////////////////////////////////////////////////////////////////// OPENCL V1
let template_data = segmented_ncc::prepare_template_picture(&template, &false, threshold);
let template_data = match template_data {
PreparedData::Segmented(x) => x,
_ => panic!(),
};
let gpu_pointers = GpuMemoryPointers::new(
image_width,
image_height,
template_width,
template_height,
&queue,
&template_data.template_segments_slow,
&template_data.template_segments_fast,
)
.unwrap();
let kernels = KernelStorage::new(
&gpu_pointers,
&program,
&queue,
image_width,
image_height,
template_width,
template_height,
template_data.template_segments_fast.len() as u32,
template_data.template_segments_slow.len() as u32,
template_data.segments_mean_fast,
template_data.segments_mean_slow,
template_data.segment_sum_squared_deviations_fast,
template_data.segment_sum_squared_deviations_slow,
template_data.expected_corr_fast,
256,
)
.unwrap();
let start = std::time::Instant::now();
let locations = open_cl::gui_opencl_ncc_template_match(
&queue,
&program,
256,
&kernels,
&gpu_pointers,
0.95,
&main_image,
&template_data,
ocl_v,
)
.unwrap();
let mut first_location = (0, 0, 0.0);
if locations.len() > 0 {
first_location = locations[0];
println!(
"OCL V{v_string} {insert_str}: Location found at {:?}, time: {}",
first_location,
start.elapsed().as_secs_f32()
);
}
assert!(
first_location.0 == target_positions.0 as u32
&& first_location.1 == target_positions.1 as u32
);
}
fn fft_run(
template: &image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
main_image: &image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
target_positions: (i32, i32),
image_width: u32,
image_height: u32,
) {
// fft corr
let template_data = fft_ncc::prepare_template_picture(&template, image_width, image_height);
let start = std::time::Instant::now();
let locations = fft_ncc::fft_ncc(&main_image, 0.90, &template_data);
let mut first_location = (0, 0, 0.0);
if locations.len() > 0 {
first_location.0 = locations[0].0;
first_location.1 = locations[0].1;
println!(
"FFT: Location found at {}, {} and corr {} , time: {}",
first_location.0,
first_location.1,
locations[0].2,
start.elapsed().as_secs_f32()
);
}
assert!(
first_location.0 == target_positions.0 as u32
&& first_location.1 == target_positions.1 as u32
);
println!("\n");
}
fn testing_run(image_path: &str, template_path: &str, target_positions: (i32, i32)) {
let template: image::ImageBuffer<image::Luma<u8>, Vec<u8>> =
imgtools::load_image_bw(template_path).unwrap();
let main_image: image::ImageBuffer<image::Luma<u8>, Vec<u8>> =
imgtools::load_image_bw(image_path).unwrap();
let (image_width, image_height) = main_image.dimensions();
let (template_width, template_height) = template.dimensions();
println!("Image: {}, template:{}", image_path, template_path);
segmented_run(
&template,
&main_image,
target_positions,
template_path,
false,
); // default cpu
segmented_run(
&template,
&main_image,
target_positions,
template_path,
true,
); // cpu custom
ocl_run(
&template,
&main_image,
target_positions,
false,
OclVersion::V1,
image_width,
image_height,
template_width,
template_height,
);
ocl_run(
&template,
&main_image,
target_positions,
false,
OclVersion::V2,
image_width,
image_height,
template_width,
template_height,
);
ocl_run(
&template,
&main_image,
target_positions,
true,
OclVersion::V1,
image_width,
image_height,
template_width,
template_height,
);
ocl_run(
&template,
&main_image,
target_positions,
true,
OclVersion::V2,
image_width,
image_height,
template_width,
template_height,
);
fft_run(
&template,
&main_image,
target_positions,
image_width,
image_height,
);
}
}