protected void MatchFeatures(
            int y,
            List <float> left_row_features,
            List <float> right_row_features,
            float calibration_offset_x,
            float calibration_offset_y,
            byte[] left_bmp, byte[] right_bmp)
        {
            int max_disparity_pixels = image_width * max_disparity / 100;

            List <possible_match>[] match_probabilities_left  = new List <possible_match> [left_row_features.Count / 3];
            List <possible_match>[] match_probabilities_right = new List <possible_match> [right_row_features.Count / 3];

            // get matching probabilities
            int ii = 0;

            for (int i = 0; i < left_row_features.Count; i += 3, ii++)
            {
                int   jj             = 0;
                float x_left         = left_row_features[i];
                int   direction_left = (int)left_row_features[i + 2];
                for (int j = 0; j < right_row_features.Count; j += 3, jj++)
                {
                    float x_right = right_row_features[j];

                    float disparity = x_left - x_right + calibration_offset_x;
                    //float disparity = x_right - x_left - calibration_offset_x;
                    if ((disparity >= 0) && (disparity < max_disparity_pixels))
                    {
                        int direction_right = (int)right_row_features[j + 2];
                        if (direction_left == direction_right)
                        {
                            int n1 = (y * image_width) + (int)x_left;
                            int n2 = ((y + (int)calibration_offset_y) * image_width) + (int)x_right;

                            int v = similarity(n1, n2, (int)disparity, left_bmp, right_bmp);
                            if ((v > 0) && (v < matching_threshold))
                            {
                                possible_match match = new possible_match();
                                match.feature_index_left  = ii;
                                match.feature_index_right = jj;
                                match.ssd = v;
                                match.probability_left  = v;
                                match.probability_right = v;
                                match.disparity         = disparity;

                                if (match_probabilities_left[ii] == null)
                                {
                                    match_probabilities_left[ii] = new List <possible_match>();
                                }
                                match_probabilities_left[ii].Add(match);

                                if (match_probabilities_right[jj] == null)
                                {
                                    match_probabilities_right[jj] = new List <possible_match>();
                                }
                                match_probabilities_right[jj].Add(match);
                            }
                        }
                    }
                }
            }

            // update probabilities for left to right search
            for (int i = 0; i < match_probabilities_left.Length; i++)
            {
                if (match_probabilities_left[i] != null)
                {
                    // total SSD
                    float tot_ssd = 0;
                    for (int j = match_probabilities_left[i].Count - 1; j >= 0; j--)
                    {
                        tot_ssd += match_probabilities_left[i][j].probability_left;
                    }

                    // convert SSD values to probabilities
                    if (tot_ssd > 0)
                    {
                        for (int j = match_probabilities_left[i].Count - 1; j >= 0; j--)
                        {
                            match_probabilities_left[i][j].probability_left = 1.0f - (match_probabilities_left[i][j].probability_left / tot_ssd);
                        }
                    }
                }
            }

            // update probabilities for right to left search
            for (int i = 0; i < match_probabilities_right.Length; i++)
            {
                if (match_probabilities_right[i] != null)
                {
                    // total SSD
                    float tot_ssd = 0;
                    for (int j = match_probabilities_right[i].Count - 1; j >= 0; j--)
                    {
                        tot_ssd += match_probabilities_right[i][j].probability_right;
                    }

                    // convert SSD values to probabilities
                    if (tot_ssd > 0)
                    {
                        for (int j = match_probabilities_right[i].Count - 1; j >= 0; j--)
                        {
                            match_probabilities_right[i][j].probability_right = 1.0f - (match_probabilities_right[i][j].probability_right / tot_ssd);
                        }
                    }
                }
            }

            int   hits          = 0;
            float average_score = 0;

            // combine probabilities from left to right and right to left search
            float[] prob = new float[match_probabilities_left.Length];
            float[] disp = new float[match_probabilities_left.Length];
            for (int i = 0; i < match_probabilities_left.Length; i++)
            {
                if (match_probabilities_left[i] != null)
                {
                    float best_score = matching_threshold;
                    float disparity  = 0;
                    for (int j = match_probabilities_left[i].Count - 1; j >= 0; j--)
                    {
                        possible_match match       = match_probabilities_left[i][j];
                        float          probability = match.probability_left * match.probability_right;

                        float score = match.ssd * (1.0f - probability);
                        if ((score > 0) && (score < best_score))
                        {
                            best_score = score;
                            disparity  = match.disparity;
                        }
                    }
                    prob[i]        = best_score;
                    disp[i]        = disparity;
                    average_score += best_score;
                    hits++;
                }
            }

            if (hits > 0)
            {
                average_score /= hits;
                float threshold = average_score * similarity_threshold_percent / 100;
                for (int i = 0; i < prob.Length; i++)
                {
                    if ((prob[i] < threshold) && (disp[i] > 0))
                    {
                        float x_left    = left_row_features[i * 3];
                        float disparity = disp[i];
                        features.Add(new StereoFeature(x_left, y, disparity));
                    }
                }
            }
        }
        protected void MatchFeatures(
            int y, 
            List<float> left_row_features, 
            List<float> right_row_features,
            float calibration_offset_x,
            float calibration_offset_y,
            byte[] left_bmp, byte[] right_bmp)
        {
            int max_disparity_pixels = image_width * max_disparity / 100;
            
            List<possible_match>[] match_probabilities_left = new List<possible_match>[left_row_features.Count/3];
            List<possible_match>[] match_probabilities_right = new List<possible_match>[right_row_features.Count/3];
            
            // get matching probabilities
            int ii = 0;
            for (int i = 0; i < left_row_features.Count; i += 3, ii++)
            {
                int jj = 0;
                float x_left = left_row_features[i];
				int direction_left = (int)left_row_features[i + 2];
                for (int j = 0; j < right_row_features.Count; j += 3, jj++)
                {
                    float x_right = right_row_features[j];

                    float disparity = x_left - x_right + calibration_offset_x;
					//float disparity = x_right - x_left - calibration_offset_x;
                    if ((disparity >= 0) && (disparity < max_disparity_pixels))
                    {
                        int direction_right = (int)right_row_features[j + 2];
						if (direction_left == direction_right)
						{						
	                        int n1 = (y * image_width) + (int)x_left;
	                        int n2 = ((y+(int)calibration_offset_y) * image_width) + (int)x_right;
	                        
	                        int v = similarity(n1, n2, (int)disparity, left_bmp, right_bmp);
	                        if ((v > 0) && (v < matching_threshold))
	                        {
                                possible_match match =  new possible_match();
                                match.feature_index_left = ii;
                                match.feature_index_right = jj;
                                match.ssd = v;
                                match.probability_left = v;
                                match.probability_right = v;
                                match.disparity = disparity;

	                            if (match_probabilities_left[ii] == null)
	                                match_probabilities_left[ii] = new List<possible_match>();
                                match_probabilities_left[ii].Add(match);

	                            if (match_probabilities_right[jj] == null)
	                                match_probabilities_right[jj] = new List<possible_match>();
                                match_probabilities_right[jj].Add(match);
	                        }
						}
                    }
                }
            }

            // update probabilities for left to right search
            for (int i = 0; i < match_probabilities_left.Length; i++)
            {
                if (match_probabilities_left[i] != null)
                {
                    // total SSD
                    float tot_ssd = 0;
                    for (int j = match_probabilities_left[i].Count - 1; j >= 0; j--)
                        tot_ssd += match_probabilities_left[i][j].probability_left;

                    // convert SSD values to probabilities
                    if (tot_ssd > 0)
                        for (int j = match_probabilities_left[i].Count - 1; j >= 0; j--)
                            match_probabilities_left[i][j].probability_left = 1.0f - (match_probabilities_left[i][j].probability_left / tot_ssd);
                }
            }
            
            // update probabilities for right to left search
            for (int i = 0; i < match_probabilities_right.Length; i++)
            {
                if (match_probabilities_right[i] != null)
                {
                    // total SSD
                    float tot_ssd = 0;
                    for (int j = match_probabilities_right[i].Count - 1; j >= 0; j--)
                        tot_ssd += match_probabilities_right[i][j].probability_right;

                    // convert SSD values to probabilities
                    if (tot_ssd > 0)
                        for (int j = match_probabilities_right[i].Count - 1; j >= 0; j--)
                            match_probabilities_right[i][j].probability_right = 1.0f - (match_probabilities_right[i][j].probability_right / tot_ssd);
                }
            }
            
            int hits = 0;
            float average_score = 0;            

            // combine probabilities from left to right and right to left search
            float[] prob  = new float[match_probabilities_left.Length];
            float[] disp  = new float[match_probabilities_left.Length];
            for (int i = 0; i < match_probabilities_left.Length; i++)
            {
                if (match_probabilities_left[i] != null)
                {
                    float best_score = matching_threshold;
                    float disparity= 0;
                    for (int j = match_probabilities_left[i].Count - 1; j >= 0; j--)
                    {
                        possible_match match = match_probabilities_left[i][j];
                        float probability = match.probability_left * match.probability_right;

                            float score = match.ssd * (1.0f - probability);
                            if ((score > 0) && (score < best_score))
                            {
                                best_score = score;
                                disparity = match.disparity;
                            }
                        
                    }
                    prob[i] = best_score;
                    disp[i] = disparity;
                    average_score += best_score;
                    hits++;                        
                }
            }
            
            if (hits > 0)
            {            
                average_score /= hits;
                float threshold = average_score * similarity_threshold_percent / 100;
                for (int i = 0; i < prob.Length; i++)
                {
                    if ((prob[i] < threshold) && (disp[i] > 0))
                    {
                        float x_left = left_row_features[i*3];
                        float disparity = disp[i];
                        features.Add(new StereoFeature(x_left, y, disparity));
                    }
                }
            }
        }