Commit 678c93b8 authored by Daniel Rubery's avatar Daniel Rubery Committed by Commit Bot

Import OpenCV EMD implementation into Chrome

This CL imports the portion of OpenCV needed to use its EMD
implementation. This API will be fuzzed (see
https://crrev.com/c/2166582), and is intended to run only in the
renderer process.

Bug: 1068616
Change-Id: I16cd87b493290dc83f85c6b78a1c02b89ae53b90
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2163432Reviewed-by: default avatarMustafa Emre Acer <meacer@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Reviewed-by: default avatarBettina Dea <bdea@chromium.org>
Reviewed-by: default avatarVarun Khaneja <vakh@chromium.org>
Commit-Queue: Daniel Rubery <drubery@chromium.org>
Cr-Commit-Position: refs/heads/master@{#766428}
parent 6d42d6ea
config("opencv_warnings") {
cflags = [ "-Wno-unused-function" ]
}
source_set("emd") {
sources = [
"src/emd.cpp",
"src/emd_wrapper.h",
]
defines = [ "CHROMIUM_OPENCV" ]
configs -= [ "//build/config/compiler:chromium_code" ]
configs += [
"//build/config/compiler:no_chromium_code",
":opencv_warnings",
]
deps = [
"//base",
]
}
include_rules = [
'+base',
]
bdea@chromium.org
drubery@chromium.org
vakh@chromium.org
# COMPONENT: Services>Safebrowsing
# TEAM: safebrowsing@chromium.org
Name: OpenCV
Short Name: opencv
URL: https://opencv.org/releases/
Version: 4.3.0
Date: 2020/04/06
License: BSD
License File: src/LICENSE
Security Critical: Yes
CPEPrefix: cpe:/a:opencv:opencv:4.3.0
Description:
This package includes the Earth Mover's Distance (EMD) implementation from
OpenCV, a standard metric for comparing histograms of visual data.
Local Modifications:
- Included only the source for EMD computation
- Added a C++ API to avoid needing to include the entire OpenCV C++ API.
Process for incorporating a new release:
- Download the newest release from https://opencv.org/releases/
- Copy modules/imgproc/src/emd.cpp and LICENSE from the release.
- Apply emd_patch.patch
diff --git b/third_party/opencv/src/emd.cpp a/third_party/opencv/src/emd.cpp
index 20ab6feafbc8..30b19f676705 100644
--- b/third_party/opencv/src/emd.cpp
+++ a/third_party/opencv/src/emd.cpp
@@ -56,12 +56,34 @@
E-Mail: rubner@cs.stanford.edu URL: http://vision.stanford.edu/~rubner
==========================================================================
*/
+#ifdef CHROMIUM_OPENCV
+#include "emd_wrapper.h"
+
+#include <vector>
+#include <cassert>
+
+#include "base/numerics/checked_math.h"
+#else
#include "precomp.hpp"
+#endif
#define MAX_ITERATIONS 500
#define CV_EMD_INF ((float)1e20)
#define CV_EMD_EPS ((float)1e-5)
+#ifdef CHROMIUM_OPENCV
+namespace cv {
+template <typename T>
+using AutoBuffer = std::vector<T>;
+}
+typedef void CvArr;
+typedef float (* CvDistanceFunction)( const float* a, const float* b, void* user_param );
+
+#define cvSqrt(value) ((float)sqrt(value))
+#define CV_Error(code, msg) do { (void)(msg); abort(); } while (0)
+#define CV_Assert(expr) do { if (!(expr)) abort(); } while (0)
+#endif
+
/* CvNode1D is used for lists, representing 1D sparse array */
typedef struct CvNode1D
{
@@ -144,8 +166,25 @@ static float icvDistL2( const float *x, const float *y, void *user_param );
static float icvDistL1( const float *x, const float *y, void *user_param );
static float icvDistC( const float *x, const float *y, void *user_param );
+#ifdef CHROMIUM_OPENCV
+std::vector<float> GetDataFromPointDistribution(const opencv::PointDistribution& distribution) {
+ std::vector<float> data;
+ data.reserve((distribution.dimensions + 1) * distribution.positions.size());
+ for (size_t i = 0; i < distribution.positions.size(); i++) {
+ data.push_back(distribution.weights[i]);
+ data.insert(data.end(), distribution.positions[i].begin(),
+ distribution.positions[i].end());
+ }
+
+ return data;
+}
+#endif
+
/* The main function */
-CV_IMPL float cvCalcEMD2( const CvArr* signature_arr1,
+#ifndef CHROMIUM_OPENCV
+CV_IMPL
+#endif
+float cvCalcEMD2( const CvArr* signature_arr1,
const CvArr* signature_arr2,
int dist_type,
CvDistanceFunction dist_func,
@@ -164,6 +203,23 @@ CV_IMPL float cvCalcEMD2( const CvArr* signature_arr1,
int result = 0;
float eps, min_delta;
CvNode2D *xp = 0;
+#ifdef CHROMIUM_OPENCV
+ opencv::PointDistribution* signature1 =
+ (opencv::PointDistribution*)signature_arr1;
+ opencv::PointDistribution* signature2 =
+ (opencv::PointDistribution*)signature_arr2;
+
+ int dims = signature1->dimensions;
+ int size1 = signature1->positions.size();
+ int size2 = signature2->positions.size();
+ dist_func = icvDistL1;
+
+ std::vector<float> signature1_data =
+ GetDataFromPointDistribution(*signature1);
+ std::vector<float> signature2_data =
+ GetDataFromPointDistribution(*signature2);
+ user_param = (void*)(size_t)dims;
+#else
CvMat sign_stub1, *signature1 = (CvMat*)signature_arr1;
CvMat sign_stub2, *signature2 = (CvMat*)signature_arr2;
CvMat cost_stub, *cost = &cost_stub;
@@ -245,12 +301,19 @@ CV_IMPL float cvCalcEMD2( const CvArr* signature_arr1,
CV_Error( CV_StsBadFlag, "Bad or unsupported metric type" );
}
}
+#endif
+#ifdef CHROMIUM_OPENCV
+ result = icvInitEMD(signature1_data.data(), size1, signature2_data.data(),
+ size2, dims, icvDistL2, user_param, nullptr, 0, &state,
+ lower_bound, local_buf);
+#else
result = icvInitEMD( signature1->data.fl, size1,
signature2->data.fl, size2,
dims, dist_func, user_param,
cost->data.fl, cost->step,
&state, lower_bound, local_buf );
+#endif
if( result > 0 && lower_bound )
{
@@ -307,8 +370,10 @@ CV_IMPL float cvCalcEMD2( const CvArr* signature_arr1,
if( ci >= 0 && cj >= 0 )
{
total_cost += (double)val * state.cost[i][j];
+#ifndef CHROMIUM_OPENCV
if( flow )
((float*)(flow->data.ptr + flow->step*ci))[cj] = val;
+#endif
}
}
@@ -357,7 +422,11 @@ static int icvInitEMD( const float* signature1, int size1,
}
/* allocate buffers */
+#ifdef CHROMIUM_OPENCV
+ _buffer.resize(buffer_size);
+#else
_buffer.allocate(buffer_size);
+#endif
state->buffer = buffer = _buffer.data();
buffer_end = buffer + buffer_size;
@@ -1146,7 +1215,89 @@ icvDistC( const float *x, const float *y, void *user_param )
return (float)s;
}
+#ifdef CHROMIUM_OPENCV
+namespace opencv {
+
+namespace {
+
+bool ValidatePointDistribution(const PointDistribution& distribution) {
+ if (distribution.positions.size() != distribution.weights.size()) {
+ return false;
+ }
+
+ if (distribution.weights.empty()) {
+ return false;
+ }
+
+ for (const float f : distribution.weights) {
+ if (f < 0) {
+ return false;
+ }
+ }
+
+ if (std::all_of(distribution.weights.begin(), distribution.weights.end(),
+ [](float f) { return f == 0; })) {
+ return false;
+ }
+
+ for (const std::vector<float>& point : distribution.positions) {
+ if (point.size() != distribution.dimensions) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Makes sure the buffers allocated during the EMD computation don't overflow
+// in size.
+bool ValidateSize(const PointDistribution& distribution1,
+ const PointDistribution& distribution2) {
+ base::CheckedNumeric<size_t> size1(distribution1.positions.size());
+ size1 *= distribution1.dimensions + 1;
+
+ base::CheckedNumeric<size_t> size2(distribution2.positions.size());
+ size2 *= distribution2.dimensions + 1;
+
+ // This computation should always match the buffer allocation in icvInitEMD
+ base::CheckedNumeric<size_t> total_size =
+ (size1 + 1) * (size2 + 1) *
+ (sizeof(float) + sizeof(char) + sizeof(float)) +
+ (size1 + size2 + 2) *
+ (sizeof(CvNode2D) + sizeof(CvNode2D*) + sizeof(CvNode1D) +
+ sizeof(float) + sizeof(int) + sizeof(CvNode2D*)) +
+ (size1 + 1) * (sizeof(float*) + sizeof(char*) + sizeof(float*)) + 256;
+
+ return total_size.IsValid();
+}
+
+} // namespace
+
+base::Optional<double> EMD(const PointDistribution& distribution1,
+ const PointDistribution& distribution2) {
+ if (!ValidatePointDistribution(distribution1) ||
+ !ValidatePointDistribution(distribution2)) {
+ return base::nullopt;
+ }
+
+ if (distribution1.dimensions != distribution2.dimensions) {
+ return base::nullopt;
+ }
+
+ if (distribution1.dimensions == 0) {
+ return base::nullopt;
+ }
+
+ if (!ValidateSize(distribution1,distribution2)) {
+ return base::nullopt;
+ }
+
+ return cvCalcEMD2(&distribution1, &distribution2, 0, 0, nullptr, nullptr,
+ nullptr, nullptr);
+}
+} // namespace opencv
+#else
float cv::EMD( InputArray _signature1, InputArray _signature2,
int distType, InputArray _cost,
float* lowerBound, OutputArray _flow )
@@ -1177,5 +1328,6 @@ float cv::wrapperEMD(InputArray _signature1, InputArray _signature2,
{
return EMD(_signature1, _signature2, distType, _cost, lowerBound.get(), _flow);
}
+#endif
/* End of file. */
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)
Copyright (C) 2000-2020, Intel Corporation, all rights reserved.
Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved.
Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved.
Copyright (C) 2015-2016, Itseez Inc., all rights reserved.
Copyright (C) 2019-2020, Xperience AI, all rights reserved.
Third party copyrights are property of their respective owners.
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.
This diff is collapsed.
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_EMD_WRAPPER_H_
#define THIRD_PARTY_EMD_WRAPPER_H_
#include <vector>
#include "base/optional.h"
namespace opencv {
struct PointDistribution {
// The weight of each point.
std::vector<float> weights;
// The number of dimensions.
int dimensions;
// The positions of each point. Must have the same size as |weights|, and each
// element must have size |dimensions|.
std::vector<std::vector<float>> positions;
};
base::Optional<double> EMD(const PointDistribution& distribution1,
const PointDistribution& distribution2);
} // namespace opencv
#endif // THIRD_PARTY_EMD_WRAPPER_H_
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