Commit 80063fd4 authored by Maksim Shabunin's avatar Maksim Shabunin

Merge pull request #736 from sbokov:color_constancy

parents 159534a2 5fbc7daf
...@@ -6,3 +6,11 @@ ...@@ -6,3 +6,11 @@
year={2012}, year={2012},
publisher={Springer} publisher={Springer}
} }
@inproceedings{Cheng2015,
title={Effective learning-based illuminant estimation using simple features},
author={Cheng, Dongliang and Price, Brian and Cohen, Scott and Brown, Michael S},
booktitle={Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition},
pages={1000--1008},
year={2015}
}
...@@ -105,7 +105,7 @@ namespace xphoto ...@@ -105,7 +105,7 @@ namespace xphoto
threshold of 0 means no pixels are used. Lower thresholds are useful in threshold of 0 means no pixels are used. Lower thresholds are useful in
white-balancing saturated images. white-balancing saturated images.
Currently only works on images of type @ref CV_8UC3. Currently only works on images of type @ref CV_8UC3 and @ref CV_16UC3.
@param src Input array. @param src Input array.
@param dst Output array of the same size and type as src. @param dst Output array of the same size and type as src.
...@@ -117,7 +117,69 @@ namespace xphoto ...@@ -117,7 +117,69 @@ namespace xphoto
CV_EXPORTS_W void autowbGrayworld(InputArray src, OutputArray dst, CV_EXPORTS_W void autowbGrayworld(InputArray src, OutputArray dst,
float thresh = 0.5f); float thresh = 0.5f);
//! @} /** @brief Implements a more sophisticated learning-based automatic color balance algorithm.
As autowbGrayworld, this function works by applying different gains to the input
image channels, but their computation is a bit more involved compared to the
simple grayworld assumption. More details about the algorithm can be found in
@cite Cheng2015 .
To mask out saturated pixels this function uses only pixels that satisfy the
following condition:
\f[ \frac{\textrm{max}(R,G,B)}{\texttt{range_max_val}} < \texttt{saturation_thresh} \f]
Currently supports images of type @ref CV_8UC3 and @ref CV_16UC3.
@param src Input three-channel image in the BGR color space.
@param dst Output image of the same size and type as src.
@param range_max_val Maximum possible value of the input image (e.g. 255 for 8 bit images, 4095 for 12 bit images)
@param saturation_thresh Threshold that is used to determine saturated pixels
@param hist_bin_num Defines the size of one dimension of a three-dimensional RGB histogram that is used internally by
the algorithm. It often makes sense to increase the number of bins for images with higher bit depth (e.g. 256 bins
for a 12 bit image)
@sa autowbGrayworld
*/
CV_EXPORTS_W void autowbLearningBased(InputArray src, OutputArray dst, int range_max_val = 255,
float saturation_thresh = 0.98f, int hist_bin_num = 64);
/** @brief Implements the feature extraction part of the learning-based color balance algorithm.
In accordance with @cite Cheng2015 , computes the following features for the input image:
1. Chromaticity of an average (R,G,B) tuple
2. Chromaticity of the brightest (R,G,B) tuple (while ignoring saturated pixels)
3. Chromaticity of the dominant (R,G,B) tuple (the one that has the highest value in the RGB histogram)
4. Mode of the chromaticity pallete, that is constructed by taking 300 most common colors according to
the RGB histogram and projecting them on the chromaticity plane. Mode is the most high-density point
of the pallete, which is computed by a straightforward fixed-bandwidth kernel density estimator with
a Epanechnikov kernel function.
@param src Input three-channel image in the BGR color space.
@param dst An array of four (r,g) chromaticity tuples corresponding to the features listed above.
@param range_max_val Maximum possible value of the input image (e.g. 255 for 8 bit images, 4095 for 12 bit images)
@param saturation_thresh Threshold that is used to determine saturated pixels
@param hist_bin_num Defines the size of one dimension of a three-dimensional RGB histogram that is used internally by
the algorithm. It often makes sense to increase the number of bins for images with higher bit depth (e.g. 256 bins
for a 12 bit image)
@sa autowbLearningBased
*/
CV_EXPORTS_W void extractSimpleFeatures(InputArray src, OutputArray dst, int range_max_val = 255,
float saturation_thresh = 0.98f, int hist_bin_num = 64);
/** @brief Implements an efficient fixed-point approximation for applying channel gains.
@param src Input three-channel image in the BGR color space (either CV_8UC3 or CV_16UC3)
@param dst Output image of the same size and type as src.
@param gainB gain for the B channel
@param gainG gain for the G channel
@param gainR gain for the R channel
@sa autowbGrayworld, autowbLearningBased
*/
CV_EXPORTS_W void applyChannelGains(InputArray src, OutputArray dst, float gainB, float gainG, float gainR);
//! @}
} }
} }
......
/*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
*
* License Agreement
* For Open Source Computer Vision Library
* (3 - clause BSD License)
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met :
*
* *Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution.
*
* * Neither the names of the copyright holders nor the names of the contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* This software is provided by the copyright holders and contributors "as is" and
* any express or implied warranties, including, but not limited to, the implied
* warranties of merchantability and fitness for a particular purpose are disclaimed.
* In no event shall copyright holders or contributors be liable for any direct,
* indirect, incidental, special, exemplary, or consequential damages
* (including, but not limited to, procurement of substitute goods or services;
* loss of use, data, or profits; or business interruption) however caused
* and on any theory of liability, whether in contract, strict liability,
* or tort(including negligence or otherwise) arising in any way out of
* the use of this software, even if advised of the possibility of such damage.
*/
#include "perf_precomp.hpp"
#include "opencv2/imgproc.hpp"
using std::tr1::tuple;
using std::tr1::get;
using namespace std;
using namespace cv;
using namespace perf;
using namespace testing;
typedef tuple<Size, MatType> learningBasedWBParams;
typedef TestBaseWithParam<learningBasedWBParams> learningBasedWBPerfTest;
PERF_TEST_P(learningBasedWBPerfTest, perf, Combine(SZ_ALL_HD, Values(CV_8UC3, CV_16UC3)))
{
Size size = get<0>(GetParam());
MatType t = get<1>(GetParam());
Mat src(size, t);
Mat dst(size, t);
int range_max_val = 255, hist_bin_num = 64;
if (t == CV_16UC3)
{
range_max_val = 65535;
hist_bin_num = 256;
}
Mat src_dscl(Size(size.width / 16, size.height / 16), t);
RNG rng(1234);
rng.fill(src_dscl, RNG::UNIFORM, 0, range_max_val);
resize(src_dscl, src, src.size());
TEST_CYCLE() xphoto::autowbLearningBased(src, dst, range_max_val, 0.98f, hist_bin_num);
SANITY_CHECK_NOTHING();
}
#!/usr/bin/env python
from __future__ import print_function
import os, sys, argparse, json
import numpy as np
import scipy.io
import cv2
import timeit
def load_json(path):
f = open(path, "r")
data = json.load(f)
return data
def save_json(obj, path):
tmp_file = path + ".bak"
f = open(tmp_file, "w")
json.dump(obj, f, indent=2)
f.flush()
os.fsync(f.fileno())
f.close()
try:
os.rename(tmp_file, path)
except:
os.remove(path)
os.rename(tmp_file, path)
def parse_sequence(input_str):
if len(input_str) == 0:
return []
else:
return [o.strip() for o in input_str.split(",") if o]
def stretch_to_8bit(arr, clip_percentile = 2.5):
arr = np.clip(arr * (255.0 / np.percentile(arr, 100 - clip_percentile)), 0, 255)
return arr.astype(np.uint8)
def evaluate(im, algo, gt_illuminant, i, range_thresh, bin_num, dst_folder):
new_im = None
start_time = timeit.default_timer()
if algo=="grayworld":
new_im = cv2.xphoto.autowbGrayworld(im, 0.95)
elif algo=="nothing":
new_im = im
elif algo=="learning_based":
new_im = cv2.xphoto.autowbLearningBased(im, None, range_thresh, 0.98, bin_num)
elif algo=="GT":
gains = gt_illuminant / min(gt_illuminant)
g1 = float(1.0 / gains[2])
g2 = float(1.0 / gains[1])
g3 = float(1.0 / gains[0])
new_im = cv2.xphoto.applyChannelGains(im, g1, g2, g3)
time = 1000*(timeit.default_timer() - start_time) #time in ms
if len(dst_folder)>0:
if not os.path.exists(dst_folder):
os.makedirs(dst_folder)
im_name = ("%04d_" % i) + algo + ".jpg"
cv2.imwrite(os.path.join(dst_folder, im_name), stretch_to_8bit(new_im))
#recover the illuminant from the color balancing result, assuming the standard model:
estimated_illuminant = [0, 0, 0]
eps = 0.01
estimated_illuminant[2] = np.percentile((im[:,:,0] + eps) / (new_im[:,:,0] + eps), 50)
estimated_illuminant[1] = np.percentile((im[:,:,1] + eps) / (new_im[:,:,1] + eps), 50)
estimated_illuminant[0] = np.percentile((im[:,:,2] + eps) / (new_im[:,:,2] + eps), 50)
res = np.arccos(np.dot(gt_illuminant,estimated_illuminant)/
(np.linalg.norm(gt_illuminant) * np.linalg.norm(estimated_illuminant)))
return (time, (res / np.pi) * 180)
def build_html_table(out, state, stat_list, img_range):
stat_dict = {'mean': ('Mean error', lambda arr: np.mean(arr)),
'median': ('Median error',lambda arr: np.percentile(arr, 50)),
'p05': ('5<sup>th</sup> percentile',lambda arr: np.percentile(arr, 5)),
'p20': ('20<sup>th</sup> percentile',lambda arr: np.percentile(arr, 20)),
'p80': ('80<sup>th</sup> percentile',lambda arr: np.percentile(arr, 80)),
'p95': ('95<sup>th</sup> percentile',lambda arr: np.percentile(arr, 95))
}
html_out = ['<style type="text/css">\n',
' html, body {font-family: Lucida Console, Courier New, Courier;font-size: 16px;color:#3e4758;}\n',
' .tbl{background:none repeat scroll 0 0 #FFFFFF;border-collapse:collapse;font-family:"Lucida Sans Unicode","Lucida Grande",Sans-Serif;font-size:14px;margin:20px;text-align:left;width:480px;margin-left: auto;margin-right: auto;white-space:nowrap;}\n',
' .tbl span{display:block;white-space:nowrap;}\n',
' .tbl thead tr:last-child th {padding-bottom:5px;}\n',
' .tbl tbody tr:first-child td {border-top:3px solid #6678B1;}\n',
' .tbl th{border:none;color:#003399;font-size:16px;font-weight:normal;white-space:nowrap;padding:3px 10px;}\n',
' .tbl td{border:none;border-bottom:1px solid #CCCCCC;color:#666699;padding:6px 8px;white-space:nowrap;}\n',
' .tbl tbody tr:hover td{color:#000099;}\n',
' .tbl caption{font:italic 16px "Trebuchet MS",Verdana,Arial,Helvetica,sans-serif;padding:0 0 5px;text-align:right;white-space:normal;}\n',
' .firstingroup {border-top:2px solid #6678B1;}\n',
'</style>\n\n']
html_out += ['<table class="tbl">\n',
' <thead>\n',
' <tr>\n',
' <th align="center" valign="top"> Algorithm Name </th>\n',
' <th align="center" valign="top"> Average Time </th>\n']
for stat in stat_list:
if stat not in stat_dict.keys():
print("Error: unsupported statistic " + stat)
sys.exit(1)
html_out += [' <th align="center" valign="top"> ' +
stat_dict[stat][0] +
' </th>\n']
html_out += [' </tr>\n',
' </thead>\n',
' <tbody>\n']
for algorithm in state.keys():
arr = [state[algorithm][file]["angular_error"] for file in state[algorithm].keys() if file>=img_range[0] and file<=img_range[1]]
average_time = "%.2f ms" % np.mean([state[algorithm][file]["time"] for file in state[algorithm].keys()
if file>=img_range[0] and file<=img_range[1]])
html_out += [' <tr>\n',
' <td>' + algorithm + '</td>\n',
' <td>' + average_time + '</td>\n']
for stat in stat_list:
html_out += [' <td> ' +
"%.2f&deg" % stat_dict[stat][1](arr) +
' </td>\n']
html_out += [' </tr>\n']
html_out += [' </tbody>\n',
'</table>\n']
f = open(out, 'w')
f.writelines(html_out)
f.close()
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description=("A benchmarking script for color balance algorithms"),
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
"-a",
"--algorithms",
metavar="ALGORITHMS",
default="",
help=("Comma-separated list of color balance algorithms to evaluate. "
"Currently available: GT,learning_based,grayworld,nothing."))
parser.add_argument(
"-i",
"--input_folder",
metavar="INPUT_FOLDER",
default="",
help=("Folder containing input images to evaluate on. Assumes minimally "
"processed png images like in the Gehler-Shi (http://www.cs.sfu.ca/~colour/data/shi_gehler/) "
"or NUS 8-camera (http://www.comp.nus.edu.sg/~whitebal/illuminant/illuminant.html) datasets"))
parser.add_argument(
"-g",
"--ground_truth",
metavar="GROUND_TRUTH",
default="real_illum_568..mat",
help=("Path to the mat file containing ground truth illuminations. Currently "
"supports formats supplied by the Gehler-Shi and NUS 8-camera datasets."))
parser.add_argument(
"-o",
"--out",
metavar="OUT",
default="./white_balance_eval_result.html",
help="Path to the output html table")
parser.add_argument(
"-s",
"--state",
metavar="STATE_JSON",
default="./WB_evaluation_state.json",
help=("Path to a json file that stores the current evaluation state"))
parser.add_argument(
"-t",
"--stats",
metavar="STATS",
default="mean,median,p05,p20,p80,p95",
help=("Comma-separated list of error statistics to compute and list "
"in the output table. All the available ones are used by default"))
parser.add_argument(
"-b",
"--input_bit_depth",
metavar="INPUT_BIT_DEPTH",
default="",
help=("Assumed bit depth for input images. Should be specified in order to "
"use full bit depth for evaluation (for instance, -b 12 for 12 bit images). "
"Otherwise, input images are converted to 8 bit prior to the evaluation."))
parser.add_argument(
"-d",
"--dst_folder",
metavar="DST_FOLDER",
default="",
help=("If specified, this folder will be used to store the color correction results"))
parser.add_argument(
"-r",
"--range",
metavar="RANGE",
default="0,0",
help=("Comma-separated range of images from the dataset to evaluate on (for instance: 0,568). "
"All available images are used by default."))
args, other_args = parser.parse_known_args()
if not os.path.exists(args.input_folder):
print("Error: " + args.input_folder + (" does not exist. Please, correctly "
"specify the -i parameter"))
sys.exit(1)
if not os.path.exists(args.ground_truth):
print("Error: " + args.ground_truth + (" does not exist. Please, correctly "
"specify the -g parameter"))
sys.exit(1)
state = {}
if os.path.isfile(args.state):
state = load_json(args.state)
algorithm_list = parse_sequence(args.algorithms)
img_range = map(int, parse_sequence(args.range))
if len(img_range)!=2:
print("Error: Please specify the -r parameter in form <first_image_index>,<last_image_index>")
sys.exit(1)
gt = scipy.io.loadmat(args.ground_truth)
img_files = sorted(os.listdir(args.input_folder))
gt_illuminants = []
black_levels = []
if "groundtruth_illuminants" in gt.keys() and "darkness_level" in gt.keys():
#NUS 8-camera dataset format
gt_illuminants = gt["groundtruth_illuminants"]
black_levels = len(gt_illuminants) * [gt["darkness_level"][0][0]]
elif "real_rgb" in gt.keys():
#Gehler-Shi dataset format
gt_illuminants = gt["real_rgb"]
black_levels = 87 * [0] + (len(gt_illuminants) - 87) * [129]
else:
print("Error: unknown ground-truth format, only formats of Gehler-Shi and NUS 8-camera datasets are supported")
sys.exit(1)
for algorithm in algorithm_list:
i = 0
if algorithm not in state.keys():
state[algorithm] = {}
sz = len(img_files)
for file in img_files:
if file not in state[algorithm].keys() and\
((i>=img_range[0] and i<img_range[1]) or img_range[0]==img_range[1]==0):
cur_path = os.path.join(args.input_folder, file)
im = cv2.imread(cur_path, -1).astype(np.float32)
im -= black_levels[i]
range_thresh = 255
if len(args.input_bit_depth)>0:
range_thresh = 2**int(args.input_bit_depth) - 1
im = np.clip(im, 0, range_thresh).astype(np.uint16)
else:
im = stretch_to_8bit(im)
(time,angular_err) = evaluate(im, algorithm, gt_illuminants[i], i, range_thresh,
256 if range_thresh > 255 else 64, args.dst_folder)
state[algorithm][file] = {"angular_error": angular_err, "time": time}
sys.stdout.write("Algorithm: %-20s Done: [%3d/%3d]\r" % (algorithm, i, sz)),
sys.stdout.flush()
save_json(state, args.state)
i+=1
save_json(state, args.state)
build_html_table(args.out, state, parse_sequence(args.stats), [img_files[img_range[0]], img_files[img_range[1]-1]])
#include "opencv2/xphoto.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
using namespace std;
const char *keys = {"{help h usage ? | | print this message}"
"{i | | input image name }"
"{o | | output image name }"};
int main(int argc, const char **argv)
{
CommandLineParser parser(argc, argv, keys);
parser.about("OpenCV learning-based color balance demonstration sample");
if (parser.has("help") || argc < 2)
{
parser.printMessage();
return 0;
}
string inFilename = parser.get<string>("i");
string outFilename = parser.get<string>("o");
if (!parser.check())
{
parser.printErrors();
return -1;
}
Mat src = imread(inFilename, 1);
if (src.empty())
{
printf("Cannot read image file: %s\n", inFilename.c_str());
return -1;
}
Mat res;
xphoto::autowbLearningBased(src, res);
if (outFilename == "")
{
namedWindow("after white balance", 1);
imshow("after white balance", res);
waitKey(0);
}
else
imwrite(outFilename, res);
return 0;
}
...@@ -37,177 +37,301 @@ ...@@ -37,177 +37,301 @@
// //
//M*/ //M*/
#include "opencv2/xphoto.hpp"
#include "opencv2/core.hpp" #include "opencv2/core.hpp"
#include "opencv2/core/hal/intrin.hpp" #include "opencv2/core/hal/intrin.hpp"
#include "opencv2/xphoto.hpp"
namespace cv
{
namespace xphoto
{
void calculateChannelSums(uint &sumB, uint &sumG, uint &sumR, uchar *src_data, int src_len, float thresh);
void calculateChannelSums(uint64 &sumB, uint64 &sumG, uint64 &sumR, ushort *src_data, int src_len, float thresh);
namespace cv { namespace xphoto { /* Computes sums for each channel, while ignoring saturated pixels which are determined by thresh
* (version for CV_8UC3)
*/
void calculateChannelSums(uint &sumB, uint &sumG, uint &sumR, uchar *src_data, int src_len, float thresh)
{
sumB = sumG = sumR = 0;
ushort thresh255 = (ushort)cvRound(thresh * 255);
int i = 0;
#if CV_SIMD128
v_uint8x16 v_inB, v_inG, v_inR, v_min_val, v_max_val;
v_uint16x8 v_iB1, v_iB2, v_iG1, v_iG2, v_iR1, v_iR2;
v_uint16x8 v_min1, v_min2, v_max1, v_max2, v_m1, v_m2;
v_uint16x8 v_255 = v_setall_u16(255), v_thresh = v_setall_u16(thresh255);
v_uint32x4 v_uint1, v_uint2;
v_uint32x4 v_SB = v_setzero_u32(), v_SG = v_setzero_u32(), v_SR = v_setzero_u32();
void autowbGrayworld(InputArray _src, OutputArray _dst, float thresh) for (; i < src_len - 47; i += 48)
{ {
// Load 3x uint8x16 and deinterleave into vectors of each channel
v_load_deinterleave(&src_data[i], v_inB, v_inG, v_inR);
Mat src = _src.getMat(); // Get min and max
CV_Assert(!src.empty()); v_min_val = v_min(v_inB, v_min(v_inG, v_inR));
CV_Assert(src.isContinuous()); v_max_val = v_max(v_inB, v_max(v_inG, v_inR));
// TODO: Handle CV_8UC1 // Split into two ushort vectors per channel
// TODO: Handle types other than CV_8U v_expand(v_inB, v_iB1, v_iB2);
CV_Assert(src.type() == CV_8UC3); v_expand(v_inG, v_iG1, v_iG2);
v_expand(v_inR, v_iR1, v_iR2);
v_expand(v_min_val, v_min1, v_min2);
v_expand(v_max_val, v_max1, v_max2);
_dst.create(src.size(), src.type()); // Calculate masks
Mat dst = _dst.getMat(); v_m1 = ~((v_max1 - v_min1) * v_255 > v_thresh * v_max1);
CV_Assert(dst.isContinuous()); v_m2 = ~((v_max2 - v_min2) * v_255 > v_thresh * v_max2);
int width = src.cols, // Apply masks
height = src.rows, v_iB1 = (v_iB1 & v_m1) + (v_iB2 & v_m2);
N = width*height, v_iG1 = (v_iG1 & v_m1) + (v_iG2 & v_m2);
N3 = N*3; v_iR1 = (v_iR1 & v_m1) + (v_iR2 & v_m2);
// Calculate sum of pixel values of each channel // Split and add to the sums:
const uchar* src_data = src.ptr<uchar>(0); v_expand(v_iB1, v_uint1, v_uint2);
unsigned long sum1 = 0, sum2 = 0, sum3 = 0; v_SB += v_uint1 + v_uint2;
unsigned int thresh255 = cvRound(thresh * 255); v_expand(v_iG1, v_uint1, v_uint2);
v_SG += v_uint1 + v_uint2;
v_expand(v_iR1, v_uint1, v_uint2);
v_SR += v_uint1 + v_uint2;
}
sumB = v_reduce_sum(v_SB);
sumG = v_reduce_sum(v_SG);
sumR = v_reduce_sum(v_SR);
#endif
unsigned int minRGB, maxRGB;
for (; i < src_len; i += 3)
{
minRGB = min(src_data[i], min(src_data[i + 1], src_data[i + 2]));
maxRGB = max(src_data[i], max(src_data[i + 1], src_data[i + 2]));
if ((maxRGB - minRGB) * 255 > thresh255 * maxRGB)
continue;
sumB += src_data[i];
sumG += src_data[i + 1];
sumR += src_data[i + 2];
}
}
/* Computes sums for each channel, while ignoring saturated pixels which are determined by thresh
* (version for CV_16UC3)
*/
void calculateChannelSums(uint64 &sumB, uint64 &sumG, uint64 &sumR, ushort *src_data, int src_len, float thresh)
{
sumB = sumG = sumR = 0;
uint thresh65535 = cvRound(thresh * 65535);
int i = 0; int i = 0;
#if CV_SIMD128 #if CV_SIMD128
v_uint8x16 v_inB, v_inG, v_inR; v_uint16x8 v_inB, v_inG, v_inR, v_min_val, v_max_val;
v_uint16x8 v_s1, v_s2; v_uint32x4 v_iB1, v_iB2, v_iG1, v_iG2, v_iR1, v_iR2;
v_uint32x4 v_iB1, v_iB2, v_iB3, v_iB4, v_uint32x4 v_min1, v_min2, v_max1, v_max2, v_m1, v_m2;
v_iG1, v_iG2, v_iG3, v_iG4, v_uint32x4 v_65535 = v_setall_u32(65535), v_thresh = v_setall_u32(thresh65535);
v_iR1, v_iR2, v_iR3, v_iR4, v_uint64x2 v_u64_1, v_u64_2;
v_255 = v_setall_u32(255), v_uint64x2 v_SB = v_setzero_u64(), v_SG = v_setzero_u64(), v_SR = v_setzero_u64();
v_thresh = v_setall_u32(thresh255),
v_min1, v_min2, v_min3, v_min4,
v_max1, v_max2, v_max3, v_max4,
v_m1, v_m2, v_m3, v_m4,
v_SB = v_setzero_u32(),
v_SG = v_setzero_u32(),
v_SR = v_setzero_u32();
for ( ; i < N3 - 47; i += 48 )
{
// NOTE: This block assumes BGR channels in naming variables
// Load 3x uint8x16 and deinterleave into vectors of each channel for (; i < src_len - 23; i += 24)
{
// Load 3x uint16x8 and deinterleave into vectors of each channel
v_load_deinterleave(&src_data[i], v_inB, v_inG, v_inR); v_load_deinterleave(&src_data[i], v_inB, v_inG, v_inR);
// Split into four int vectors per channel // Get min and max
v_expand(v_inB, v_s1, v_s2); v_min_val = v_min(v_inB, v_min(v_inG, v_inR));
v_expand(v_s1, v_iB1, v_iB2); v_max_val = v_max(v_inB, v_max(v_inG, v_inR));
v_expand(v_s2, v_iB3, v_iB4);
v_expand(v_inG, v_s1, v_s2); // Split into two uint vectors per channel
v_expand(v_s1, v_iG1, v_iG2); v_expand(v_inB, v_iB1, v_iB2);
v_expand(v_s2, v_iG3, v_iG4); v_expand(v_inG, v_iG1, v_iG2);
v_expand(v_inR, v_iR1, v_iR2);
v_expand(v_inR, v_s1, v_s2); v_expand(v_min_val, v_min1, v_min2);
v_expand(v_s1, v_iR1, v_iR2); v_expand(v_max_val, v_max1, v_max2);
v_expand(v_s2, v_iR3, v_iR4);
// Get mins and maxs
v_min1 = v_min(v_iB1, v_min(v_iG1, v_iR1));
v_min2 = v_min(v_iB2, v_min(v_iG2, v_iR2));
v_min3 = v_min(v_iB3, v_min(v_iG3, v_iR3));
v_min4 = v_min(v_iB4, v_min(v_iG4, v_iR4));
v_max1 = v_max(v_iB1, v_max(v_iG1, v_iR1));
v_max2 = v_max(v_iB2, v_max(v_iG2, v_iR2));
v_max3 = v_max(v_iB3, v_max(v_iG3, v_iR3));
v_max4 = v_max(v_iB4, v_max(v_iG4, v_iR4));
// Calculate masks // Calculate masks
v_m1 = ~((v_max1 - v_min1) * v_255 > v_thresh * v_max1); v_m1 = ~((v_max1 - v_min1) * v_65535 > v_thresh * v_max1);
v_m2 = ~((v_max2 - v_min2) * v_255 > v_thresh * v_max2); v_m2 = ~((v_max2 - v_min2) * v_65535 > v_thresh * v_max2);
v_m3 = ~((v_max3 - v_min3) * v_255 > v_thresh * v_max3);
v_m4 = ~((v_max4 - v_min4) * v_255 > v_thresh * v_max4);
// Apply mask // Apply masks
v_SB += (v_iB1 & v_m1) + (v_iB2 & v_m2) + (v_iB3 & v_m3) + (v_iB4 & v_m4); v_iB1 = (v_iB1 & v_m1) + (v_iB2 & v_m2);
v_SG += (v_iG1 & v_m1) + (v_iG2 & v_m2) + (v_iG3 & v_m3) + (v_iG4 & v_m4); v_iG1 = (v_iG1 & v_m1) + (v_iG2 & v_m2);
v_SR += (v_iR1 & v_m1) + (v_iR2 & v_m2) + (v_iR3 & v_m3) + (v_iR4 & v_m4); v_iR1 = (v_iR1 & v_m1) + (v_iR2 & v_m2);
// Split and add to the sums:
v_expand(v_iB1, v_u64_1, v_u64_2);
v_SB += v_u64_1 + v_u64_2;
v_expand(v_iG1, v_u64_1, v_u64_2);
v_SG += v_u64_1 + v_u64_2;
v_expand(v_iR1, v_u64_1, v_u64_2);
v_SR += v_u64_1 + v_u64_2;
} }
// Perform final reduction // Perform final reduction
sum1 = v_reduce_sum(v_SB); uint64 sum_arr[2];
sum2 = v_reduce_sum(v_SG); v_store(sum_arr, v_SB);
sum3 = v_reduce_sum(v_SR); sumB = sum_arr[0] + sum_arr[1];
v_store(sum_arr, v_SG);
sumG = sum_arr[0] + sum_arr[1];
v_store(sum_arr, v_SR);
sumR = sum_arr[0] + sum_arr[1];
#endif #endif
unsigned int minRGB, maxRGB; unsigned int minRGB, maxRGB;
for ( ; i < N3; i += 3 ) for (; i < src_len; i += 3)
{ {
minRGB = min(src_data[i], min(src_data[i + 1], src_data[i + 2])); minRGB = min(src_data[i], min(src_data[i + 1], src_data[i + 2]));
maxRGB = max(src_data[i], max(src_data[i + 1], src_data[i + 2])); maxRGB = max(src_data[i], max(src_data[i + 1], src_data[i + 2]));
if ( (maxRGB - minRGB) * 255 > thresh255 * maxRGB ) continue; if ((maxRGB - minRGB) * 65535 > thresh65535 * maxRGB)
sum1 += src_data[i]; continue;
sum2 += src_data[i + 1]; sumB += src_data[i];
sum3 += src_data[i + 2]; sumG += src_data[i + 1];
sumR += src_data[i + 2];
} }
}
// Find inverse of averages void applyChannelGains(InputArray _src, OutputArray _dst, float gainB, float gainG, float gainR)
double dinv1 = sum1 == 0 ? 0.f : (double)N / (double)sum1, {
dinv2 = sum2 == 0 ? 0.f : (double)N / (double)sum2, Mat src = _src.getMat();
dinv3 = sum3 == 0 ? 0.f : (double)N / (double)sum3; CV_Assert(!src.empty());
CV_Assert(src.isContinuous());
// Find maximum CV_Assert(src.type() == CV_8UC3 || src.type() == CV_16UC3);
double inv_max = max(dinv1, max(dinv2, dinv3));
// Convert to floats _dst.create(src.size(), src.type());
float inv1 = (float) dinv1, Mat dst = _dst.getMat();
inv2 = (float) dinv2, int N3 = 3 * src.cols * src.rows;
inv3 = (float) dinv3; int i = 0;
// Scale by maximum // Scale gains by their maximum (fixed point approximation works only when all gains are <=1)
if ( inv_max > 0 ) float gain_max = max(gainB, max(gainG, gainR));
if (gain_max > 0)
{ {
inv1 = (float)((double)inv1 / inv_max); gainB /= gain_max;
inv2 = (float)((double)inv2 / inv_max); gainG /= gain_max;
inv3 = (float)((double)inv3 / inv_max); gainR /= gain_max;
} }
if (src.type() == CV_8UC3)
{
// Fixed point arithmetic, mul by 2^8 then shift back 8 bits // Fixed point arithmetic, mul by 2^8 then shift back 8 bits
int i_inv1 = cvRound(inv1 * (1 << 8)), int i_gainB = cvRound(gainB * (1 << 8)), i_gainG = cvRound(gainG * (1 << 8)),
i_inv2 = cvRound(inv2 * (1 << 8)), i_gainR = cvRound(gainR * (1 << 8));
i_inv3 = cvRound(inv3 * (1 << 8)); const uchar *src_data = src.ptr<uchar>();
uchar *dst_data = dst.ptr<uchar>();
// Scale input pixel values
uchar* dst_data = dst.ptr<uchar>(0);
i = 0;
#if CV_SIMD128 #if CV_SIMD128
v_uint8x16 v_inB, v_inG, v_inR;
v_uint8x16 v_outB, v_outG, v_outR; v_uint8x16 v_outB, v_outG, v_outR;
v_uint16x8 v_sB1, v_sB2, v_sG1, v_sG2, v_sR1, v_sR2, v_uint16x8 v_sB1, v_sB2, v_sG1, v_sG2, v_sR1, v_sR2;
v_invB = v_setall_u16((unsigned short) i_inv1), v_uint16x8 v_gainB = v_setall_u16((ushort)i_gainB), v_gainG = v_setall_u16((ushort)i_gainG),
v_invG = v_setall_u16((unsigned short) i_inv2), v_gainR = v_setall_u16((ushort)i_gainR);
v_invR = v_setall_u16((unsigned short) i_inv3);
for ( ; i < N3 - 47; i += 48 ) for (; i < N3 - 47; i += 48)
{ {
// Load 16 x 8bit uchars // Load 3x uint8x16 and deinterleave into vectors of each channel
v_load_deinterleave(&src_data[i], v_inB, v_inG, v_inR); v_load_deinterleave(&src_data[i], v_inB, v_inG, v_inR);
// Split into four int vectors per channel // Split into two ushort vectors per channel
v_expand(v_inB, v_sB1, v_sB2); v_expand(v_inB, v_sB1, v_sB2);
v_expand(v_inG, v_sG1, v_sG2); v_expand(v_inG, v_sG1, v_sG2);
v_expand(v_inR, v_sR1, v_sR2); v_expand(v_inR, v_sR1, v_sR2);
// Multiply by scaling factors // Multiply by gains
v_sB1 = (v_sB1 * v_invB) >> 8; v_sB1 = (v_sB1 * v_gainB) >> 8;
v_sB2 = (v_sB2 * v_invB) >> 8; v_sB2 = (v_sB2 * v_gainB) >> 8;
v_sG1 = (v_sG1 * v_invG) >> 8; v_sG1 = (v_sG1 * v_gainG) >> 8;
v_sG2 = (v_sG2 * v_invG) >> 8; v_sG2 = (v_sG2 * v_gainG) >> 8;
v_sR1 = (v_sR1 * v_invR) >> 8; v_sR1 = (v_sR1 * v_gainR) >> 8;
v_sR2 = (v_sR2 * v_invR) >> 8; v_sR2 = (v_sR2 * v_gainR) >> 8;
// Pack into vectors of v_uint8x16 // Pack into vectors of v_uint8x16
v_store_interleave(&dst_data[i], v_pack(v_sB1, v_sB2), v_store_interleave(&dst_data[i], v_pack(v_sB1, v_sB2), v_pack(v_sG1, v_sG2), v_pack(v_sR1, v_sR2));
v_pack(v_sG1, v_sG2), v_pack(v_sR1, v_sR2));
} }
#endif #endif
for ( ; i < N3; i += 3 ) for (; i < N3; i += 3)
{ {
dst_data[i] = (uchar)((src_data[i] * i_inv1) >> 8); dst_data[i] = (uchar)((src_data[i] * i_gainB) >> 8);
dst_data[i + 1] = (uchar)((src_data[i + 1] * i_inv2) >> 8); dst_data[i + 1] = (uchar)((src_data[i + 1] * i_gainG) >> 8);
dst_data[i + 2] = (uchar)((src_data[i + 2] * i_inv3) >> 8); dst_data[i + 2] = (uchar)((src_data[i + 2] * i_gainR) >> 8);
} }
} }
else if (src.type() == CV_16UC3)
{
// Fixed point arithmetic, mul by 2^16 then shift back 16 bits
int i_gainB = cvRound(gainB * (1 << 16)), i_gainG = cvRound(gainG * (1 << 16)),
i_gainR = cvRound(gainR * (1 << 16));
const ushort *src_data = src.ptr<ushort>();
ushort *dst_data = dst.ptr<ushort>();
#if CV_SIMD128
v_uint16x8 v_inB, v_inG, v_inR;
v_uint16x8 v_outB, v_outG, v_outR;
v_uint32x4 v_sB1, v_sB2, v_sG1, v_sG2, v_sR1, v_sR2;
v_uint32x4 v_gainB = v_setall_u32((uint)i_gainB), v_gainG = v_setall_u32((uint)i_gainG),
v_gainR = v_setall_u32((uint)i_gainR);
for (; i < N3 - 23; i += 24)
{
// Load 3x uint16x8 and deinterleave into vectors of each channel
v_load_deinterleave(&src_data[i], v_inB, v_inG, v_inR);
// Split into two uint vectors per channel
v_expand(v_inB, v_sB1, v_sB2);
v_expand(v_inG, v_sG1, v_sG2);
v_expand(v_inR, v_sR1, v_sR2);
// Multiply by scaling factors
v_sB1 = (v_sB1 * v_gainB) >> 16;
v_sB2 = (v_sB2 * v_gainB) >> 16;
v_sG1 = (v_sG1 * v_gainG) >> 16;
v_sG2 = (v_sG2 * v_gainG) >> 16;
v_sR1 = (v_sR1 * v_gainR) >> 16;
v_sR2 = (v_sR2 * v_gainR) >> 16;
// Pack into vectors of v_uint16x8
v_store_interleave(&dst_data[i], v_pack(v_sB1, v_sB2), v_pack(v_sG1, v_sG2), v_pack(v_sR1, v_sR2));
}
#endif
for (; i < N3; i += 3)
{
dst_data[i] = (ushort)((src_data[i] * i_gainB) >> 16);
dst_data[i + 1] = (ushort)((src_data[i + 1] * i_gainG) >> 16);
dst_data[i + 2] = (ushort)((src_data[i + 2] * i_gainR) >> 16);
}
}
}
void autowbGrayworld(InputArray _src, OutputArray _dst, float thresh)
{
Mat src = _src.getMat();
CV_Assert(!src.empty());
CV_Assert(src.isContinuous());
CV_Assert(src.type() == CV_8UC3 || src.type() == CV_16UC3);
int N = src.cols * src.rows, N3 = N * 3;
double dsumB = 0.0, dsumG = 0.0, dsumR = 0.0;
if (src.type() == CV_8UC3)
{
uint sumB = 0, sumG = 0, sumR = 0;
calculateChannelSums(sumB, sumG, sumR, src.ptr<uchar>(), N3, thresh);
dsumB = (double)sumB;
dsumG = (double)sumG;
dsumR = (double)sumR;
}
else if (src.type() == CV_16UC3)
{
uint64 sumB = 0, sumG = 0, sumR = 0;
calculateChannelSums(sumB, sumG, sumR, src.ptr<ushort>(), N3, thresh);
dsumB = (double)sumB;
dsumG = (double)sumG;
dsumR = (double)sumR;
}
// Find inverse of averages
double max_sum = max(dsumB, max(dsumR, dsumG));
const double eps = 0.1;
float dinvB = dsumB < eps ? 0.f : (float)(max_sum / dsumB), dinvG = dsumG < eps ? 0.f : (float)(max_sum / dsumG),
dinvR = dsumR < eps ? 0.f : (float)(max_sum / dsumR);
}} // Use the inverse of averages as channel gains:
applyChannelGains(src, _dst, dinvB, dinvG, dinvR);
}
}
}
#!/usr/bin/env python
from __future__ import print_function
import os, sys, argparse
import numpy as np
import scipy.io
from sklearn.tree import DecisionTreeRegressor
import cv2
import random
def parse_sequence(input_str):
if len(input_str) == 0:
return []
else:
return [o.strip() for o in input_str.split(",") if o]
def convert_to_8bit(arr, clip_percentile = 2.5):
arr = np.clip(arr * (255.0 / np.percentile(arr, 100 - clip_percentile)), 0, 255)
return arr.astype(np.uint8)
def learn_regression_tree_ensemble(img_features, gt_illuminants, num_trees, max_tree_depth):
eps = 0.001
inst = [[img_features[i], gt_illuminants[i][0] / (sum(gt_illuminants[i]) + eps),
gt_illuminants[i][1] / (sum(gt_illuminants[i]) + eps)] for i in range(len(img_features))]
inst.sort(key = lambda obj: obj[1]) #sort by r chromaticity
stride = int(np.ceil(len(inst) / float(num_trees+1)))
sz = 2*stride
dst_model = []
for tree_idx in range(num_trees):
#local group in the training data is additionally weighted by num_trees
local_group_range = range(tree_idx*stride, min(tree_idx*stride+sz, len(inst)))
X = num_trees * [inst[i][0] for i in local_group_range]
y_r = num_trees * [inst[i][1] for i in local_group_range]
y_g = num_trees * [inst[i][2] for i in local_group_range]
#add the rest of the training data:
X = X + [inst[i][0] for i in range(len(inst)) if i not in local_group_range]
y_r = y_r + [inst[i][1] for i in range(len(inst)) if i not in local_group_range]
y_g = y_g + [inst[i][2] for i in range(len(inst)) if i not in local_group_range]
local_model = []
for feature_idx in range(len(X[0])):
tree_r = DecisionTreeRegressor(max_depth = max_tree_depth, random_state = 1234)
tree_r.fit([el[feature_idx][0] for el in X], y_r)
tree_g = DecisionTreeRegressor(max_depth = max_tree_depth, random_state = 1234)
tree_g.fit([el[feature_idx][0] for el in X], y_g)
local_model.append([tree_r, tree_g])
dst_model.append(local_model)
return dst_model
def get_tree_node_lists(tree, tree_depth):
dst_feature_idx = (2**tree_depth-1) * [0]
dst_thresh_vals = (2**tree_depth-1) * [.5]
dst_leaf_vals = (2**tree_depth) * [-1]
leaf_idx_offset = (2**tree_depth-1)
left = tree.tree_.children_left
right = tree.tree_.children_right
threshold = tree.tree_.threshold
value = tree.tree_.value
feature = tree.tree_.feature
def recurse(left, right, threshold, feature, node, dst_idx, cur_depth):
if (threshold[node] != -2):
dst_feature_idx[dst_idx] = feature[node]
dst_thresh_vals[dst_idx] = threshold[node]
if left[node] != -1:
recurse (left, right, threshold, feature, left[node], 2*dst_idx+1, cur_depth + 1)
if right[node] != -1:
recurse (left, right, threshold, feature, right[node], 2*dst_idx+2, cur_depth + 1)
else:
range_start = 2**(tree_depth - cur_depth) * dst_idx + (2**(tree_depth - cur_depth) - 1) - leaf_idx_offset
range_end = 2**(tree_depth - cur_depth) * dst_idx + (2**(tree_depth - cur_depth+1) - 2) - leaf_idx_offset + 1
dst_leaf_vals[range_start:range_end] = (range_end - range_start) * [value[node][0][0]]
recurse(left, right, threshold, feature, 0, 0, 0)
return (dst_feature_idx, dst_thresh_vals, dst_leaf_vals)
def generate_code(model, input_params):
feature_idx = []
thresh_vals = []
leaf_vals = []
depth = int(input_params["--max_tree_depth"])
for local_model in model:
for feature in local_model:
(local_feature_idx, local_thresh_vals, local_leaf_vals) = get_tree_node_lists(feature[0], depth)
feature_idx += local_feature_idx
thresh_vals += local_thresh_vals
leaf_vals += local_leaf_vals
(local_feature_idx, local_thresh_vals, local_leaf_vals) = get_tree_node_lists(feature[1], depth)
feature_idx += local_feature_idx
thresh_vals += local_thresh_vals
leaf_vals += local_leaf_vals
res = "/* This file was automatically generated by learn_color_balance.py script\n" +\
" * using the following parameters:\n"
for key in input_params:
res += " " + key + " " + input_params[key]
res += "\n */\n"
res += "const int num_trees = " + str(len(model)) + ";\n"
res += "const int num_features = 4;\n"
res += "const int num_tree_nodes = " + str(2**depth) + ";\n"
res += "unsigned char feature_idx[num_trees*num_features*2*(num_tree_nodes-1)] = {" + str(feature_idx[0])
for i in range(1,len(feature_idx)):
res += "," + str(feature_idx[i])
res += "};\n"
res += "float thresh_vals[num_trees*num_features*2*(num_tree_nodes-1)] = {" + ("%.3ff" % thresh_vals[0])[1:]
for i in range(1,len(thresh_vals)):
res += "," + ("%.3ff" % thresh_vals[i])[1:]
res += "};\n"
res += "float leaf_vals[num_trees*num_features*2*num_tree_nodes] = {" + ("%.3ff" % leaf_vals[0])[1:]
for i in range(1,len(leaf_vals)):
res += "," + ("%.3ff" % leaf_vals[i])[1:]
res += "};\n"
return res
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description=("A tool for training the learning-based "
"color balance algorithm. Currently supports "
"training only on the Gehler-Shi and NUS 8-camera datasets."),
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
"-i",
"--input_folder",
metavar="INPUT_FOLDER",
default="",
help=("Folder containing the training dataset. Assumes minimally "
"processed png images like in the Gehler-Shi (http://www.cs.sfu.ca/~colour/data/shi_gehler/) "
"or NUS 8-camera (http://www.comp.nus.edu.sg/~whitebal/illuminant/illuminant.html) datasets"))
parser.add_argument(
"-g",
"--ground_truth",
metavar="GROUND_TRUTH",
default="real_illum_568..mat",
help=("Path to the mat file containing ground truth illuminations. Currently "
"supports formats supplied by the Gehler-Shi and NUS 8-camera datasets."))
parser.add_argument(
"-r",
"--range",
metavar="RANGE",
default="0,0",
help="Range of images from the input dataset to use for training")
parser.add_argument(
"-o",
"--out",
metavar="OUT",
default="learning_based_color_balance_model.hpp",
help="Path to the output learnt model")
parser.add_argument(
"--hist_bin_num",
metavar="HIST_BIN_NUM",
default="64",
help=("Size of one dimension of a three-dimensional RGB histogram employed in the "
"feature extraction step."))
parser.add_argument(
"--num_trees",
metavar="NUM_TREES",
default="20",
help=("Parameter to control the size of the regression tree ensemble"))
parser.add_argument(
"--max_tree_depth",
metavar="MAX_TREE_DEPTH",
default="4",
help=("Maxmimum depth of regression trees constructed during training."))
parser.add_argument(
"-a",
"--num_augmented",
metavar="NUM_AUGMENTED",
default="2",
help=("Number of augmented samples per one training image. Training set "
"augmentation tends to improve the learnt model robustness."))
args, other_args = parser.parse_known_args()
if not os.path.exists(args.input_folder):
print("Error: " + args.input_folder + (" does not exist. Please, correctly "
"specify the -i parameter"))
sys.exit(1)
if not os.path.exists(args.ground_truth):
print("Error: " + args.ground_truth + (" does not exist. Please, correctly "
"specify the -g parameter"))
sys.exit(1)
img_range = map(int,parse_sequence(args.range))
if len(img_range)!=2:
print("Error: Please specify the -r parameter in form <first_image_index>,<last_image_index>")
sys.exit(1)
hist_bin_num = int(args.hist_bin_num)
num_trees = int(args.num_trees)
max_tree_depth = int(args.max_tree_depth)
gt = scipy.io.loadmat(args.ground_truth)
img_files = sorted(os.listdir(args.input_folder))
base_gt_illuminants = []
black_levels = []
if "groundtruth_illuminants" in gt.keys() and "darkness_level" in gt.keys():
#NUS 8-camera dataset format
base_gt_illuminants = gt["groundtruth_illuminants"]
black_levels = len(gt_illuminants) * [gt["darkness_level"][0][0]]
elif "real_rgb" in gt.keys():
#Gehler-Shi dataset format
base_gt_illuminants = gt["real_rgb"]
black_levels = 87 * [0] + (len(base_gt_illuminants) - 87) * [129]
else:
print("Error: unknown ground-truth format, only formats of Gehler-Shi and NUS 8-camera datasets are supported")
sys.exit(1)
features = []
gt_illuminants = []
i=0
sz = len(img_files)
random.seed(1234)
for file in img_files:
if (i>=img_range[0] and i<img_range[1]) or (img_range[0]==img_range[1]==0):
cur_path = os.path.join(args.input_folder,file)
im = cv2.imread(cur_path, -1).astype(np.float32)
im -= black_levels[i]
im_8bit = convert_to_8bit(im)
cur_img_features = cv2.xphoto.extractSimpleFeatures(im_8bit, None, 255, 0.98, hist_bin_num)
features.append(cur_img_features.tolist())
gt_illuminants.append(base_gt_illuminants[i].tolist())
for iter in range(int(args.num_augmented)):
R_coef = random.uniform(0.2, 5.0)
G_coef = random.uniform(0.2, 5.0)
B_coef = random.uniform(0.2, 5.0)
im_8bit = im
im_8bit[:,:,0] *= B_coef
im_8bit[:,:,1] *= G_coef
im_8bit[:,:,2] *= R_coef
im_8bit = convert_to_8bit(im)
cur_img_features = cv2.xphoto.extractSimpleFeatures(im_8bit, None, 255, 0.98, hist_bin_num)
features.append(cur_img_features.tolist())
illum = base_gt_illuminants[i]
illum[0] *= R_coef
illum[1] *= G_coef
illum[2] *= B_coef
gt_illuminants.append(illum.tolist())
sys.stdout.write("Computing features: [%3d/%3d]\r" % (i, sz)),
sys.stdout.flush()
i+=1
print("\nLearning the model...")
model = learn_regression_tree_ensemble(features, gt_illuminants, num_trees, max_tree_depth)
print("Generating code...")
str = generate_code(model,{"-r":args.range, "--hist_bin_num": args.hist_bin_num, "--num_trees": args.num_trees,
"--max_tree_depth": args.max_tree_depth, "--num_augmented": args.num_augmented})
f = open(args.out,"w")
f.write(str)
f.close()
print("Done")
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "learning_based_color_balance_model.hpp"
#include "opencv2/core.hpp"
#include "opencv2/core/hal/intrin.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/xphoto.hpp"
using namespace std;
#define EPS 0.00001f
namespace cv
{
namespace xphoto
{
inline void getChromaticity(Vec2f &dst, float R, float G, float B)
{
dst[0] = R / (R + G + B + EPS);
dst[1] = G / (R + G + B + EPS);
}
struct hist_elem
{
float hist_val;
float r, g;
hist_elem(float _hist_val, Vec2f chromaticity) : hist_val(_hist_val), r(chromaticity[0]), g(chromaticity[1]) {}
};
bool operator<(const hist_elem &a, const hist_elem &b);
void getColorPalleteMode(Vec2f &dst, hist_elem *pallete, int pallete_sz, float bandwidth);
void preprocessing(Mat &dst_mask, int &dst_max_val, Mat &src, int range_max_val, float saturation_thresh);
void getAverageAndBrightestColorChromaticity(Vec2f &average_chromaticity, Vec2f &brightest_chromaticity, Mat &src,
Mat &mask);
void getHistogramBasedFeatures(Vec2f &dominant_chromaticity, Vec2f &chromaticity_pallete_mode, Mat &src, Mat &mask,
int hist_bin_num, int max_val);
bool operator<(const hist_elem &a, const hist_elem &b) { return a.hist_val > b.hist_val; }
/* Returns the most high-density point (i.e. mode) of the color pallete.
* Uses a simplistic kernel density estimator with a Epanechnikov kernel and
* fixed bandwidth.
*/
void getColorPalleteMode(Vec2f &dst, hist_elem *pallete, int pallete_sz, float bandwidth)
{
float max_density = -1.0f;
float denom = bandwidth * bandwidth;
for (int i = 0; i < pallete_sz; i++)
{
float cur_density = 0.0f;
float cur_dist_sq;
for (int j = 0; j < pallete_sz; j++)
{
cur_dist_sq = (pallete[i].r - pallete[j].r) * (pallete[i].r - pallete[j].r) +
(pallete[i].g - pallete[j].g) * (pallete[i].g - pallete[j].g);
cur_density += max((1.0f - (cur_dist_sq / denom)), 0.0f);
}
if (cur_density > max_density)
{
max_density = cur_density;
dst[0] = pallete[i].r;
dst[1] = pallete[i].g;
}
}
}
/* Computes a mask for non-saturated pixels and maximum pixel value
* which are then used for feature computation
*/
void preprocessing(Mat &dst_mask, int &dst_max_val, Mat &src, int range_max_val, float saturation_thresh)
{
dst_mask = Mat(src.size(), CV_8U);
uchar *mask_ptr = dst_mask.ptr<uchar>();
int src_len = src.rows * src.cols;
int thresh = (int)(saturation_thresh * range_max_val);
int i = 0;
int local_max;
dst_max_val = -1;
if (src.type() == CV_8UC3)
{
uchar *src_ptr = src.ptr<uchar>();
#if CV_SIMD128
v_uint8x16 v_inB, v_inG, v_inR, v_local_max;
v_uint8x16 v_global_max = v_setall_u8(0), v_mask, v_thresh = v_setall_u8((uchar)thresh);
for (; i < src_len - 15; i += 16)
{
v_load_deinterleave(src_ptr + 3 * i, v_inB, v_inG, v_inR);
v_local_max = v_max(v_inB, v_max(v_inG, v_inR));
v_global_max = v_max(v_local_max, v_global_max);
v_mask = (v_local_max < v_thresh);
v_store(mask_ptr + i, v_mask);
}
uchar global_max[16];
v_store(global_max, v_global_max);
for (int j = 0; j < 16; j++)
{
if (global_max[j] > dst_max_val)
dst_max_val = global_max[j];
}
#endif
for (; i < src_len; i++)
{
local_max = max(src_ptr[3 * i], max(src_ptr[3 * i + 1], src_ptr[3 * i + 2]));
if (local_max > dst_max_val)
dst_max_val = local_max;
if (local_max < thresh)
mask_ptr[i] = 255;
else
mask_ptr[i] = 0;
}
}
else if (src.type() == CV_16UC3)
{
ushort *src_ptr = src.ptr<ushort>();
#if CV_SIMD128
v_uint16x8 v_inB, v_inG, v_inR, v_local_max;
v_uint16x8 v_global_max = v_setall_u16(0), v_mask, v_thresh = v_setall_u16((ushort)thresh);
for (; i < src_len - 7; i += 8)
{
v_load_deinterleave(src_ptr + 3 * i, v_inB, v_inG, v_inR);
v_local_max = v_max(v_inB, v_max(v_inG, v_inR));
v_global_max = v_max(v_local_max, v_global_max);
v_mask = (v_local_max < v_thresh);
v_pack_store(mask_ptr + i, v_mask);
}
ushort global_max[8];
v_store(global_max, v_global_max);
for (int j = 0; j < 8; j++)
{
if (global_max[j] > dst_max_val)
dst_max_val = global_max[j];
}
#endif
for (; i < src_len; i++)
{
local_max = max(src_ptr[3 * i], max(src_ptr[3 * i + 1], src_ptr[3 * i + 2]));
if (local_max > dst_max_val)
dst_max_val = local_max;
if (local_max < thresh)
mask_ptr[i] = 255;
else
mask_ptr[i] = 0;
}
}
}
void getAverageAndBrightestColorChromaticity(Vec2f &average_chromaticity, Vec2f &brightest_chromaticity, Mat &src,
Mat &mask)
{
int i = 0;
int src_len = src.rows * src.cols;
uchar *mask_ptr = mask.ptr<uchar>();
uint brightestB = 0, brightestG = 0, brightestR = 0;
uint max_sum = 0;
if (src.type() == CV_8UC3)
{
uint sumB = 0, sumG = 0, sumR = 0;
uchar *src_ptr = src.ptr<uchar>();
#if CV_SIMD128
v_uint8x16 v_inB, v_inG, v_inR, v_mask;
v_uint16x8 v_sR1, v_sR2, v_sG1, v_sG2, v_sB1, v_sB2, v_sum;
v_uint16x8 v_max_sum = v_setall_u16(0), v_max_mask, v_brightestR, v_brightestG, v_brightestB;
v_uint32x4 v_uint1, v_uint2, v_SB = v_setzero_u32(), v_SG = v_setzero_u32(), v_SR = v_setzero_u32();
for (; i < src_len - 15; i += 16)
{
v_load_deinterleave(src_ptr + 3 * i, v_inB, v_inG, v_inR);
v_mask = v_load(mask_ptr + i);
v_inB &= v_mask;
v_inG &= v_mask;
v_inR &= v_mask;
v_expand(v_inB, v_sB1, v_sB2);
v_expand(v_inG, v_sG1, v_sG2);
v_expand(v_inR, v_sR1, v_sR2);
// update the brightest (R,G,B) tuple (process left half):
v_sum = v_sB1 + v_sG1 + v_sR1;
v_max_mask = (v_sum > v_max_sum);
v_max_sum = v_max(v_sum, v_max_sum);
v_brightestB = (v_sB1 & v_max_mask) + (v_brightestB & (~v_max_mask));
v_brightestG = (v_sG1 & v_max_mask) + (v_brightestG & (~v_max_mask));
v_brightestR = (v_sR1 & v_max_mask) + (v_brightestR & (~v_max_mask));
// update the brightest (R,G,B) tuple (process right half):
v_sum = v_sB2 + v_sG2 + v_sR2;
v_max_mask = (v_sum > v_max_sum);
v_max_sum = v_max(v_sum, v_max_sum);
v_brightestB = (v_sB2 & v_max_mask) + (v_brightestB & (~v_max_mask));
v_brightestG = (v_sG2 & v_max_mask) + (v_brightestG & (~v_max_mask));
v_brightestR = (v_sR2 & v_max_mask) + (v_brightestR & (~v_max_mask));
// update sums:
v_sB1 = v_sB1 + v_sB2;
v_sG1 = v_sG1 + v_sG2;
v_sR1 = v_sR1 + v_sR2;
v_expand(v_sB1, v_uint1, v_uint2);
v_SB += v_uint1 + v_uint2;
v_expand(v_sG1, v_uint1, v_uint2);
v_SG += v_uint1 + v_uint2;
v_expand(v_sR1, v_uint1, v_uint2);
v_SR += v_uint1 + v_uint2;
}
sumB = v_reduce_sum(v_SB);
sumG = v_reduce_sum(v_SG);
sumR = v_reduce_sum(v_SR);
ushort brightestB_arr[8], brightestG_arr[8], brightestR_arr[8], max_sum_arr[8];
v_store(brightestB_arr, v_brightestB);
v_store(brightestG_arr, v_brightestG);
v_store(brightestR_arr, v_brightestR);
v_store(max_sum_arr, v_max_sum);
for (int j = 0; j < 8; j++)
{
if (max_sum_arr[j] > max_sum)
{
max_sum = max_sum_arr[j];
brightestB = brightestB_arr[j];
brightestG = brightestG_arr[j];
brightestR = brightestR_arr[j];
}
}
#endif
for (; i < src_len; i++)
{
uint sum_val = src_ptr[3 * i] + src_ptr[3 * i + 1] + src_ptr[3 * i + 2];
if (mask_ptr[i])
{
sumB += src_ptr[3 * i];
sumG += src_ptr[3 * i + 1];
sumR += src_ptr[3 * i + 2];
if (sum_val > max_sum)
{
max_sum = sum_val;
brightestB = src_ptr[3 * i];
brightestG = src_ptr[3 * i + 1];
brightestR = src_ptr[3 * i + 2];
}
}
}
double maxRGB = (double)max(sumR, max(sumG, sumB));
getChromaticity(average_chromaticity, (float)(sumR / maxRGB), (float)(sumG / maxRGB), (float)(sumB / maxRGB));
getChromaticity(brightest_chromaticity, (float)brightestR, (float)brightestG, (float)brightestB);
}
else if (src.type() == CV_16UC3)
{
uint64 sumB = 0, sumG = 0, sumR = 0;
ushort *src_ptr = src.ptr<ushort>();
#if CV_SIMD128
v_uint16x8 v_inB, v_inG, v_inR, v_mask, v_mask_lower = v_setall_u16(255);
v_uint32x4 v_iR1, v_iR2, v_iG1, v_iG2, v_iB1, v_iB2, v_sum;
v_uint32x4 v_max_sum = v_setall_u32(0), v_max_mask, v_brightestR, v_brightestG, v_brightestB;
v_uint64x2 v_uint64_1, v_uint64_2, v_SB = v_setzero_u64(), v_SG = v_setzero_u64(), v_SR = v_setzero_u64();
for (; i < src_len - 7; i += 8)
{
v_load_deinterleave(src_ptr + 3 * i, v_inB, v_inG, v_inR);
v_mask = v_load_expand(mask_ptr + i);
v_mask = v_mask | ((v_mask & v_mask_lower) << 8);
v_inB &= v_mask;
v_inG &= v_mask;
v_inR &= v_mask;
v_expand(v_inB, v_iB1, v_iB2);
v_expand(v_inG, v_iG1, v_iG2);
v_expand(v_inR, v_iR1, v_iR2);
// update the brightest (R,G,B) tuple (process left half):
v_sum = v_iB1 + v_iG1 + v_iR1;
v_max_mask = (v_sum > v_max_sum);
v_max_sum = v_max(v_sum, v_max_sum);
v_brightestB = (v_iB1 & v_max_mask) + (v_brightestB & (~v_max_mask));
v_brightestG = (v_iG1 & v_max_mask) + (v_brightestG & (~v_max_mask));
v_brightestR = (v_iR1 & v_max_mask) + (v_brightestR & (~v_max_mask));
// update the brightest (R,G,B) tuple (process right half):
v_sum = v_iB2 + v_iG2 + v_iR2;
v_max_mask = (v_sum > v_max_sum);
v_max_sum = v_max(v_sum, v_max_sum);
v_brightestB = (v_iB2 & v_max_mask) + (v_brightestB & (~v_max_mask));
v_brightestG = (v_iG2 & v_max_mask) + (v_brightestG & (~v_max_mask));
v_brightestR = (v_iR2 & v_max_mask) + (v_brightestR & (~v_max_mask));
// update sums:
v_iB1 = v_iB1 + v_iB2;
v_iG1 = v_iG1 + v_iG2;
v_iR1 = v_iR1 + v_iR2;
v_expand(v_iB1, v_uint64_1, v_uint64_2);
v_SB += v_uint64_1 + v_uint64_2;
v_expand(v_iG1, v_uint64_1, v_uint64_2);
v_SG += v_uint64_1 + v_uint64_2;
v_expand(v_iR1, v_uint64_1, v_uint64_2);
v_SR += v_uint64_1 + v_uint64_2;
}
uint64 sum_arr[2];
v_store(sum_arr, v_SB);
sumB = sum_arr[0] + sum_arr[1];
v_store(sum_arr, v_SG);
sumG = sum_arr[0] + sum_arr[1];
v_store(sum_arr, v_SR);
sumR = sum_arr[0] + sum_arr[1];
uint brightestB_arr[4], brightestG_arr[4], brightestR_arr[4], max_sum_arr[4];
v_store(brightestB_arr, v_brightestB);
v_store(brightestG_arr, v_brightestG);
v_store(brightestR_arr, v_brightestR);
v_store(max_sum_arr, v_max_sum);
for (int j = 0; j < 4; j++)
{
if (max_sum_arr[j] > max_sum)
{
max_sum = max_sum_arr[j];
brightestB = brightestB_arr[j];
brightestG = brightestG_arr[j];
brightestR = brightestR_arr[j];
}
}
#endif
for (; i < src_len; i++)
{
uint sum_val = src_ptr[3 * i] + src_ptr[3 * i + 1] + src_ptr[3 * i + 2];
if (mask_ptr[i])
{
sumB += src_ptr[3 * i];
sumG += src_ptr[3 * i + 1];
sumR += src_ptr[3 * i + 2];
if (sum_val > max_sum)
{
max_sum = sum_val;
brightestB = src_ptr[3 * i];
brightestG = src_ptr[3 * i + 1];
brightestR = src_ptr[3 * i + 2];
}
}
}
double maxRGB = (double)max(sumR, max(sumG, sumB));
getChromaticity(average_chromaticity, (float)(sumR / maxRGB), (float)(sumG / maxRGB), (float)(sumB / maxRGB));
getChromaticity(brightest_chromaticity, (float)brightestR, (float)brightestG, (float)brightestB);
}
}
void getHistogramBasedFeatures(Vec2f &dominant_chromaticity, Vec2f &chromaticity_pallete_mode, Mat &src, Mat &mask,
int hist_bin_num, int max_val)
{
const int pallete_size = 300;
const float pallete_bandwidth = 0.1f;
MatND hist;
int channels[] = {0, 1, 2};
int histSize[] = {hist_bin_num, hist_bin_num, hist_bin_num};
float range[] = {0, (float)max(hist_bin_num, max_val)};
const float *ranges[] = {range, range, range};
calcHist(&src, 1, channels, mask, hist, 3, histSize, ranges);
int dominant_B = 0, dominant_G = 0, dominant_R = 0;
double max_hist_val = 0;
float *hist_ptr = hist.ptr<float>();
for (int i = 0; i < hist_bin_num; i++)
for (int j = 0; j < hist_bin_num; j++)
for (int k = 0; k < hist_bin_num; k++)
{
if (*hist_ptr > max_hist_val)
{
max_hist_val = *hist_ptr;
dominant_B = i;
dominant_G = j;
dominant_R = k;
}
hist_ptr++;
}
getChromaticity(dominant_chromaticity, (float)dominant_R, (float)dominant_G, (float)dominant_B);
vector<hist_elem> pallete;
pallete.reserve(pallete_size);
hist_ptr = hist.ptr<float>();
// extract top pallete_size most common colors and add them to the pallete:
for (int i = 0; i < hist_bin_num; i++)
for (int j = 0; j < hist_bin_num; j++)
for (int k = 0; k < hist_bin_num; k++)
{
float bin_count = *hist_ptr;
if (bin_count < EPS)
{
hist_ptr++;
continue;
}
Vec2f chromaticity;
getChromaticity(chromaticity, (float)k, (float)j, (float)i);
hist_elem el(bin_count, chromaticity);
if (pallete.size() < pallete_size)
{
pallete.push_back(el);
if (pallete.size() == pallete_size)
make_heap(pallete.begin(), pallete.end());
}
else if (bin_count > pallete.front().hist_val)
{
pop_heap(pallete.begin(), pallete.end());
pallete.back() = el;
push_heap(pallete.begin(), pallete.end());
}
hist_ptr++;
}
getColorPalleteMode(chromaticity_pallete_mode, (hist_elem *)(&pallete[0]), (int)pallete.size(), pallete_bandwidth);
}
void extractSimpleFeatures(InputArray _src, OutputArray _dst, int range_max_val, float saturation_thresh,
int hist_bin_num)
{
Mat src = _src.getMat();
CV_Assert(!src.empty());
CV_Assert(src.isContinuous());
CV_Assert(src.type() == CV_8UC3 || src.type() == CV_16UC3);
vector<Vec2f> dst(num_features);
Mat mask;
int max_val = 0;
preprocessing(mask, max_val, src, range_max_val, saturation_thresh);
getAverageAndBrightestColorChromaticity(dst[0], dst[1], src, mask);
getHistogramBasedFeatures(dst[2], dst[3], src, mask, hist_bin_num, max_val);
Mat(dst).convertTo(_dst, CV_32F);
}
inline float regressionTreePredict(Vec2f src, uchar *tree_feature_idx, float *tree_thresh_vals, float *tree_leaf_vals)
{
int node_idx = 0;
int depth = (int)round(log(num_tree_nodes) / log(2));
for (int i = 0; i < depth; i++)
{
if (src[tree_feature_idx[node_idx]] <= tree_thresh_vals[node_idx])
node_idx = 2 * node_idx + 1;
else
node_idx = 2 * node_idx + 2;
}
return tree_leaf_vals[node_idx - num_tree_nodes + 1];
}
void autowbLearningBased(InputArray _src, OutputArray _dst, int range_max_val, float saturation_thresh,
int hist_bin_num)
{
const float prediction_thresh = 0.025f;
Mat src = _src.getMat();
CV_Assert(!src.empty());
CV_Assert(src.isContinuous());
CV_Assert(src.type() == CV_8UC3 || src.type() == CV_16UC3);
vector<Vec2f> features;
extractSimpleFeatures(src, features, range_max_val, saturation_thresh, hist_bin_num);
int feature_model_size = 2 * (num_tree_nodes - 1);
int local_model_size = num_features * feature_model_size;
int feature_model_size_leaf = 2 * num_tree_nodes;
int local_model_size_leaf = num_features * feature_model_size_leaf;
vector<float> consensus_r, consensus_g;
vector<float> all_r, all_g;
for (int i = 0; i < num_trees; i++)
{
Vec2f local_predictions[num_features];
for (int j = 0; j < num_features; j++)
{
float r = regressionTreePredict(features[j], feature_idx + local_model_size * i + feature_model_size * j,
thresh_vals + local_model_size * i + feature_model_size * j,
leaf_vals + local_model_size_leaf * i + feature_model_size_leaf * j);
float g = regressionTreePredict(
features[j], feature_idx + local_model_size * i + feature_model_size * j + feature_model_size / 2,
thresh_vals + local_model_size * i + feature_model_size * j + feature_model_size / 2,
leaf_vals + local_model_size_leaf * i + feature_model_size_leaf * j + feature_model_size_leaf / 2);
local_predictions[j] = Vec2f(r, g);
all_r.push_back(r);
all_g.push_back(g);
}
int agreement_degree = 0;
for (int j = 0; j < num_features - 1; j++)
for (int k = j + 1; k < num_features; k++)
{
if (norm(local_predictions[j] - local_predictions[k]) < prediction_thresh)
agreement_degree++;
}
if (agreement_degree >= 3)
{
for (int j = 0; j < num_features; j++)
{
consensus_r.push_back(local_predictions[j][0]);
consensus_g.push_back(local_predictions[j][1]);
}
}
}
float illuminant_r, illuminant_g;
if (consensus_r.size() == 0)
{
nth_element(all_r.begin(), all_r.begin() + all_r.size() / 2, all_r.end());
illuminant_r = all_r[all_r.size() / 2];
nth_element(all_g.begin(), all_g.begin() + all_g.size() / 2, all_g.end());
illuminant_g = all_g[all_g.size() / 2];
}
else
{
nth_element(consensus_r.begin(), consensus_r.begin() + consensus_r.size() / 2, consensus_r.end());
illuminant_r = consensus_r[consensus_r.size() / 2];
nth_element(consensus_g.begin(), consensus_g.begin() + consensus_g.size() / 2, consensus_g.end());
illuminant_g = consensus_g[consensus_g.size() / 2];
}
float denom = 1 - illuminant_r - illuminant_g;
float gainB = 1.0f;
float gainG = denom / illuminant_g;
float gainR = denom / illuminant_r;
applyChannelGains(src, _dst, gainB, gainG, gainR);
}
}
}
/* This file was automatically generated by learn_color_balance.py script
* using the following parameters:
--num_trees 20 --hist_bin_num 64 --max_tree_depth 4 --num_augmented 2 -r 0,0
*/
const int num_trees = 20;
const int num_features = 4;
const int num_tree_nodes = 16;
unsigned char feature_idx[num_trees * num_features * 2 * (num_tree_nodes - 1)] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1,
1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1,
0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0,
0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0,
1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0,
1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0,
1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1,
0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1,
0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1,
0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1,
0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1,
1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0,
1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0,
0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1,
1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
float thresh_vals[num_trees * num_features * 2 * (num_tree_nodes - 1)] = {
.193f, .098f, .455f, .040f, .145f, .316f, .571f, .016f, .058f, .137f, .174f, .276f, .356f, .515f, .730f, .606f, .324f,
.794f, .230f, .440f, .683f, .878f, .134f, .282f, .406f, .532f, .036f, .747f, .830f, .931f, .196f, .145f, .363f, .047f,
.351f, .279f, .519f, .013f, .887f, .191f, .193f, .361f, .316f, .576f, .445f, .524f, .368f, .752f, .271f, .477f, .636f,
.798f, .146f, .249f, .423f, .521f, .446f, .023f, .795f, .908f, .259f, .026f, .557f, .125f, .121f, .432f, .774f, .500f,
.500f, .984f, .202f, .307f, .509f, .038f, .042f, .667f, .500f, .000f, .014f, .560f, .984f, .000f, .125f, .333f, .553f,
.333f, .860f, .000f, .500f, .000f, .193f, .114f, .432f, .032f, .157f, .310f, .567f, .013f, .048f, .127f, .428f, .271f,
.370f, .511f, .762f, .615f, .325f, .833f, .193f, .440f, .728f, .887f, .086f, .230f, .411f, .546f, .671f, .009f, .863f,
.944f, .283f, .174f, .515f, .087f, .209f, .356f, .693f, .059f, .145f, .344f, .254f, .316f, .455f, .571f, .811f, .537f,
.343f, .751f, .243f, .435f, .630f, .813f, .134f, .310f, .391f, .487f, .597f, .683f, .755f, .878f, .307f, .145f, .446f,
.063f, .349f, .457f, .576f, .021f, .792f, .268f, .250f, .421f, .463f, .574f, .610f, .478f, .354f, .636f, .271f, .423f,
.529f, .795f, .146f, .257f, .124f, .458f, .043f, .022f, .752f, .836f, .307f, .026f, .557f, .125f, .135f, .435f, .774f,
.500f, .500f, .984f, .238f, .500f, .509f, .038f, .042f, .600f, .400f, .000f, .014f, .500f, .667f, .000f, .125f, .250f,
.455f, .097f, .333f, .984f, .000f, .000f, .271f, .155f, .511f, .078f, .198f, .372f, .696f, .059f, .123f, .183f, .237f,
.310f, .432f, .564f, .875f, .546f, .324f, .728f, .195f, .423f, .616f, .823f, .131f, .281f, .355f, .490f, .581f, .677f,
.775f, .885f, .300f, .192f, .515f, .109f, .248f, .374f, .693f, .065f, .157f, .580f, .276f, .341f, .455f, .571f, .811f,
.532f, .324f, .679f, .196f, .421f, .630f, .804f, .111f, .252f, .348f, .447f, .565f, .247f, .755f, .866f, .335f, .188f,
.503f, .104f, .270f, .521f, .607f, .021f, .134f, .364f, .332f, .421f, .571f, .576f, .836f, .458f, .294f, .523f, .171f,
.413f, .501f, .651f, .065f, .273f, .354f, .062f, .052f, .489f, .523f, .784f, .307f, .026f, .557f, .125f, .156f, .432f,
.774f, .500f, .500f, .984f, .200f, .399f, .509f, .038f, .042f, .560f, .429f, .000f, .014f, .138f, .667f, .000f, .125f,
.222f, .500f, .522f, .286f, .984f, .500f, .000f, .295f, .186f, .564f, .104f, .293f, .432f, .762f, .059f, .127f, .271f,
.593f, .353f, .511f, .630f, .875f, .538f, .324f, .685f, .195f, .423f, .616f, .791f, .099f, .281f, .389f, .452f, .581f,
.293f, .728f, .842f, .320f, .211f, .521f, .098f, .251f, .403f, .693f, .058f, .158f, .580f, .469f, .188f, .455f, .571f,
.811f, .532f, .324f, .717f, .193f, .415f, .587f, .791f, .122f, .265f, .391f, .457f, .559f, .665f, .753f, .835f, .405f,
.305f, .503f, .135f, .457f, .483f, .607f, .044f, .182f, .394f, .569f, .156f, .484f, .576f, .836f, .455f, .294f, .576f,
.248f, .376f, .482f, .651f, .089f, .273f, .181f, .430f, .472f, .500f, .265f, .784f, .414f, .228f, .557f, .026f, .620f,
.476f, .774f, .500f, .156f, .595f, .304f, .322f, .509f, .038f, .042f, .533f, .391f, .683f, .106f, .175f, .286f, .010f,
.026f, .226f, .500f, .429f, .197f, .655f, .000f, .800f, .386f, .218f, .564f, .082f, .295f, .479f, .762f, .043f, .155f,
.293f, .440f, .432f, .511f, .630f, .875f, .544f, .306f, .705f, .185f, .412f, .606f, .791f, .109f, .263f, .372f, .502f,
.315f, .685f, .741f, .855f, .365f, .221f, .544f, .110f, .307f, .455f, .730f, .059f, .179f, .276f, .468f, .403f, .515f,
.678f, .860f, .520f, .340f, .683f, .196f, .440f, .591f, .754f, .122f, .274f, .394f, .464f, .556f, .622f, .723f, .835f,
.375f, .147f, .503f, .055f, .319f, .436f, .607f, .018f, .773f, .624f, .298f, .467f, .450f, .576f, .836f, .454f, .287f,
.571f, .163f, .376f, .308f, .648f, .086f, .236f, .373f, .427f, .477f, .496f, .333f, .795f, .500f, .307f, .774f, .026f,
.462f, .557f, .042f, .125f, .191f, .406f, .500f, .509f, .038f, .000f, .138f, .600f, .400f, .000f, .073f, .500f, .010f,
.000f, .026f, .275f, .226f, .588f, .984f, .688f, .000f, .000f, .386f, .245f, .564f, .097f, .343f, .479f, .762f, .048f,
.180f, .424f, .466f, .432f, .511f, .630f, .875f, .541f, .335f, .702f, .193f, .413f, .602f, .791f, .116f, .296f, .376f,
.481f, .210f, .641f, .741f, .863f, .341f, .145f, .531f, .087f, .283f, .455f, .729f, .047f, .109f, .189f, .469f, .375f,
.515f, .571f, .811f, .553f, .324f, .683f, .196f, .425f, .607f, .761f, .122f, .252f, .368f, .499f, .248f, .648f, .723f,
.848f, .365f, .135f, .503f, .082f, .279f, .503f, .607f, .044f, .086f, .182f, .319f, .395f, .448f, .576f, .836f, .454f,
.287f, .571f, .163f, .389f, .308f, .752f, .086f, .250f, .373f, .436f, .498f, .496f, .636f, .834f, .500f, .307f, .774f,
.026f, .474f, .557f, .042f, .125f, .000f, .414f, .561f, .509f, .038f, .000f, .138f, .560f, .626f, .000f, .400f, .174f,
.984f, .000f, .073f, .500f, .852f, .226f, .667f, .500f, .000f, .000f, .353f, .141f, .564f, .073f, .265f, .432f, .762f,
.032f, .106f, .193f, .478f, .470f, .511f, .630f, .875f, .605f, .325f, .728f, .193f, .437f, .652f, .855f, .116f, .264f,
.412f, .502f, .202f, .685f, .776f, .912f, .341f, .161f, .531f, .087f, .298f, .455f, .729f, .047f, .109f, .185f, .467f,
.375f, .515f, .571f, .811f, .580f, .324f, .685f, .196f, .425f, .607f, .824f, .122f, .252f, .368f, .512f, .231f, .648f,
.749f, .885f, .365f, .135f, .503f, .082f, .319f, .499f, .607f, .044f, .086f, .702f, .384f, .439f, .465f, .576f, .836f,
.478f, .287f, .594f, .163f, .454f, .502f, .784f, .086f, .250f, .364f, .345f, .500f, .370f, .651f, .836f, .476f, .295f,
.557f, .026f, .474f, .509f, .774f, .125f, .000f, .333f, .364f, .495f, .405f, .038f, .042f, .560f, .626f, .000f, .400f,
.174f, .010f, .000f, .059f, .500f, .852f, .226f, .667f, .667f, .000f, .000f, .419f, .157f, .564f, .078f, .295f, .479f,
.762f, .043f, .124f, .256f, .353f, .432f, .511f, .630f, .875f, .605f, .325f, .713f, .193f, .423f, .677f, .842f, .116f,
.264f, .385f, .502f, .621f, .177f, .756f, .912f, .388f, .174f, .564f, .095f, .310f, .456f, .730f, .047f, .143f, .276f,
.468f, .431f, .515f, .682f, .860f, .352f, .215f, .591f, .124f, .290f, .536f, .723f, .064f, .172f, .244f, .318f, .428f,
.548f, .666f, .825f, .394f, .146f, .503f, .055f, .349f, .499f, .607f, .018f, .773f, .271f, .327f, .439f, .465f, .576f,
.836f, .464f, .287f, .588f, .163f, .377f, .502f, .752f, .086f, .250f, .237f, .454f, .464f, .370f, .636f, .834f, .476f,
.000f, .557f, .282f, .000f, .509f, .774f, .026f, .481f, .000f, .000f, .495f, .405f, .038f, .042f, .556f, .626f, .000f,
.343f, .174f, .010f, .000f, .023f, .559f, .852f, .226f, .667f, .667f, .000f, .000f, .468f, .157f, .567f, .078f, .305f,
.511f, .762f, .043f, .124f, .271f, .464f, .511f, .193f, .630f, .875f, .351f, .208f, .602f, .116f, .302f, .489f, .720f,
.059f, .164f, .281f, .325f, .414f, .282f, .654f, .842f, .388f, .174f, .564f, .095f, .313f, .456f, .730f, .047f, .143f,
.279f, .468f, .431f, .515f, .682f, .860f, .356f, .215f, .591f, .124f, .290f, .539f, .753f, .064f, .174f, .274f, .318f,
.435f, .556f, .647f, .848f, .411f, .146f, .411f, .055f, .349f, .490f, .499f, .018f, .773f, .271f, .327f, .181f, .610f,
.467f, .448f, .287f, .163f, .454f, .086f, .262f, .359f, .636f, .041f, .476f, .200f, .633f, .257f, .428f, .522f, .795f,
.435f, .000f, .557f, .049f, .000f, .509f, .774f, .330f, .282f, .500f, .000f, .462f, .405f, .038f, .042f, .393f, .029f,
.000f, .026f, .226f, .602f, .000f, .500f, .183f, .163f, .337f, .500f, .984f, .500f, .000f, .468f, .157f, .567f, .078f,
.314f, .511f, .762f, .043f, .124f, .271f, .456f, .511f, .193f, .630f, .875f, .355f, .208f, .602f, .116f, .296f, .414f,
.761f, .059f, .179f, .263f, .324f, .381f, .502f, .681f, .855f, .356f, .174f, .537f, .095f, .289f, .455f, .729f, .047f,
.143f, .186f, .471f, .403f, .515f, .588f, .811f, .352f, .215f, .568f, .147f, .282f, .435f, .723f, .077f, .191f, .265f,
.318f, .395f, .543f, .630f, .824f, .439f, .141f, .443f, .051f, .363f, .490f, .479f, .018f, .773f, .279f, .446f, .223f,
.607f, .504f, .491f, .298f, .179f, .454f, .086f, .264f, .350f, .571f, .041f, .122f, .390f, .287f, .324f, .431f, .502f,
.752f, .500f, .307f, .774f, .026f, .500f, .557f, .042f, .125f, .000f, .434f, .524f, .509f, .038f, .000f, .138f, .447f,
.225f, .000f, .026f, .226f, .600f, .000f, .500f, .337f, .667f, .333f, .269f, .984f, .500f, .000f, .425f, .157f, .564f,
.078f, .314f, .511f, .762f, .043f, .124f, .269f, .467f, .479f, .561f, .630f, .875f, .372f, .208f, .602f, .148f, .296f,
.441f, .775f, .072f, .179f, .230f, .324f, .408f, .516f, .652f, .863f, .403f, .179f, .571f, .095f, .337f, .456f, .730f,
.047f, .145f, .267f, .458f, .056f, .521f, .682f, .860f, .352f, .193f, .568f, .095f, .282f, .435f, .696f, .056f, .147f,
.230f, .318f, .395f, .534f, .630f, .808f, .484f, .141f, .454f, .051f, .372f, .605f, .487f, .018f, .773f, .316f, .483f,
.490f, .610f, .486f, .490f, .298f, .200f, .472f, .122f, .275f, .397f, .571f, .061f, .174f, .370f, .364f, .349f, .435f,
.499f, .752f, .500f, .307f, .667f, .026f, .500f, .661f, .774f, .125f, .000f, .398f, .524f, .557f, .167f, .760f, .042f,
.500f, .667f, .000f, .429f, .138f, .984f, .000f, .029f, .364f, .906f, .226f, .667f, .500f, .000f, .000f, .488f, .157f,
.567f, .078f, .366f, .511f, .762f, .043f, .124f, .309f, .487f, .511f, .193f, .630f, .875f, .376f, .208f, .575f, .148f,
.302f, .452f, .687f, .072f, .182f, .230f, .325f, .413f, .516f, .621f, .833f, .452f, .179f, .571f, .095f, .349f, .515f,
.730f, .047f, .145f, .244f, .444f, .456f, .206f, .682f, .860f, .368f, .215f, .568f, .124f, .290f, .444f, .689f, .063f,
.188f, .252f, .343f, .406f, .516f, .630f, .804f, .162f, .085f, .490f, .044f, .086f, .372f, .454f, .015f, .424f, .463f,
.134f, .262f, .478f, .607f, .490f, .462f, .297f, .583f, .163f, .349f, .522f, .702f, .065f, .237f, .315f, .428f, .499f,
.397f, .636f, .795f, .500f, .310f, .667f, .026f, .432f, .661f, .000f, .125f, .146f, .500f, .332f, .557f, .167f, .774f,
.000f, .537f, .667f, .000f, .400f, .138f, .984f, .000f, .029f, .500f, .906f, .226f, .667f, .500f, .000f, .000f, .511f,
.157f, .696f, .078f, .361f, .564f, .875f, .043f, .124f, .223f, .404f, .561f, .630f, .762f, .896f, .376f, .209f, .578f,
.116f, .296f, .452f, .687f, .058f, .179f, .264f, .332f, .413f, .516f, .621f, .833f, .455f, .189f, .571f, .098f, .274f,
.515f, .730f, .056f, .148f, .244f, .354f, .233f, .206f, .682f, .860f, .381f, .230f, .565f, .124f, .315f, .496f, .673f,
.064f, .177f, .282f, .351f, .441f, .528f, .622f, .804f, .146f, .082f, .503f, .044f, .086f, .349f, .607f, .015f, .424f,
.085f, .134f, .262f, .298f, .576f, .836f, .349f, .271f, .466f, .146f, .297f, .413f, .608f, .065f, .200f, .364f, .318f,
.441f, .291f, .531f, .752f, .535f, .000f, .226f, .286f, .000f, .000f, .348f, .026f, .432f, .000f, .000f, .667f, .000f,
.333f, .423f, .500f, .391f, .000f, .073f, .500f, .600f, .000f, .026f, .226f, .455f, .551f, .379f, .984f, .000f, .000f,
.564f, .184f, .762f, .089f, .404f, .630f, .875f, .044f, .141f, .271f, .332f, .614f, .752f, .864f, .896f, .385f, .230f,
.590f, .116f, .332f, .505f, .687f, .059f, .185f, .283f, .506f, .452f, .546f, .626f, .833f, .254f, .161f, .456f, .087f,
.189f, .300f, .571f, .047f, .109f, .179f, .209f, .275f, .375f, .521f, .730f, .395f, .230f, .565f, .112f, .348f, .505f,
.751f, .064f, .177f, .290f, .387f, .441f, .282f, .623f, .848f, .146f, .055f, .503f, .018f, .773f, .316f, .607f, .011f,
.428f, .134f, .887f, .236f, .358f, .576f, .836f, .349f, .271f, .472f, .146f, .364f, .413f, .636f, .065f, .200f, .288f,
.366f, .441f, .398f, .516f, .795f, .592f, .000f, .774f, .282f, .000f, .735f, .042f, .026f, .476f, .000f, .000f, .038f,
.127f, .000f, .138f, .500f, .391f, .000f, .029f, .500f, .522f, .000f, .026f, .226f, .455f, .535f, .260f, .984f, .000f,
.000f, .250f, .155f, .564f, .078f, .198f, .404f, .762f, .043f, .124f, .183f, .223f, .295f, .432f, .630f, .875f, .385f,
.230f, .574f, .116f, .330f, .505f, .728f, .065f, .173f, .283f, .554f, .453f, .546f, .626f, .842f, .285f, .161f, .515f,
.087f, .209f, .441f, .693f, .047f, .109f, .184f, .264f, .334f, .456f, .571f, .811f, .352f, .198f, .555f, .124f, .274f,
.449f, .723f, .076f, .172f, .236f, .318f, .402f, .519f, .622f, .824f, .236f, .146f, .503f, .055f, .147f, .366f, .607f,
.018f, .773f, .146f, .188f, .280f, .499f, .576f, .836f, .329f, .262f, .449f, .163f, .364f, .350f, .588f, .086f, .200f,
.296f, .366f, .331f, .412f, .482f, .784f, .571f, .000f, .774f, .307f, .000f, .234f, .042f, .026f, .406f, .000f, .000f,
.647f, .620f, .000f, .138f, .500f, .400f, .000f, .109f, .068f, .537f, .000f, .026f, .226f, .500f, .447f, .522f, .984f,
.000f, .000f, .268f, .157f, .511f, .078f, .198f, .346f, .696f, .043f, .124f, .183f, .237f, .313f, .458f, .564f, .875f,
.351f, .208f, .571f, .116f, .265f, .460f, .728f, .065f, .164f, .230f, .308f, .412f, .516f, .632f, .842f, .300f, .161f,
.521f, .087f, .209f, .360f, .693f, .047f, .109f, .184f, .276f, .313f, .452f, .571f, .811f, .351f, .196f, .558f, .122f,
.274f, .449f, .723f, .072f, .166f, .236f, .318f, .421f, .520f, .617f, .824f, .190f, .134f, .503f, .051f, .402f, .280f,
.607f, .018f, .773f, .156f, .616f, .389f, .345f, .576f, .836f, .326f, .237f, .466f, .179f, .294f, .350f, .636f, .086f,
.407f, .277f, .338f, .295f, .415f, .562f, .795f, .307f, .026f, .774f, .125f, .000f, .543f, .042f, .500f, .500f, .205f,
.000f, .420f, .321f, .000f, .138f, .447f, .371f, .560f, .026f, .226f, .500f, .000f, .125f, .297f, .187f, .304f, .500f,
.522f, .984f, .000f, .256f, .155f, .564f, .078f, .193f, .314f, .762f, .043f, .124f, .428f, .223f, .258f, .470f, .630f,
.875f, .338f, .193f, .582f, .116f, .264f, .465f, .728f, .065f, .164f, .230f, .296f, .414f, .531f, .652f, .842f, .316f,
.174f, .571f, .095f, .276f, .433f, .730f, .047f, .143f, .209f, .300f, .343f, .521f, .682f, .860f, .343f, .193f, .587f,
.105f, .274f, .438f, .751f, .066f, .166f, .230f, .318f, .381f, .519f, .648f, .848f, .271f, .146f, .503f, .055f, .403f,
.366f, .607f, .018f, .773f, .365f, .445f, .444f, .517f, .576f, .836f, .324f, .237f, .478f, .115f, .287f, .350f, .636f,
.074f, .405f, .387f, .337f, .295f, .431f, .522f, .795f, .307f, .026f, .667f, .125f, .984f, .426f, .000f, .500f, .500f,
.156f, .000f, .500f, .557f, .972f, .000f, .429f, .434f, .000f, .023f, .226f, .560f, .000f, .125f, .222f, .760f, .500f,
.500f, .667f, .000f, .000f, .310f, .157f, .564f, .078f, .256f, .425f, .762f, .043f, .124f, .193f, .258f, .350f, .511f,
.630f, .875f, .337f, .182f, .597f, .101f, .264f, .443f, .728f, .063f, .161f, .216f, .296f, .395f, .522f, .652f, .842f,
.341f, .174f, .516f, .095f, .276f, .431f, .678f, .047f, .143f, .209f, .300f, .391f, .460f, .571f, .811f, .324f, .193f,
.543f, .122f, .254f, .434f, .689f, .066f, .145f, .230f, .431f, .391f, .508f, .597f, .808f, .271f, .146f, .366f, .055f,
.365f, .357f, .503f, .018f, .773f, .249f, .417f, .316f, .444f, .517f, .607f, .308f, .200f, .472f, .115f, .264f, .357f,
.571f, .058f, .138f, .260f, .364f, .338f, .431f, .499f, .752f, .414f, .049f, .557f, .125f, .307f, .438f, .000f, .500f,
.330f, .984f, .333f, .446f, .122f, .014f, .000f, .391f, .333f, .518f, .026f, .174f, .500f, .000f, .125f, .279f, .760f,
.299f, .500f, .408f, .667f, .000f, .310f, .157f, .479f, .078f, .255f, .370f, .564f, .043f, .124f, .193f, .271f, .483f,
.418f, .512f, .762f, .335f, .182f, .597f, .086f, .265f, .423f, .728f, .041f, .129f, .230f, .302f, .375f, .522f, .652f,
.842f, .374f, .184f, .544f, .095f, .300f, .455f, .811f, .047f, .145f, .254f, .341f, .404f, .516f, .569f, .860f, .326f,
.196f, .584f, .118f, .252f, .434f, .751f, .064f, .141f, .236f, .290f, .391f, .508f, .648f, .848f, .316f, .146f, .421f,
.055f, .279f, .454f, .503f, .018f, .773f, .183f, .280f, .347f, .571f, .499f, .605f, .294f, .161f, .499f, .086f, .225f,
.364f, .636f, .058f, .476f, .200f, .264f, .347f, .454f, .571f, .795f, .500f, .520f, .307f, .462f, .571f, .000f, .450f,
.051f, .220f, .557f, .833f, .984f, .000f, .655f, .532f, .355f, .073f, .556f, .026f, .138f, .500f, .000f, .500f, .183f,
.906f, .299f, .410f, .333f, .984f, .000f, .370f, .157f, .559f, .078f, .271f, .483f, .762f, .043f, .124f, .198f, .310f,
.418f, .512f, .752f, .022f, .308f, .161f, .582f, .086f, .230f, .414f, .728f, .042f, .116f, .193f, .281f, .346f, .489f,
.632f, .842f, .455f, .184f, .729f, .095f, .316f, .579f, .811f, .047f, .145f, .276f, .356f, .544f, .666f, .806f, .860f,
.326f, .174f, .584f, .088f, .247f, .435f, .751f, .042f, .141f, .215f, .290f, .368f, .528f, .648f, .848f, .347f, .306f,
.553f, .146f, .364f, .433f, .610f, .055f, .198f, .338f, .451f, .445f, .432f, .096f, .836f, .294f, .161f, .499f, .108f,
.204f, .381f, .636f, .041f, .474f, .198f, .262f, .324f, .362f, .571f, .795f, .500f, .569f, .307f, .101f, .774f, .000f,
.423f, .043f, .465f, .121f, .000f, .984f, .000f, .500f, .524f, .333f, .675f, .560f, .292f, .138f, .429f, .000f, .073f,
.550f, .000f, .195f, .377f, .500f, .984f, .000f, .479f, .183f, .704f, .082f, .310f, .567f, .875f, .043f, .141f, .271f,
.372f, .511f, .630f, .762f, .896f, .325f, .164f, .602f, .086f, .230f, .414f, .761f, .040f, .131f, .197f, .283f, .352f,
.516f, .685f, .855f};
float leaf_vals[num_trees * num_features * 2 * num_tree_nodes] = {
.011f, .029f, .047f, .064f, .075f, .102f, .141f, .172f, .212f, .259f, .308f, .364f, .443f, .497f, .592f, .767f, .069f,
.165f, .241f, .278f, .357f, .412f, .463f, .540f, .562f, .623f, .676f, .734f, .797f, .838f, .894f, .944f, .014f, .040f,
.061f, .033f, .040f, .160f, .181f, .101f, .123f, .047f, .195f, .282f, .374f, .775f, .248f, .068f, .064f, .155f, .177f,
.351f, .409f, .479f, .576f, .451f, .677f, .784f, .817f, .764f, .823f, .860f, .898f, .941f, .154f, .154f, .248f, .248f,
.050f, .081f, .177f, .227f, .252f, .309f, .385f, .428f, .441f, .525f, .616f, .689f, .435f, .137f, .208f, .406f, .457f,
.483f, .518f, .576f, .669f, .844f, .593f, .706f, .853f, .853f, .895f, .925f, .012f, .029f, .047f, .067f, .111f, .134f,
.148f, .178f, .214f, .261f, .311f, .357f, .420f, .476f, .592f, .773f, .057f, .143f, .194f, .262f, .358f, .415f, .465f,
.541f, .602f, .649f, .655f, .739f, .808f, .849f, .894f, .944f, .050f, .068f, .089f, .118f, .146f, .187f, .211f, .230f,
.263f, .308f, .364f, .443f, .497f, .581f, .690f, .832f, .079f, .171f, .263f, .306f, .356f, .401f, .452f, .486f, .538f,
.577f, .629f, .687f, .722f, .766f, .834f, .900f, .046f, .066f, .083f, .064f, .090f, .113f, .143f, .235f, .289f, .416f,
.094f, .204f, .454f, .074f, .697f, .836f, .067f, .156f, .200f, .332f, .266f, .411f, .473f, .514f, .627f, .575f, .758f,
.676f, .775f, .826f, .864f, .900f, .162f, .162f, .248f, .248f, .079f, .102f, .165f, .241f, .281f, .337f, .385f, .428f,
.441f, .525f, .616f, .689f, .397f, .137f, .166f, .307f, .421f, .443f, .525f, .486f, .527f, .585f, .687f, .611f, .767f,
.821f, .942f, .916f, .055f, .073f, .090f, .110f, .165f, .188f, .207f, .225f, .261f, .312f, .358f, .420f, .475f, .579f,
.693f, .875f, .079f, .164f, .238f, .277f, .325f, .378f, .448f, .487f, .527f, .557f, .610f, .648f, .716f, .769f, .830f,
.896f, .038f, .090f, .112f, .131f, .206f, .160f, .224f, .249f, .286f, .334f, .370f, .443f, .497f, .581f, .690f, .832f,
.056f, .153f, .221f, .278f, .311f, .365f, .420f, .463f, .524f, .562f, .625f, .699f, .696f, .762f, .829f, .889f, .024f,
.093f, .104f, .119f, .104f, .154f, .153f, .216f, .273f, .376f, .202f, .138f, .609f, .690f, .814f, .930f, .027f, .098f,
.158f, .252f, .304f, .393f, .706f, .462f, .630f, .554f, .845f, .643f, .852f, .694f, .781f, .858f, .169f, .169f, .248f,
.248f, .105f, .124f, .110f, .197f, .308f, .242f, .385f, .428f, .441f, .525f, .616f, .689f, .375f, .137f, .146f, .314f,
.412f, .437f, .454f, .520f, .510f, .615f, .692f, .576f, .701f, .701f, .780f, .846f, .039f, .091f, .109f, .125f, .209f,
.256f, .251f, .126f, .295f, .350f, .420f, .475f, .568f, .625f, .738f, .875f, .055f, .153f, .236f, .281f, .338f, .390f,
.425f, .462f, .522f, .563f, .609f, .687f, .674f, .721f, .776f, .846f, .034f, .078f, .123f, .148f, .201f, .153f, .215f,
.253f, .239f, .335f, .382f, .446f, .502f, .581f, .690f, .832f, .063f, .145f, .220f, .284f, .340f, .386f, .424f, .467f,
.520f, .550f, .611f, .671f, .718f, .758f, .792f, .854f, .065f, .117f, .138f, .163f, .225f, .371f, .188f, .145f, .457f,
.345f, .102f, .276f, .609f, .690f, .814f, .930f, .032f, .133f, .188f, .247f, .268f, .350f, .427f, .495f, .538f, .578f,
.641f, .835f, .700f, .759f, .780f, .868f, .187f, .187f, .135f, .170f, .218f, .144f, .261f, .340f, .416f, .335f, .388f,
.428f, .441f, .525f, .616f, .689f, .367f, .273f, .143f, .308f, .382f, .439f, .410f, .470f, .524f, .461f, .626f, .528f,
.583f, .702f, .673f, .773f, .031f, .068f, .124f, .154f, .217f, .154f, .255f, .302f, .358f, .405f, .435f, .475f, .568f,
.625f, .738f, .875f, .061f, .144f, .221f, .261f, .325f, .366f, .448f, .495f, .538f, .590f, .618f, .659f, .686f, .739f,
.791f, .858f, .034f, .079f, .149f, .175f, .198f, .231f, .249f, .327f, .353f, .382f, .443f, .489f, .570f, .649f, .740f,
.882f, .076f, .148f, .218f, .296f, .357f, .400f, .444f, .472f, .516f, .554f, .597f, .630f, .678f, .722f, .781f, .864f,
.021f, .055f, .135f, .053f, .180f, .150f, .370f, .214f, .331f, .530f, .219f, .326f, .609f, .690f, .814f, .930f, .049f,
.095f, .149f, .216f, .370f, .294f, .443f, .489f, .526f, .594f, .621f, .747f, .656f, .762f, .780f, .884f, .216f, .248f,
.160f, .190f, .197f, .356f, .296f, .341f, .391f, .428f, .441f, .525f, .593f, .668f, .760f, .637f, .388f, .250f, .155f,
.334f, .419f, .456f, .497f, .448f, .591f, .542f, .552f, .719f, .656f, .709f, .849f, .897f, .034f, .078f, .151f, .184f,
.211f, .253f, .262f, .351f, .358f, .405f, .435f, .475f, .568f, .625f, .738f, .875f, .076f, .148f, .229f, .303f, .341f,
.376f, .444f, .480f, .548f, .510f, .594f, .638f, .685f, .742f, .800f, .882f, .028f, .062f, .089f, .114f, .174f, .196f,
.241f, .294f, .335f, .371f, .443f, .482f, .511f, .590f, .714f, .832f, .075f, .157f, .223f, .281f, .342f, .386f, .450f,
.489f, .542f, .590f, .611f, .653f, .682f, .728f, .783f, .893f, .041f, .076f, .186f, .109f, .175f, .195f, .209f, .227f,
.274f, .355f, .196f, .314f, .609f, .690f, .814f, .930f, .049f, .097f, .161f, .221f, .415f, .304f, .454f, .492f, .527f,
.581f, .629f, .747f, .685f, .758f, .836f, .914f, .225f, .248f, .187f, .074f, .228f, .365f, .295f, .337f, .391f, .428f,
.441f, .525f, .593f, .668f, .760f, .637f, .413f, .277f, .431f, .456f, .115f, .162f, .254f, .334f, .503f, .661f, .515f,
.515f, .696f, .751f, .836f, .897f, .023f, .057f, .090f, .116f, .180f, .197f, .239f, .283f, .338f, .365f, .420f, .475f,
.568f, .625f, .738f, .875f, .074f, .157f, .227f, .282f, .365f, .410f, .451f, .504f, .577f, .610f, .646f, .679f, .728f,
.782f, .855f, .923f, .028f, .062f, .089f, .120f, .165f, .204f, .243f, .304f, .335f, .371f, .443f, .482f, .511f, .590f,
.714f, .832f, .073f, .157f, .220f, .287f, .343f, .393f, .451f, .489f, .567f, .596f, .616f, .650f, .711f, .760f, .840f,
.917f, .041f, .076f, .186f, .092f, .203f, .116f, .222f, .261f, .330f, .438f, .214f, .316f, .609f, .690f, .814f, .930f,
.049f, .104f, .163f, .221f, .414f, .448f, .513f, .561f, .566f, .744f, .614f, .683f, .721f, .761f, .854f, .915f, .228f,
.248f, .196f, .096f, .300f, .225f, .295f, .344f, .466f, .385f, .403f, .468f, .441f, .525f, .616f, .689f, .414f, .307f,
.445f, .460f, .115f, .162f, .254f, .334f, .459f, .495f, .501f, .705f, .680f, .751f, .836f, .897f, .031f, .065f, .100f,
.132f, .201f, .221f, .280f, .333f, .374f, .405f, .435f, .475f, .568f, .625f, .738f, .875f, .073f, .157f, .226f, .288f,
.349f, .401f, .450f, .489f, .589f, .621f, .649f, .680f, .718f, .759f, .843f, .923f, .029f, .067f, .107f, .140f, .207f,
.227f, .279f, .339f, .369f, .393f, .444f, .494f, .575f, .651f, .740f, .882f, .042f, .093f, .147f, .184f, .220f, .256f,
.290f, .323f, .402f, .455f, .495f, .540f, .619f, .687f, .748f, .876f, .021f, .055f, .098f, .053f, .206f, .221f, .389f,
.239f, .343f, .438f, .228f, .316f, .609f, .690f, .814f, .930f, .049f, .104f, .160f, .221f, .235f, .426f, .455f, .529f,
.623f, .551f, .600f, .677f, .697f, .760f, .836f, .914f, .232f, .201f, .231f, .309f, .117f, .096f, .070f, .044f, .466f,
.385f, .403f, .468f, .441f, .525f, .616f, .689f, .418f, .251f, .450f, .394f, .115f, .162f, .254f, .334f, .460f, .488f,
.494f, .703f, .680f, .751f, .836f, .897f, .031f, .065f, .100f, .132f, .207f, .229f, .289f, .342f, .435f, .346f, .461f,
.482f, .568f, .625f, .738f, .875f, .043f, .093f, .146f, .180f, .241f, .278f, .307f, .330f, .391f, .451f, .472f, .524f,
.610f, .651f, .741f, .874f, .029f, .067f, .107f, .140f, .212f, .233f, .269f, .343f, .369f, .393f, .444f, .494f, .575f,
.651f, .740f, .882f, .042f, .093f, .151f, .188f, .238f, .271f, .293f, .321f, .408f, .459f, .513f, .553f, .609f, .672f,
.777f, .893f, .021f, .055f, .098f, .053f, .210f, .226f, .355f, .247f, .439f, .514f, .637f, .836f, .333f, .420f, .227f,
.313f, .019f, .060f, .098f, .133f, .147f, .179f, .237f, .125f, .196f, .407f, .451f, .477f, .572f, .654f, .774f, .903f,
.239f, .375f, .204f, .250f, .150f, .150f, .096f, .057f, .426f, .383f, .403f, .468f, .441f, .525f, .616f, .689f, .407f,
.407f, .126f, .244f, .134f, .203f, .294f, .406f, .449f, .469f, .573f, .482f, .751f, .751f, .836f, .897f, .031f, .065f,
.100f, .132f, .212f, .232f, .281f, .348f, .435f, .346f, .461f, .482f, .568f, .625f, .738f, .875f, .043f, .093f, .152f,
.190f, .235f, .262f, .295f, .330f, .354f, .417f, .455f, .492f, .620f, .685f, .768f, .888f, .029f, .067f, .107f, .140f,
.167f, .219f, .238f, .298f, .352f, .382f, .443f, .485f, .532f, .596f, .714f, .832f, .056f, .105f, .161f, .195f, .230f,
.267f, .289f, .322f, .367f, .414f, .462f, .529f, .579f, .667f, .742f, .875f, .021f, .053f, .094f, .052f, .214f, .235f,
.288f, .235f, .451f, .530f, .632f, .826f, .316f, .233f, .466f, .356f, .019f, .060f, .084f, .110f, .192f, .162f, .235f,
.287f, .418f, .363f, .447f, .482f, .573f, .631f, .724f, .880f, .243f, .248f, .210f, .074f, .237f, .308f, .378f, .334f,
.391f, .428f, .441f, .525f, .593f, .668f, .760f, .637f, .398f, .398f, .235f, .418f, .105f, .166f, .287f, .405f, .458f,
.482f, .589f, .488f, .630f, .630f, .751f, .866f, .031f, .065f, .100f, .132f, .218f, .235f, .269f, .344f, .400f, .435f,
.478f, .396f, .568f, .625f, .738f, .875f, .056f, .106f, .160f, .190f, .215f, .248f, .292f, .331f, .383f, .415f, .459f,
.503f, .594f, .678f, .783f, .898f, .029f, .067f, .108f, .144f, .226f, .241f, .293f, .353f, .275f, .384f, .446f, .502f,
.579f, .651f, .740f, .882f, .038f, .077f, .112f, .161f, .202f, .241f, .289f, .323f, .362f, .410f, .462f, .515f, .582f,
.658f, .727f, .868f, .021f, .053f, .094f, .052f, .227f, .249f, .316f, .237f, .483f, .630f, .726f, .836f, .583f, .493f,
.274f, .426f, .034f, .080f, .109f, .146f, .210f, .181f, .285f, .223f, .385f, .436f, .469f, .544f, .576f, .619f, .714f,
.880f, .250f, .248f, .218f, .074f, .241f, .293f, .378f, .334f, .408f, .522f, .409f, .317f, .547f, .397f, .616f, .689f,
.410f, .308f, .440f, .469f, .111f, .160f, .250f, .328f, .516f, .674f, .506f, .506f, .685f, .751f, .836f, .897f, .031f,
.065f, .100f, .132f, .229f, .267f, .359f, .244f, .442f, .346f, .461f, .482f, .568f, .625f, .738f, .875f, .056f, .106f,
.160f, .190f, .219f, .255f, .302f, .340f, .392f, .421f, .463f, .496f, .578f, .642f, .717f, .869f, .029f, .067f, .108f,
.144f, .223f, .250f, .318f, .362f, .400f, .444f, .476f, .508f, .579f, .651f, .740f, .882f, .032f, .096f, .155f, .192f,
.227f, .255f, .306f, .349f, .381f, .418f, .464f, .519f, .589f, .653f, .721f, .867f, .018f, .049f, .037f, .080f, .201f,
.248f, .091f, .152f, .229f, .253f, .323f, .259f, .632f, .826f, .274f, .428f, .028f, .096f, .165f, .230f, .434f, .361f,
.449f, .500f, .554f, .596f, .610f, .679f, .678f, .743f, .801f, .903f, .260f, .248f, .208f, .243f, .259f, .302f, .414f,
.315f, .408f, .522f, .409f, .317f, .535f, .620f, .357f, .692f, .405f, .266f, .432f, .463f, .111f, .170f, .250f, .328f,
.535f, .656f, .525f, .525f, .693f, .751f, .836f, .897f, .031f, .065f, .100f, .132f, .211f, .249f, .320f, .372f, .478f,
.396f, .568f, .608f, .647f, .738f, .849f, .902f, .032f, .095f, .153f, .190f, .237f, .269f, .305f, .344f, .390f, .423f,
.465f, .514f, .581f, .637f, .718f, .869f, .033f, .072f, .110f, .152f, .214f, .250f, .273f, .316f, .419f, .449f, .476f,
.508f, .579f, .651f, .740f, .882f, .039f, .095f, .144f, .185f, .250f, .296f, .323f, .362f, .416f, .467f, .502f, .531f,
.589f, .643f, .714f, .867f, .018f, .049f, .036f, .079f, .095f, .246f, .091f, .131f, .233f, .268f, .342f, .294f, .609f,
.690f, .814f, .930f, .037f, .093f, .146f, .175f, .270f, .226f, .408f, .339f, .448f, .303f, .472f, .506f, .580f, .640f,
.726f, .880f, .273f, .235f, .283f, .319f, .117f, .096f, .070f, .044f, .475f, .609f, .357f, .692f, .414f, .278f, .536f,
.462f, .374f, .229f, .139f, .344f, .414f, .441f, .505f, .402f, .496f, .572f, .606f, .526f, .680f, .751f, .836f, .897f,
.031f, .072f, .110f, .157f, .239f, .277f, .437f, .352f, .565f, .578f, .631f, .514f, .748f, .578f, .849f, .902f, .039f,
.095f, .152f, .202f, .252f, .305f, .345f, .432f, .423f, .467f, .509f, .544f, .592f, .640f, .713f, .869f, .028f, .062f,
.089f, .120f, .152f, .173f, .191f, .211f, .252f, .277f, .302f, .324f, .446f, .502f, .592f, .767f, .043f, .090f, .136f,
.191f, .256f, .311f, .359f, .390f, .424f, .470f, .492f, .534f, .593f, .655f, .776f, .893f, .012f, .032f, .021f, .058f,
.093f, .135f, .059f, .026f, .228f, .270f, .292f, .324f, .609f, .690f, .814f, .930f, .042f, .097f, .141f, .176f, .218f,
.342f, .143f, .270f, .446f, .303f, .480f, .516f, .580f, .627f, .774f, .903f, .292f, .238f, .299f, .331f, .117f, .096f,
.070f, .044f, .430f, .536f, .612f, .347f, .593f, .668f, .760f, .637f, .386f, .214f, .133f, .342f, .405f, .444f, .507f,
.442f, .464f, .479f, .565f, .517f, .680f, .751f, .836f, .897f, .031f, .065f, .100f, .131f, .165f, .188f, .204f, .222f,
.275f, .303f, .336f, .383f, .568f, .625f, .738f, .875f, .046f, .101f, .141f, .193f, .256f, .302f, .345f, .451f, .425f,
.468f, .509f, .535f, .586f, .649f, .744f, .874f, .028f, .062f, .089f, .120f, .155f, .189f, .214f, .247f, .310f, .338f,
.392f, .444f, .497f, .581f, .690f, .832f, .049f, .101f, .142f, .181f, .211f, .247f, .287f, .325f, .377f, .426f, .473f,
.530f, .587f, .645f, .745f, .875f, .021f, .055f, .098f, .053f, .280f, .306f, .168f, .226f, .257f, .314f, .351f, .309f,
.609f, .690f, .814f, .930f, .048f, .102f, .140f, .185f, .274f, .321f, .143f, .250f, .443f, .359f, .443f, .483f, .542f,
.609f, .746f, .894f, .317f, .252f, .324f, .348f, .117f, .096f, .070f, .044f, .594f, .404f, .499f, .531f, .593f, .668f,
.760f, .637f, .402f, .260f, .124f, .345f, .382f, .444f, .423f, .448f, .473f, .511f, .563f, .516f, .680f, .751f, .836f,
.897f, .031f, .065f, .100f, .132f, .166f, .188f, .207f, .225f, .301f, .322f, .341f, .404f, .475f, .579f, .693f, .875f,
.048f, .100f, .142f, .187f, .216f, .244f, .283f, .318f, .378f, .430f, .473f, .525f, .586f, .642f, .744f, .874f, .028f,
.062f, .089f, .120f, .155f, .189f, .215f, .249f, .274f, .345f, .363f, .409f, .502f, .581f, .690f, .832f, .053f, .095f,
.132f, .175f, .211f, .243f, .288f, .326f, .399f, .434f, .474f, .527f, .579f, .637f, .745f, .875f, .021f, .053f, .092f,
.052f, .054f, .108f, .180f, .116f, .204f, .271f, .321f, .362f, .609f, .690f, .814f, .930f, .054f, .105f, .177f, .148f,
.216f, .260f, .394f, .301f, .226f, .381f, .443f, .484f, .588f, .680f, .774f, .903f, .339f, .339f, .248f, .248f, .222f,
.288f, .111f, .057f, .352f, .366f, .422f, .505f, .593f, .668f, .760f, .637f, .387f, .016f, .160f, .380f, .113f, .221f,
.346f, .410f, .442f, .482f, .474f, .496f, .563f, .508f, .700f, .866f, .031f, .065f, .100f, .131f, .148f, .177f, .202f,
.218f, .338f, .280f, .357f, .420f, .568f, .625f, .738f, .875f, .053f, .098f, .135f, .175f, .212f, .242f, .277f, .307f,
.384f, .434f, .476f, .536f, .594f, .644f, .744f, .874f, .029f, .067f, .107f, .140f, .184f, .215f, .249f, .274f, .335f,
.376f, .406f, .479f, .579f, .651f, .740f, .882f, .056f, .085f, .129f, .177f, .209f, .238f, .289f, .323f, .361f, .413f,
.471f, .539f, .601f, .677f, .776f, .893f, .021f, .055f, .098f, .053f, .138f, .211f, .281f, .183f, .345f, .232f, .387f,
.290f, .609f, .690f, .814f, .930f, .057f, .083f, .167f, .124f, .232f, .182f, .401f, .293f, .226f, .358f, .445f, .497f,
.581f, .654f, .774f, .903f, .353f, .353f, .248f, .248f, .112f, .240f, .290f, .096f, .354f, .374f, .393f, .433f, .595f,
.468f, .648f, .692f, .378f, .016f, .106f, .339f, .119f, .191f, .327f, .397f, .446f, .477f, .512f, .549f, .680f, .751f,
.836f, .897f, .031f, .065f, .100f, .132f, .172f, .211f, .338f, .261f, .349f, .375f, .402f, .437f, .568f, .625f, .738f,
.875f, .057f, .086f, .129f, .173f, .199f, .232f, .280f, .306f, .366f, .419f, .471f, .531f, .592f, .652f, .744f, .874f,
.029f, .067f, .107f, .140f, .184f, .215f, .249f, .286f, .371f, .396f, .420f, .443f, .477f, .533f, .682f, .832f, .047f,
.094f, .134f, .165f, .205f, .237f, .277f, .306f, .366f, .411f, .466f, .519f, .558f, .619f, .727f, .868f, .021f, .055f,
.098f, .053f, .115f, .175f, .238f, .195f, .305f, .390f, .332f, .232f, .424f, .243f, .626f, .826f, .030f, .071f, .108f,
.138f, .195f, .114f, .295f, .240f, .320f, .362f, .424f, .489f, .542f, .613f, .730f, .880f, .385f, .385f, .248f, .375f,
.172f, .262f, .393f, .347f, .364f, .416f, .412f, .433f, .448f, .486f, .648f, .533f, .354f, .016f, .183f, .308f, .111f,
.177f, .269f, .346f, .414f, .440f, .454f, .520f, .507f, .544f, .700f, .866f, .031f, .065f, .100f, .132f, .172f, .210f,
.232f, .261f, .323f, .361f, .390f, .418f, .444f, .468f, .548f, .773f, .030f, .072f, .110f, .153f, .205f, .246f, .283f,
.312f, .355f, .399f, .461f, .532f, .594f, .652f, .744f, .874f, .029f, .067f, .108f, .147f, .206f, .237f, .286f, .334f,
.358f, .424f, .461f, .494f, .522f, .565f, .791f, .882f, .039f, .091f, .132f, .168f, .214f, .244f, .278f, .311f, .373f,
.413f, .465f, .521f, .596f, .677f, .776f, .893f, .021f, .055f, .098f, .053f, .166f, .219f, .411f, .273f, .379f, .461f,
.256f, .206f, .501f, .264f, .587f, .816f, .031f, .066f, .104f, .139f, .154f, .187f, .203f, .241f, .318f, .360f, .414f,
.493f, .621f, .692f, .775f, .903f, .429f, .223f, .430f, .462f, .480f, .504f, .532f, .555f, .170f, .266f, .111f, .057f,
.327f, .407f, .441f, .475f, .307f, .307f, .126f, .257f, .091f, .171f, .253f, .332f, .390f, .420f, .457f, .488f, .573f,
.503f, .700f, .866f, .031f, .065f, .100f, .132f, .176f, .215f, .261f, .311f, .386f, .439f, .466f, .489f, .562f, .514f,
.612f, .773f, .033f, .073f, .102f, .146f, .177f, .216f, .255f, .295f, .323f, .388f, .450f, .502f, .580f, .644f, .744f,
.874f, .029f, .067f, .108f, .147f, .210f, .259f, .308f, .364f, .503f, .543f, .584f, .646f, .723f, .578f, .791f, .882f,
.028f, .068f, .122f, .163f, .194f, .231f, .273f, .310f, .357f, .403f, .464f, .533f, .596f, .677f, .776f, .893f, .048f,
.094f, .177f, .218f, .307f, .432f, .273f, .229f, .500f, .253f, .603f, .513f, .754f, .673f, .825f, .930f, .018f, .062f,
.102f, .129f, .142f, .173f, .188f, .226f, .391f, .313f, .471f, .402f, .621f, .692f, .775f, .903f, .489f, .443f, .207f,
.494f, .541f, .577f, .648f, .720f, .175f, .237f, .111f, .057f, .287f, .335f, .409f, .374f, .264f, .187f, .285f, .318f,
.156f, .106f, .341f, .251f, .380f, .391f, .432f, .475f, .584f, .513f, .700f, .866f, .031f, .068f, .108f, .157f, .212f,
.261f, .312f, .371f, .483f, .517f, .571f, .611f, .665f, .738f, .849f, .902f, .028f, .068f, .120f, .154f, .188f, .219f,
.259f, .305f, .338f, .387f, .454f, .520f, .609f, .691f, .768f, .888f};
...@@ -81,7 +81,13 @@ namespace cvtest { ...@@ -81,7 +81,13 @@ namespace cvtest {
Mat currentResult; Mat currentResult;
xphoto::autowbGrayworld(src, currentResult, wb_thresh); xphoto::autowbGrayworld(src, currentResult, wb_thresh);
ASSERT_LE(cv::norm(currentResult, referenceResult, NORM_INF), acc_thresh);
// test the 16-bit depth:
Mat currentResult_16U, src_16U;
src.convertTo(src_16U, CV_16UC3, 256.0);
xphoto::autowbGrayworld(src_16U, currentResult_16U, wb_thresh);
currentResult_16U.convertTo(currentResult, CV_8UC3, 1/256.0);
ASSERT_LE(cv::norm(currentResult, referenceResult, NORM_INF), acc_thresh); ASSERT_LE(cv::norm(currentResult, referenceResult, NORM_INF), acc_thresh);
} }
} }
......
#include "test_precomp.hpp"
using namespace cv;
namespace cvtest
{
TEST(xphoto_simplefeatures, regression)
{
float acc_thresh = 0.01f;
// Generate a test image:
Mat test_im(1000, 1000, CV_8UC3);
RNG rng(1234);
rng.fill(test_im, RNG::NORMAL, Scalar(64, 100, 128), Scalar(10, 10, 10));
threshold(test_im, test_im, 200.0, 255.0, THRESH_TRUNC);
test_im.at<Vec3b>(0, 0) = Vec3b(240, 220, 200);
// Which should have the following features:
Vec2f ref1(128.0f / (64 + 100 + 128), 100.0f / (64 + 100 + 128));
Vec2f ref2(200.0f / (240 + 220 + 200), 220.0f / (240 + 220 + 200));
vector<Vec2f> dst_features;
xphoto::extractSimpleFeatures(test_im, dst_features, 255, 0.98f, 64);
ASSERT_LE(cv::norm(dst_features[0], ref1, NORM_INF), acc_thresh);
ASSERT_LE(cv::norm(dst_features[1], ref2, NORM_INF), acc_thresh);
ASSERT_LE(cv::norm(dst_features[2], ref1, NORM_INF), acc_thresh);
ASSERT_LE(cv::norm(dst_features[3], ref1, NORM_INF), acc_thresh);
// check 16 bit depth:
test_im.convertTo(test_im, CV_16U, 256.0);
xphoto::extractSimpleFeatures(test_im, dst_features, 65535, 0.98f, 64);
ASSERT_LE(cv::norm(dst_features[0], ref1, NORM_INF), acc_thresh);
ASSERT_LE(cv::norm(dst_features[1], ref2, NORM_INF), acc_thresh);
ASSERT_LE(cv::norm(dst_features[2], ref1, NORM_INF), acc_thresh);
ASSERT_LE(cv::norm(dst_features[3], ref1, NORM_INF), acc_thresh);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment