added Documentary
This commit is contained in:
parent
5b5e8a48f5
commit
6b140e0978
|
@ -1,3 +1,30 @@
|
||||||
|
//!
|
||||||
|
//! This module provides the Database for Images and compare methods to search in it.
|
||||||
|
//!
|
||||||
|
//! The database Struct provides the Images and has a threadpool to efficiently process all given features for all Images
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! to generate a database you need a vector of paths of picture that you want to save and search in it.
|
||||||
|
//! You also need a Vector of Feature generator functions that generates the feature of every image
|
||||||
|
//!
|
||||||
|
//!```
|
||||||
|
//! # use std::path::{PathBuf};
|
||||||
|
//! # use imsearch::image::Image;
|
||||||
|
//! # use imsearch::search_index;
|
||||||
|
//! use imsearch::search_index::FeatureGenerator;
|
||||||
|
//!
|
||||||
|
//! let path: Vec<PathBuf> = Vec::new();
|
||||||
|
//! let features: Vec<FeatureGenerator> = Vec::new();
|
||||||
|
//!
|
||||||
|
//! let Database = search_index::Database::new(&path, features );
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!This Library provides some Feature generator functions but you can also create your own.
|
||||||
|
//!The Feature generator has to fit in the "FeatureGenerator" type to work with the database.
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
|
||||||
use crate::image::Image;
|
use crate::image::Image;
|
||||||
use crate::multithreading::{Task, ThreadPool};
|
use crate::multithreading::{Task, ThreadPool};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -7,11 +34,13 @@ use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
///this trait provides a function to compare objects and returns a f32 between 0 and 1.
|
||||||
|
/// 1 is identical and 0 is different. with this trait you get the similarity between the objects
|
||||||
trait WeightedCmp {
|
trait WeightedCmp {
|
||||||
fn weighted(&self, other: &Self) -> f32;
|
fn weighted(&self, other: &Self) -> f32;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Every feature returns a known and sized type
|
/// Every feature returns a known and sized type from this enum
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum FeatureResult {
|
pub enum FeatureResult {
|
||||||
/// A boolean. Just a boolean
|
/// A boolean. Just a boolean
|
||||||
|
@ -31,7 +60,7 @@ pub enum FeatureResult {
|
||||||
Char(char),
|
Char(char),
|
||||||
///A String ;)
|
///A String ;)
|
||||||
String(String),
|
String(String),
|
||||||
///a f32 between 0 and 1
|
///a f32 between 0 and 1 where 1 is 100% and 0 is 0%
|
||||||
Percent(f32),
|
Percent(f32),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +71,6 @@ impl Default for FeatureResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For some feature return type we want to implement a custom compare function
|
/// For some feature return type we want to implement a custom compare function
|
||||||
/// for example: histograms are compared with cosine similarity
|
|
||||||
impl PartialEq for FeatureResult {
|
impl PartialEq for FeatureResult {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
|
@ -62,6 +90,14 @@ impl PartialEq for FeatureResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///in this trait we compare the types to get the similarity between them where 1 is identical and 0 is completly different
|
||||||
|
///
|
||||||
|
/// the Vec type compares each member recursive.
|
||||||
|
/// the Rgba type returns the Delta E similarity of the Colors
|
||||||
|
/// the Indices type is compared with the cosines similarity
|
||||||
|
/// the Percent type returns the 1 - difference
|
||||||
|
///
|
||||||
|
///
|
||||||
impl WeightedCmp for FeatureResult {
|
impl WeightedCmp for FeatureResult {
|
||||||
fn weighted(&self, other: &Self) -> f32 {
|
fn weighted(&self, other: &Self) -> f32 {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
|
@ -97,18 +133,18 @@ impl WeightedCmp for FeatureResult {
|
||||||
0.
|
0.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Self::Rgba(l0, l1, l2, _), Self::Rgba(r0, r1, r2,_)) => {
|
(Self::Rgba(l0, l1, l2, _), Self::Rgba(r0, r1, r2, _)) => {
|
||||||
let lableft = rgb_to_lab(vec![*l0,*l1,*l2]);
|
let lableft = rgb_to_lab(vec![*l0, *l1, *l2]);
|
||||||
let labright = rgb_to_lab(vec![*r0,*r1,*r2]);
|
let labright = rgb_to_lab(vec![*r0, *r1, *r2]);
|
||||||
|
|
||||||
let mut result = ((lableft[0]-labright[0])*(lableft[0]-labright[0])
|
let mut result = ((lableft[0] - labright[0]) * (lableft[0] - labright[0])
|
||||||
+(lableft[1]-labright[1])*(lableft[1]-labright[1])
|
+ (lableft[1] - labright[1]) * (lableft[1] - labright[1])
|
||||||
+(lableft[2]-labright[2])*(lableft[2]-labright[2])).sqrt(); //euclidian distance between two colors: Delta E
|
+ (lableft[2] - labright[2]) * (lableft[2] - labright[2]))
|
||||||
|
.sqrt(); //euclidian distance between two colors: Delta E
|
||||||
if result > 100. {
|
if result > 100. {
|
||||||
result = 0.;
|
result = 0.;
|
||||||
}
|
} else {
|
||||||
else {
|
result = 1. - result / 100.;
|
||||||
result = 1. - result/100.;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
|
@ -117,15 +153,13 @@ impl WeightedCmp for FeatureResult {
|
||||||
let mut up = 0_u64;
|
let mut up = 0_u64;
|
||||||
let mut left = 0_u64;
|
let mut left = 0_u64;
|
||||||
let mut right = 0_u64;
|
let mut right = 0_u64;
|
||||||
for (a,b) in l.iter().zip(r.iter()).map(|(a, b)| (a,b)){
|
for (a, b) in l.iter().zip(r.iter()).map(|(a, b)| (a, b)) {
|
||||||
left += a*a;
|
left += a * a;
|
||||||
right += b*b;
|
right += b * b;
|
||||||
up += a*b;
|
up += a * b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let mut result = up as f32 / ((left * right) as f32).sqrt();//cosines similarity
|
|
||||||
|
|
||||||
|
let mut result = up as f32 / ((left * right) as f32).sqrt(); //cosines similarity
|
||||||
|
|
||||||
if result.is_nan() {
|
if result.is_nan() {
|
||||||
if left == right {
|
if left == right {
|
||||||
|
@ -158,14 +192,27 @@ impl WeightedCmp for FeatureResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///this function transforms rgb values to lab values
|
||||||
fn rgb_to_lab(rgb: Vec<f32>) -> [f32; 3] {
|
fn rgb_to_lab(rgb: Vec<f32>) -> [f32; 3] {
|
||||||
let r = rgb[0] / 255.0;
|
let r = rgb[0] / 255.0;
|
||||||
let g = rgb[1] / 255.0;
|
let g = rgb[1] / 255.0;
|
||||||
let b = rgb[2] / 255.0;
|
let b = rgb[2] / 255.0;
|
||||||
|
|
||||||
let r = if r > 0.04045 { ((r + 0.055) / 1.055).powf(2.4) } else { r / 12.92 };
|
let r = if r > 0.04045 {
|
||||||
let g = if g > 0.04045 { ((g + 0.055) / 1.055).powf(2.4) } else { g / 12.92 };
|
((r + 0.055) / 1.055).powf(2.4)
|
||||||
let b = if b > 0.04045 { ((b + 0.055) / 1.055).powf(2.4) } else { b / 12.92 };
|
} else {
|
||||||
|
r / 12.92
|
||||||
|
};
|
||||||
|
let g = if g > 0.04045 {
|
||||||
|
((g + 0.055) / 1.055).powf(2.4)
|
||||||
|
} else {
|
||||||
|
g / 12.92
|
||||||
|
};
|
||||||
|
let b = if b > 0.04045 {
|
||||||
|
((b + 0.055) / 1.055).powf(2.4)
|
||||||
|
} else {
|
||||||
|
b / 12.92
|
||||||
|
};
|
||||||
|
|
||||||
let x = r * 0.4124 + g * 0.3576 + b * 0.1805;
|
let x = r * 0.4124 + g * 0.3576 + b * 0.1805;
|
||||||
let y = r * 0.2126 + g * 0.7152 + b * 0.0722;
|
let y = r * 0.2126 + g * 0.7152 + b * 0.0722;
|
||||||
|
@ -175,9 +222,21 @@ fn rgb_to_lab(rgb: Vec<f32>) -> [f32; 3] {
|
||||||
let y = y / 1.0;
|
let y = y / 1.0;
|
||||||
let z = z / 1.08883;
|
let z = z / 1.08883;
|
||||||
|
|
||||||
let x = if x > 0.008856 { x.powf(1.0 / 3.0) } else { (7.787 * x) + (16.0 / 116.0) };
|
let x = if x > 0.008856 {
|
||||||
let y = if y > 0.008856 { y.powf(1.0 / 3.0) } else { (7.787 * y) + (16.0 / 116.0) };
|
x.powf(1.0 / 3.0)
|
||||||
let z = if z > 0.008856 { z.powf(1.0 / 3.0) } else { (7.787 * z) + (16.0 / 116.0) };
|
} else {
|
||||||
|
(7.787 * x) + (16.0 / 116.0)
|
||||||
|
};
|
||||||
|
let y = if y > 0.008856 {
|
||||||
|
y.powf(1.0 / 3.0)
|
||||||
|
} else {
|
||||||
|
(7.787 * y) + (16.0 / 116.0)
|
||||||
|
};
|
||||||
|
let z = if z > 0.008856 {
|
||||||
|
z.powf(1.0 / 3.0)
|
||||||
|
} else {
|
||||||
|
(7.787 * z) + (16.0 / 116.0)
|
||||||
|
};
|
||||||
|
|
||||||
let l = (116.0 * y) - 16.0;
|
let l = (116.0 * y) - 16.0;
|
||||||
let a = 500.0 * (x - y);
|
let a = 500.0 * (x - y);
|
||||||
|
@ -186,9 +245,11 @@ fn rgb_to_lab(rgb: Vec<f32>) -> [f32; 3] {
|
||||||
[l, a, b]
|
[l, a, b]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub type FeatureGenerator = fn(Arc<Image<f32>>) -> (String, FeatureResult);
|
pub type FeatureGenerator = fn(Arc<Image<f32>>) -> (String, FeatureResult);
|
||||||
|
|
||||||
|
///The Database stores the images with the feature generators.
|
||||||
|
///It also stores the threadpool
|
||||||
|
///the images of the Database can get serialized using Serde_Json. the complete Database cant get serialized
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Database {
|
pub struct Database {
|
||||||
images: IndexedImages,
|
images: IndexedImages,
|
||||||
|
@ -200,6 +261,9 @@ pub struct Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Database {
|
impl Database {
|
||||||
|
///This function search the Database after the Similarity to a given Image in a specific feature.
|
||||||
|
/// It returns a Vector of all images and a f32 value which represents the Similarity in percent.
|
||||||
|
///
|
||||||
pub fn search(&self, imagepath: &Path, feature: FeatureGenerator) -> Vec<(PathBuf, f32)> {
|
pub fn search(&self, imagepath: &Path, feature: FeatureGenerator) -> Vec<(PathBuf, f32)> {
|
||||||
self.images.search(imagepath, feature)
|
self.images.search(imagepath, feature)
|
||||||
}
|
}
|
||||||
|
@ -215,7 +279,7 @@ impl Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// with add_image you can add images in a existing database.
|
/// with add_image you can add images in a existing database.
|
||||||
/// databases from a file are read only
|
/// databases from a file are read only.
|
||||||
pub fn add_image(&mut self, path: &Path) {
|
pub fn add_image(&mut self, path: &Path) {
|
||||||
if !self.generators.is_empty() {
|
if !self.generators.is_empty() {
|
||||||
self.images
|
self.images
|
||||||
|
@ -225,6 +289,7 @@ impl Database {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// with from_file you can generate a Database out of a given path to a serialized database
|
||||||
pub fn from_file(path: &Path) -> Self {
|
pub fn from_file(path: &Path) -> Self {
|
||||||
let filestring = fs::read_to_string(path).expect("can't read that file");
|
let filestring = fs::read_to_string(path).expect("can't read that file");
|
||||||
let images = serde_json::from_str::<IndexedImages>(&filestring)
|
let images = serde_json::from_str::<IndexedImages>(&filestring)
|
||||||
|
@ -237,12 +302,15 @@ impl Database {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///IndexedImages stores the images of the Database and is serializable
|
||||||
#[derive(Serialize, Deserialize, Default, PartialEq, Debug)]
|
#[derive(Serialize, Deserialize, Default, PartialEq, Debug)]
|
||||||
struct IndexedImages {
|
struct IndexedImages {
|
||||||
images: HashMap<PathBuf, HashMap<String, FeatureResult>>,
|
images: HashMap<PathBuf, HashMap<String, FeatureResult>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexedImages {
|
impl IndexedImages {
|
||||||
|
///the new function generates all images and generates every feature so it can store these.
|
||||||
|
|
||||||
fn new(
|
fn new(
|
||||||
imagepaths: &Vec<PathBuf>,
|
imagepaths: &Vec<PathBuf>,
|
||||||
features: &[FeatureGenerator],
|
features: &[FeatureGenerator],
|
||||||
|
@ -271,6 +339,9 @@ impl IndexedImages {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///This function search the Database after the Similarity to a given Image in a specific feature.
|
||||||
|
/// It returns a Vector of all images and a f32 value which represents the Similarity in percent.
|
||||||
|
///
|
||||||
fn search(&self, imagepath: &Path, feature: FeatureGenerator) -> Vec<(PathBuf, f32)> {
|
fn search(&self, imagepath: &Path, feature: FeatureGenerator) -> Vec<(PathBuf, f32)> {
|
||||||
let image: Arc<Image<f32>> = Arc::new(Image::default()); //todo!("Image reader function")
|
let image: Arc<Image<f32>> = Arc::new(Image::default()); //todo!("Image reader function")
|
||||||
let search_feat = feature(image);
|
let search_feat = feature(image);
|
||||||
|
@ -286,6 +357,7 @@ impl IndexedImages {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///this function lets you add images to the Indexed Image struct
|
||||||
fn add_image(
|
fn add_image(
|
||||||
&mut self,
|
&mut self,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
|
@ -317,69 +389,71 @@ fn average_luminance(image: Arc<Image<f32>>) -> (String, FeatureResult) {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
///this function tests the Serialization of the Database
|
||||||
#[test]
|
#[test]
|
||||||
fn conversion() {
|
fn conversion() {
|
||||||
|
|
||||||
let mut images: HashMap<PathBuf, HashMap<String, FeatureResult>> = HashMap::new();
|
let mut images: HashMap<PathBuf, HashMap<String, FeatureResult>> = HashMap::new();
|
||||||
let mut feat: HashMap<String, FeatureResult> = HashMap::new();
|
let mut feat: HashMap<String, FeatureResult> = HashMap::new();
|
||||||
feat.insert(String::from("average-brightness"), FeatureResult::F32(0.0));
|
feat.insert(String::from("average-brightness"), FeatureResult::F32(0.0));
|
||||||
images.insert(PathBuf::new(), feat);
|
images.insert(PathBuf::new(), feat);
|
||||||
let data = IndexedImages { images };
|
let data = IndexedImages { images };
|
||||||
|
|
||||||
let _as_json = serde_json::to_string(&data).expect("couldnt convert");
|
let _as_json = serde_json::to_string(&data).expect("couldnt convert");
|
||||||
println!("{:?}", _as_json);
|
println!("{:?}", _as_json);
|
||||||
let data_after_conversion = serde_json::from_str::<IndexedImages>(&_as_json).expect("couldnt convert from string");
|
let data_after_conversion =
|
||||||
|
serde_json::from_str::<IndexedImages>(&_as_json).expect("couldnt convert from string");
|
||||||
assert_eq!(data, data_after_conversion);
|
assert_eq!(data, data_after_conversion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///this function tests Edgecases for the cosine_similarity in the weightet function
|
||||||
#[test]
|
#[test]
|
||||||
fn cosine_similarity(){
|
fn cosine_similarity() {
|
||||||
let vec1 = FeatureResult::Indices(vec!{1, 3, 4});
|
let vec1 = FeatureResult::Indices(vec![1, 3, 4]);
|
||||||
let vec2 = FeatureResult::Indices(vec!{1, 3, 4});
|
let vec2 = FeatureResult::Indices(vec![1, 3, 4]);
|
||||||
|
|
||||||
|
|
||||||
assert_eq!(1., vec1.weighted(&vec2)); // both are identical
|
assert_eq!(1., vec1.weighted(&vec2)); // both are identical
|
||||||
let vec2 = FeatureResult::Indices(vec!{0, 0, 0});
|
let vec2 = FeatureResult::Indices(vec![0, 0, 0]);
|
||||||
assert_eq!(0., vec1.weighted(&vec2)); // one is 0
|
assert_eq!(0., vec1.weighted(&vec2)); // one is 0
|
||||||
let vec1 = FeatureResult::Indices(vec!{0, 0, 0});
|
let vec1 = FeatureResult::Indices(vec![0, 0, 0]);
|
||||||
assert_eq!(1., vec1.weighted(&vec2)); // both are 0
|
assert_eq!(1., vec1.weighted(&vec2)); // both are 0
|
||||||
|
|
||||||
assert_eq!(1., vec2.weighted(&vec1)); // it shouldn't change if the Values are switched
|
assert_eq!(1., vec2.weighted(&vec1)); // it shouldn't change if the Values are switched
|
||||||
|
|
||||||
let vec1 = FeatureResult::Indices(vec!{7, 3, 4});
|
let vec1 = FeatureResult::Indices(vec![7, 3, 4]);
|
||||||
let vec2 = FeatureResult::Indices(vec!{1, 5, 2});
|
let vec2 = FeatureResult::Indices(vec![1, 5, 2]);
|
||||||
assert_eq!(vec1.weighted(&vec2), vec2.weighted(&vec1));
|
assert_eq!(vec1.weighted(&vec2), vec2.weighted(&vec1));
|
||||||
println!("{:?}", vec1.weighted(&vec2));
|
println!("{:?}", vec1.weighted(&vec2));
|
||||||
|
|
||||||
|
let mut vec1 = vec![5; 9999];
|
||||||
let mut vec1 = vec![5;9999];
|
vec1.push(1);
|
||||||
vec1.push( 1);
|
let vec1 = FeatureResult::Indices(vec1);
|
||||||
let vec1 = FeatureResult::Indices(vec1);
|
let vec2 = FeatureResult::Indices(vec![7; 10000]);
|
||||||
let vec2 = FeatureResult::Indices(vec!{7;10000});
|
|
||||||
println!("{:?}", vec1.weighted(&vec2));
|
println!("{:?}", vec1.weighted(&vec2));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
///this function tests all of the weighted function
|
||||||
#[test]
|
#[test]
|
||||||
fn weighted() {
|
fn weighted() {
|
||||||
|
let vec1 = FeatureResult::Vec(vec![
|
||||||
|
FeatureResult::Bool(true),
|
||||||
|
FeatureResult::Char('c'),
|
||||||
|
FeatureResult::Vec(vec![FeatureResult::Percent(0.5)]),
|
||||||
|
FeatureResult::F32(44.543),
|
||||||
|
]);
|
||||||
|
|
||||||
let vec1 = FeatureResult::Vec(vec![FeatureResult::Bool(true),
|
let vec2 = FeatureResult::Vec(vec![
|
||||||
FeatureResult::Char('c'),
|
FeatureResult::Bool(true),
|
||||||
FeatureResult::Vec(vec![FeatureResult::Percent(0.5)]),
|
FeatureResult::Char('c'),
|
||||||
FeatureResult::F32(44.543) ]);
|
FeatureResult::Vec(vec![FeatureResult::Percent(0.5)]),
|
||||||
|
FeatureResult::F32(44.543),
|
||||||
|
]);
|
||||||
|
assert_eq!(1., vec2.weighted(&vec1));
|
||||||
|
|
||||||
let vec2 = FeatureResult::Vec(vec![FeatureResult::Bool(true),
|
let vec2 = FeatureResult::Vec(vec![
|
||||||
FeatureResult::Char('c'),
|
FeatureResult::Bool(true),
|
||||||
FeatureResult::Vec(vec![FeatureResult::Percent(0.5)]),
|
FeatureResult::Char('c'),
|
||||||
FeatureResult::F32(44.543) ]);
|
FeatureResult::F32(44.543),
|
||||||
assert_eq!(1., vec2.weighted(&vec1));
|
FeatureResult::Vec(vec![FeatureResult::Percent(0.5)]),
|
||||||
|
]);
|
||||||
|
|
||||||
let vec2 = FeatureResult::Vec(vec![FeatureResult::Bool(true),
|
|
||||||
FeatureResult::Char('c'),
|
|
||||||
FeatureResult::F32(44.543) ,
|
|
||||||
FeatureResult::Vec(vec![FeatureResult::Percent(0.5)])]);
|
|
||||||
assert_eq!(0.5, vec2.weighted(&vec1));
|
assert_eq!(0.5, vec2.weighted(&vec1));
|
||||||
println!("{:?}", vec1.weighted(&vec2));
|
println!("{:?}", vec1.weighted(&vec2));
|
||||||
|
|
||||||
|
@ -395,43 +469,35 @@ mod tests {
|
||||||
let value2 = FeatureResult::String(String::from("notTesting"));
|
let value2 = FeatureResult::String(String::from("notTesting"));
|
||||||
assert_eq!(0., value1.weighted(&value2));
|
assert_eq!(0., value1.weighted(&value2));
|
||||||
|
|
||||||
|
|
||||||
let value2 = FeatureResult::String(String::from("Testing"));
|
let value2 = FeatureResult::String(String::from("Testing"));
|
||||||
assert_eq!(1., value1.weighted(&value2)) ;
|
assert_eq!(1., value1.weighted(&value2));
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
///this test is for the rgba values in the weighted function
|
||||||
#[test]
|
#[test]
|
||||||
fn weighted_rgba() {
|
fn weighted_rgba() {
|
||||||
let value1 = FeatureResult::Rgba(32.6754,42.432,43.87,255.);
|
let value1 = FeatureResult::Rgba(32.6754, 42.432, 43.87, 255.);
|
||||||
let value2 = FeatureResult::Rgba(32.6754,42.432,43.87,255.);
|
let value2 = FeatureResult::Rgba(32.6754, 42.432, 43.87, 255.);
|
||||||
assert_eq!(1., value1.weighted(&value2)) ;
|
assert_eq!(1., value1.weighted(&value2));
|
||||||
|
|
||||||
let value1 = FeatureResult::Rgba(255.,255.,0.,255.);
|
let value1 = FeatureResult::Rgba(255., 255., 0., 255.);
|
||||||
let value2 = FeatureResult::Rgba(0.,0.,0.,255.);
|
let value2 = FeatureResult::Rgba(0., 0., 0., 255.);
|
||||||
//assert_eq!(1., value1.weighted(&value2)) ;
|
//assert_eq!(1., value1.weighted(&value2)) ;
|
||||||
println!("Yellow to Black: {:?}", value1.weighted(&value2));
|
println!("Yellow to Black: {:?}", value1.weighted(&value2));
|
||||||
|
|
||||||
let value1 = FeatureResult::Rgba(255.,255.,0.,255.);
|
let value1 = FeatureResult::Rgba(255., 255., 0., 255.);
|
||||||
let value2 = FeatureResult::Rgba(200.,255.,55.,255.);
|
let value2 = FeatureResult::Rgba(200., 255., 55., 255.);
|
||||||
//assert_eq!(1., value1.weighted(&value2)) ;
|
//assert_eq!(1., value1.weighted(&value2)) ;
|
||||||
println!("yellow to light green: {:?}", value1.weighted(&value2));
|
println!("yellow to light green: {:?}", value1.weighted(&value2));
|
||||||
|
|
||||||
let value1 = FeatureResult::Rgba(3.,8.,255.,255.);
|
let value1 = FeatureResult::Rgba(3., 8., 255., 255.);
|
||||||
let value2 = FeatureResult::Rgba(3.,106.,255.,255.);
|
let value2 = FeatureResult::Rgba(3., 106., 255., 255.);
|
||||||
//assert_eq!(1., value1.weighted(&value2)) ;
|
//assert_eq!(1., value1.weighted(&value2)) ;
|
||||||
println!("blue to dark blue: {:?}", value1.weighted(&value2));
|
println!("blue to dark blue: {:?}", value1.weighted(&value2));
|
||||||
|
|
||||||
let value1 = FeatureResult::Rgba(255.,106.,122.,255.);
|
let value1 = FeatureResult::Rgba(255., 106., 122., 255.);
|
||||||
let value2 = FeatureResult::Rgba(255.,1.,28.,255.);
|
let value2 = FeatureResult::Rgba(255., 1., 28., 255.);
|
||||||
//assert_eq!(1., value1.weighted(&value2)) ;
|
//assert_eq!(1., value1.weighted(&value2)) ;
|
||||||
println!("Red to light red: {:?}", value1.weighted(&value2));
|
println!("Red to light red: {:?}", value1.weighted(&value2));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue