Add option for nearest neighbor interpolation.
PiperOrigin-RevId: 564786213
This commit is contained in:
parent
5daed78844
commit
4ba1dadf92
|
@ -263,7 +263,10 @@ cc_library(
|
||||||
"//mediapipe/framework/port:opencv_imgproc",
|
"//mediapipe/framework/port:opencv_imgproc",
|
||||||
"//mediapipe/framework/port:ret_check",
|
"//mediapipe/framework/port:ret_check",
|
||||||
"//mediapipe/framework/port:status",
|
"//mediapipe/framework/port:status",
|
||||||
|
"//mediapipe/gpu:gl_base_hdr",
|
||||||
"//mediapipe/gpu:scale_mode_cc_proto",
|
"//mediapipe/gpu:scale_mode_cc_proto",
|
||||||
|
"@com_google_absl//absl/status",
|
||||||
|
"@com_google_absl//absl/strings",
|
||||||
] + select({
|
] + select({
|
||||||
"//mediapipe/gpu:disable_gpu": [],
|
"//mediapipe/gpu:disable_gpu": [],
|
||||||
"//conditions:default": [
|
"//conditions:default": [
|
||||||
|
@ -276,6 +279,36 @@ cc_library(
|
||||||
alwayslink = 1,
|
alwayslink = 1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "image_transformation_calculator_test",
|
||||||
|
srcs = ["image_transformation_calculator_test.cc"],
|
||||||
|
data = ["//mediapipe/calculators/image/testdata:test_images"],
|
||||||
|
tags = [
|
||||||
|
"desktop_only_test",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
":image_transformation_calculator",
|
||||||
|
"//mediapipe/framework:calculator_cc_proto",
|
||||||
|
"//mediapipe/framework:calculator_framework",
|
||||||
|
"//mediapipe/framework:calculator_runner",
|
||||||
|
"//mediapipe/framework/deps:file_path",
|
||||||
|
"//mediapipe/framework/formats:image_format_cc_proto",
|
||||||
|
"//mediapipe/framework/formats:image_frame",
|
||||||
|
"//mediapipe/framework/formats:image_frame_opencv",
|
||||||
|
"//mediapipe/framework/port:gtest",
|
||||||
|
"//mediapipe/framework/port:opencv_imgcodecs",
|
||||||
|
"//mediapipe/framework/port:opencv_imgproc",
|
||||||
|
"//mediapipe/framework/port:parse_text_proto",
|
||||||
|
"//mediapipe/gpu:gpu_buffer_to_image_frame_calculator",
|
||||||
|
"//mediapipe/gpu:image_frame_to_gpu_buffer_calculator",
|
||||||
|
"//third_party:opencv",
|
||||||
|
"@com_google_absl//absl/container:flat_hash_set",
|
||||||
|
"@com_google_absl//absl/flags:flag",
|
||||||
|
"@com_google_absl//absl/strings",
|
||||||
|
"@com_google_googletest//:gtest_main",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "image_cropping_calculator",
|
name = "image_cropping_calculator",
|
||||||
srcs = ["image_cropping_calculator.cc"],
|
srcs = ["image_cropping_calculator.cc"],
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "absl/status/status.h"
|
||||||
#include "mediapipe/calculators/image/image_transformation_calculator.pb.h"
|
#include "mediapipe/calculators/image/image_transformation_calculator.pb.h"
|
||||||
#include "mediapipe/calculators/image/rotation_mode.pb.h"
|
#include "mediapipe/calculators/image/rotation_mode.pb.h"
|
||||||
#include "mediapipe/framework/calculator_framework.h"
|
#include "mediapipe/framework/calculator_framework.h"
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
#include "mediapipe/framework/port/ret_check.h"
|
#include "mediapipe/framework/port/ret_check.h"
|
||||||
#include "mediapipe/framework/port/status.h"
|
#include "mediapipe/framework/port/status.h"
|
||||||
#include "mediapipe/framework/timestamp.h"
|
#include "mediapipe/framework/timestamp.h"
|
||||||
|
#include "mediapipe/gpu/gl_base.h"
|
||||||
#include "mediapipe/gpu/scale_mode.pb.h"
|
#include "mediapipe/gpu/scale_mode.pb.h"
|
||||||
|
|
||||||
#if !MEDIAPIPE_DISABLE_GPU
|
#if !MEDIAPIPE_DISABLE_GPU
|
||||||
|
@ -60,42 +62,42 @@ constexpr char kVideoPrestreamTag[] = "VIDEO_PRESTREAM";
|
||||||
|
|
||||||
int RotationModeToDegrees(mediapipe::RotationMode_Mode rotation) {
|
int RotationModeToDegrees(mediapipe::RotationMode_Mode rotation) {
|
||||||
switch (rotation) {
|
switch (rotation) {
|
||||||
case mediapipe::RotationMode_Mode_UNKNOWN:
|
case mediapipe::RotationMode::UNKNOWN:
|
||||||
case mediapipe::RotationMode_Mode_ROTATION_0:
|
case mediapipe::RotationMode::ROTATION_0:
|
||||||
return 0;
|
return 0;
|
||||||
case mediapipe::RotationMode_Mode_ROTATION_90:
|
case mediapipe::RotationMode::ROTATION_90:
|
||||||
return 90;
|
return 90;
|
||||||
case mediapipe::RotationMode_Mode_ROTATION_180:
|
case mediapipe::RotationMode::ROTATION_180:
|
||||||
return 180;
|
return 180;
|
||||||
case mediapipe::RotationMode_Mode_ROTATION_270:
|
case mediapipe::RotationMode::ROTATION_270:
|
||||||
return 270;
|
return 270;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mediapipe::RotationMode_Mode DegreesToRotationMode(int degrees) {
|
mediapipe::RotationMode_Mode DegreesToRotationMode(int degrees) {
|
||||||
switch (degrees) {
|
switch (degrees) {
|
||||||
case 0:
|
case 0:
|
||||||
return mediapipe::RotationMode_Mode_ROTATION_0;
|
return mediapipe::RotationMode::ROTATION_0;
|
||||||
case 90:
|
case 90:
|
||||||
return mediapipe::RotationMode_Mode_ROTATION_90;
|
return mediapipe::RotationMode::ROTATION_90;
|
||||||
case 180:
|
case 180:
|
||||||
return mediapipe::RotationMode_Mode_ROTATION_180;
|
return mediapipe::RotationMode::ROTATION_180;
|
||||||
case 270:
|
case 270:
|
||||||
return mediapipe::RotationMode_Mode_ROTATION_270;
|
return mediapipe::RotationMode::ROTATION_270;
|
||||||
default:
|
default:
|
||||||
return mediapipe::RotationMode_Mode_UNKNOWN;
|
return mediapipe::RotationMode::UNKNOWN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mediapipe::ScaleMode_Mode ParseScaleMode(
|
mediapipe::ScaleMode_Mode ParseScaleMode(
|
||||||
mediapipe::ScaleMode_Mode scale_mode,
|
mediapipe::ScaleMode_Mode scale_mode,
|
||||||
mediapipe::ScaleMode_Mode default_mode) {
|
mediapipe::ScaleMode_Mode default_mode) {
|
||||||
switch (scale_mode) {
|
switch (scale_mode) {
|
||||||
case mediapipe::ScaleMode_Mode_DEFAULT:
|
case mediapipe::ScaleMode::DEFAULT:
|
||||||
return default_mode;
|
return default_mode;
|
||||||
case mediapipe::ScaleMode_Mode_STRETCH:
|
case mediapipe::ScaleMode::STRETCH:
|
||||||
return scale_mode;
|
return scale_mode;
|
||||||
case mediapipe::ScaleMode_Mode_FIT:
|
case mediapipe::ScaleMode::FIT:
|
||||||
return scale_mode;
|
return scale_mode;
|
||||||
case mediapipe::ScaleMode_Mode_FILL_AND_CROP:
|
case mediapipe::ScaleMode::FILL_AND_CROP:
|
||||||
return scale_mode;
|
return scale_mode;
|
||||||
default:
|
default:
|
||||||
return default_mode;
|
return default_mode;
|
||||||
|
@ -208,6 +210,8 @@ class ImageTransformationCalculator : public CalculatorBase {
|
||||||
|
|
||||||
bool use_gpu_ = false;
|
bool use_gpu_ = false;
|
||||||
cv::Scalar padding_color_;
|
cv::Scalar padding_color_;
|
||||||
|
ImageTransformationCalculatorOptions::InterpolationMode interpolation_mode_;
|
||||||
|
|
||||||
#if !MEDIAPIPE_DISABLE_GPU
|
#if !MEDIAPIPE_DISABLE_GPU
|
||||||
GlCalculatorHelper gpu_helper_;
|
GlCalculatorHelper gpu_helper_;
|
||||||
std::unique_ptr<QuadRenderer> rgb_renderer_;
|
std::unique_ptr<QuadRenderer> rgb_renderer_;
|
||||||
|
@ -343,6 +347,11 @@ absl::Status ImageTransformationCalculator::Open(CalculatorContext* cc) {
|
||||||
options_.padding_color().green(),
|
options_.padding_color().green(),
|
||||||
options_.padding_color().blue());
|
options_.padding_color().blue());
|
||||||
|
|
||||||
|
interpolation_mode_ = options_.interpolation_mode();
|
||||||
|
if (options_.interpolation_mode() ==
|
||||||
|
ImageTransformationCalculatorOptions::DEFAULT) {
|
||||||
|
interpolation_mode_ = ImageTransformationCalculatorOptions::LINEAR;
|
||||||
|
}
|
||||||
if (use_gpu_) {
|
if (use_gpu_) {
|
||||||
#if !MEDIAPIPE_DISABLE_GPU
|
#if !MEDIAPIPE_DISABLE_GPU
|
||||||
// Let the helper access the GL context information.
|
// Let the helper access the GL context information.
|
||||||
|
@ -457,26 +466,48 @@ absl::Status ImageTransformationCalculator::RenderCpu(CalculatorContext* cc) {
|
||||||
ComputeOutputDimensions(input_width, input_height, &output_width,
|
ComputeOutputDimensions(input_width, input_height, &output_width,
|
||||||
&output_height);
|
&output_height);
|
||||||
|
|
||||||
|
int opencv_interpolation_mode = cv::INTER_LINEAR;
|
||||||
if (output_width_ > 0 && output_height_ > 0) {
|
if (output_width_ > 0 && output_height_ > 0) {
|
||||||
cv::Mat scaled_mat;
|
cv::Mat scaled_mat;
|
||||||
if (scale_mode_ == mediapipe::ScaleMode_Mode_STRETCH) {
|
if (scale_mode_ == mediapipe::ScaleMode::STRETCH) {
|
||||||
int scale_flag =
|
if (interpolation_mode_ == ImageTransformationCalculatorOptions::LINEAR) {
|
||||||
input_mat.cols > output_width_ && input_mat.rows > output_height_
|
// Use INTER_AREA for downscaling if interpolation mode is set to
|
||||||
? cv::INTER_AREA
|
// LINEAR.
|
||||||
: cv::INTER_LINEAR;
|
if (input_mat.cols > output_width_ && input_mat.rows > output_height_) {
|
||||||
|
opencv_interpolation_mode = cv::INTER_AREA;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
opencv_interpolation_mode = cv::INTER_LINEAR;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
opencv_interpolation_mode = cv::INTER_NEAREST;
|
||||||
|
}
|
||||||
cv::resize(input_mat, scaled_mat, cv::Size(output_width_, output_height_),
|
cv::resize(input_mat, scaled_mat, cv::Size(output_width_, output_height_),
|
||||||
0, 0, scale_flag);
|
0, 0, opencv_interpolation_mode);
|
||||||
} else {
|
} else {
|
||||||
const float scale =
|
const float scale =
|
||||||
std::min(static_cast<float>(output_width_) / input_width,
|
std::min(static_cast<float>(output_width_) / input_width,
|
||||||
static_cast<float>(output_height_) / input_height);
|
static_cast<float>(output_height_) / input_height);
|
||||||
const int target_width = std::round(input_width * scale);
|
const int target_width = std::round(input_width * scale);
|
||||||
const int target_height = std::round(input_height * scale);
|
const int target_height = std::round(input_height * scale);
|
||||||
int scale_flag = scale < 1.0f ? cv::INTER_AREA : cv::INTER_LINEAR;
|
|
||||||
if (scale_mode_ == mediapipe::ScaleMode_Mode_FIT) {
|
if (interpolation_mode_ == ImageTransformationCalculatorOptions::LINEAR) {
|
||||||
|
// Use INTER_AREA for downscaling if interpolation mode is set to
|
||||||
|
// LINEAR.
|
||||||
|
if (scale < 1.0f) {
|
||||||
|
opencv_interpolation_mode = cv::INTER_AREA;
|
||||||
|
} else {
|
||||||
|
opencv_interpolation_mode = cv::INTER_LINEAR;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
opencv_interpolation_mode = cv::INTER_NEAREST;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scale_mode_ == mediapipe::ScaleMode::FIT) {
|
||||||
cv::Mat intermediate_mat;
|
cv::Mat intermediate_mat;
|
||||||
cv::resize(input_mat, intermediate_mat,
|
cv::resize(input_mat, intermediate_mat,
|
||||||
cv::Size(target_width, target_height), 0, 0, scale_flag);
|
cv::Size(target_width, target_height), 0, 0,
|
||||||
|
opencv_interpolation_mode);
|
||||||
const int top = (output_height_ - target_height) / 2;
|
const int top = (output_height_ - target_height) / 2;
|
||||||
const int bottom = output_height_ - target_height - top;
|
const int bottom = output_height_ - target_height - top;
|
||||||
const int left = (output_width_ - target_width) / 2;
|
const int left = (output_width_ - target_width) / 2;
|
||||||
|
@ -488,7 +519,7 @@ absl::Status ImageTransformationCalculator::RenderCpu(CalculatorContext* cc) {
|
||||||
padding_color_);
|
padding_color_);
|
||||||
} else {
|
} else {
|
||||||
cv::resize(input_mat, scaled_mat, cv::Size(target_width, target_height),
|
cv::resize(input_mat, scaled_mat, cv::Size(target_width, target_height),
|
||||||
0, 0, scale_flag);
|
0, 0, opencv_interpolation_mode);
|
||||||
output_width = target_width;
|
output_width = target_width;
|
||||||
output_height = target_height;
|
output_height = target_height;
|
||||||
}
|
}
|
||||||
|
@ -514,17 +545,17 @@ absl::Status ImageTransformationCalculator::RenderCpu(CalculatorContext* cc) {
|
||||||
cv::warpAffine(input_mat, rotated_mat, rotation_mat, rotated_size);
|
cv::warpAffine(input_mat, rotated_mat, rotation_mat, rotated_size);
|
||||||
} else {
|
} else {
|
||||||
switch (rotation_) {
|
switch (rotation_) {
|
||||||
case mediapipe::RotationMode_Mode_UNKNOWN:
|
case mediapipe::RotationMode::UNKNOWN:
|
||||||
case mediapipe::RotationMode_Mode_ROTATION_0:
|
case mediapipe::RotationMode::ROTATION_0:
|
||||||
rotated_mat = input_mat;
|
rotated_mat = input_mat;
|
||||||
break;
|
break;
|
||||||
case mediapipe::RotationMode_Mode_ROTATION_90:
|
case mediapipe::RotationMode::ROTATION_90:
|
||||||
cv::rotate(input_mat, rotated_mat, cv::ROTATE_90_COUNTERCLOCKWISE);
|
cv::rotate(input_mat, rotated_mat, cv::ROTATE_90_COUNTERCLOCKWISE);
|
||||||
break;
|
break;
|
||||||
case mediapipe::RotationMode_Mode_ROTATION_180:
|
case mediapipe::RotationMode::ROTATION_180:
|
||||||
cv::rotate(input_mat, rotated_mat, cv::ROTATE_180);
|
cv::rotate(input_mat, rotated_mat, cv::ROTATE_180);
|
||||||
break;
|
break;
|
||||||
case mediapipe::RotationMode_Mode_ROTATION_270:
|
case mediapipe::RotationMode::ROTATION_270:
|
||||||
cv::rotate(input_mat, rotated_mat, cv::ROTATE_90_CLOCKWISE);
|
cv::rotate(input_mat, rotated_mat, cv::ROTATE_90_CLOCKWISE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -561,7 +592,7 @@ absl::Status ImageTransformationCalculator::RenderGpu(CalculatorContext* cc) {
|
||||||
ComputeOutputDimensions(input_width, input_height, &output_width,
|
ComputeOutputDimensions(input_width, input_height, &output_width,
|
||||||
&output_height);
|
&output_height);
|
||||||
|
|
||||||
if (scale_mode_ == mediapipe::ScaleMode_Mode_FILL_AND_CROP) {
|
if (scale_mode_ == mediapipe::ScaleMode::FILL_AND_CROP) {
|
||||||
const float scale =
|
const float scale =
|
||||||
std::min(static_cast<float>(output_width_) / input_width,
|
std::min(static_cast<float>(output_width_) / input_width,
|
||||||
static_cast<float>(output_height_) / input_height);
|
static_cast<float>(output_height_) / input_height);
|
||||||
|
@ -628,11 +659,20 @@ absl::Status ImageTransformationCalculator::RenderGpu(CalculatorContext* cc) {
|
||||||
glActiveTexture(GL_TEXTURE1);
|
glActiveTexture(GL_TEXTURE1);
|
||||||
glBindTexture(src1.target(), src1.name());
|
glBindTexture(src1.target(), src1.name());
|
||||||
|
|
||||||
|
if (interpolation_mode_ == ImageTransformationCalculatorOptions::NEAREST) {
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
}
|
||||||
|
|
||||||
MP_RETURN_IF_ERROR(renderer->GlRender(
|
MP_RETURN_IF_ERROR(renderer->GlRender(
|
||||||
src1.width(), src1.height(), dst.width(), dst.height(), scale_mode,
|
src1.width(), src1.height(), dst.width(), dst.height(), scale_mode,
|
||||||
rotation, flip_horizontally_, flip_vertically_,
|
rotation, flip_horizontally_, flip_vertically_,
|
||||||
/*flip_texture=*/false));
|
/*flip_texture=*/false));
|
||||||
|
|
||||||
|
// Reset interpolation modes to MediaPipe defaults.
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE1);
|
glActiveTexture(GL_TEXTURE1);
|
||||||
glBindTexture(src1.target(), 0);
|
glBindTexture(src1.target(), 0);
|
||||||
|
|
||||||
|
@ -652,8 +692,8 @@ void ImageTransformationCalculator::ComputeOutputDimensions(
|
||||||
if (output_width_ > 0 && output_height_ > 0) {
|
if (output_width_ > 0 && output_height_ > 0) {
|
||||||
*output_width = output_width_;
|
*output_width = output_width_;
|
||||||
*output_height = output_height_;
|
*output_height = output_height_;
|
||||||
} else if (rotation_ == mediapipe::RotationMode_Mode_ROTATION_90 ||
|
} else if (rotation_ == mediapipe::RotationMode::ROTATION_90 ||
|
||||||
rotation_ == mediapipe::RotationMode_Mode_ROTATION_270) {
|
rotation_ == mediapipe::RotationMode::ROTATION_270) {
|
||||||
*output_width = input_height;
|
*output_width = input_height;
|
||||||
*output_height = input_width;
|
*output_height = input_width;
|
||||||
} else {
|
} else {
|
||||||
|
@ -666,9 +706,9 @@ void ImageTransformationCalculator::ComputeOutputLetterboxPadding(
|
||||||
int input_width, int input_height, int output_width, int output_height,
|
int input_width, int input_height, int output_width, int output_height,
|
||||||
std::array<float, 4>* padding) {
|
std::array<float, 4>* padding) {
|
||||||
padding->fill(0.f);
|
padding->fill(0.f);
|
||||||
if (scale_mode_ == mediapipe::ScaleMode_Mode_FIT) {
|
if (scale_mode_ == mediapipe::ScaleMode::FIT) {
|
||||||
if (rotation_ == mediapipe::RotationMode_Mode_ROTATION_90 ||
|
if (rotation_ == mediapipe::RotationMode::ROTATION_90 ||
|
||||||
rotation_ == mediapipe::RotationMode_Mode_ROTATION_270) {
|
rotation_ == mediapipe::RotationMode::ROTATION_270) {
|
||||||
std::swap(input_width, input_height);
|
std::swap(input_width, input_height);
|
||||||
}
|
}
|
||||||
const float input_aspect_ratio =
|
const float input_aspect_ratio =
|
||||||
|
|
|
@ -54,4 +54,15 @@ message ImageTransformationCalculatorOptions {
|
||||||
// The color for the padding. This option is only used when the scale mode is
|
// The color for the padding. This option is only used when the scale mode is
|
||||||
// FIT. Default is black. This is for CPU only.
|
// FIT. Default is black. This is for CPU only.
|
||||||
optional Color padding_color = 8;
|
optional Color padding_color = 8;
|
||||||
|
|
||||||
|
// Interpolation method to use. Note that on CPU when LINEAR is specified,
|
||||||
|
// INTER_LINEAR is used for upscaling and INTER_AREA is used for downscaling.
|
||||||
|
enum InterpolationMode {
|
||||||
|
DEFAULT = 0;
|
||||||
|
LINEAR = 1;
|
||||||
|
NEAREST = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mode DEFAULT will use LINEAR interpolation.
|
||||||
|
optional InterpolationMode interpolation_mode = 9;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "absl/container/flat_hash_set.h"
|
||||||
|
#include "absl/flags/flag.h"
|
||||||
|
#include "absl/strings/substitute.h"
|
||||||
|
#include "mediapipe/framework/calculator.pb.h"
|
||||||
|
#include "mediapipe/framework/calculator_framework.h"
|
||||||
|
#include "mediapipe/framework/calculator_runner.h"
|
||||||
|
#include "mediapipe/framework/deps/file_path.h"
|
||||||
|
#include "mediapipe/framework/formats/image_format.pb.h"
|
||||||
|
#include "mediapipe/framework/formats/image_frame.h"
|
||||||
|
#include "mediapipe/framework/formats/image_frame_opencv.h"
|
||||||
|
#include "mediapipe/framework/port/gtest.h"
|
||||||
|
#include "mediapipe/framework/port/opencv_imgcodecs_inc.h"
|
||||||
|
#include "mediapipe/framework/port/opencv_imgproc_inc.h"
|
||||||
|
#include "mediapipe/framework/port/parse_text_proto.h"
|
||||||
|
#include "testing/base/public/gmock.h"
|
||||||
|
#include "testing/base/public/googletest.h"
|
||||||
|
#include "third_party/OpenCV/core/mat.hpp"
|
||||||
|
|
||||||
|
namespace mediapipe {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
absl::flat_hash_set<int> computeUniqueValues(const cv::Mat& mat) {
|
||||||
|
// Compute the unique values in cv::Mat
|
||||||
|
absl::flat_hash_set<int> unique_values;
|
||||||
|
for (int i = 0; i < mat.rows; i++) {
|
||||||
|
for (int j = 0; j < mat.cols; j++) {
|
||||||
|
unique_values.insert(mat.at<unsigned char>(i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unique_values;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ImageTransformationCalculatorTest, NearestNeighborResizing) {
|
||||||
|
cv::Mat input_mat;
|
||||||
|
cv::cvtColor(cv::imread(file::JoinPath("./",
|
||||||
|
"/mediapipe/calculators/"
|
||||||
|
"image/testdata/binary_mask.png")),
|
||||||
|
input_mat, cv::COLOR_BGR2GRAY);
|
||||||
|
Packet input_image_packet = MakePacket<ImageFrame>(
|
||||||
|
ImageFormat::GRAY8, input_mat.size().width, input_mat.size().height);
|
||||||
|
input_mat.copyTo(formats::MatView(&(input_image_packet.Get<ImageFrame>())));
|
||||||
|
|
||||||
|
std::vector<std::pair<int, int>> output_dims{
|
||||||
|
{256, 333}, {512, 512}, {1024, 1024}};
|
||||||
|
|
||||||
|
for (auto& output_dim : output_dims) {
|
||||||
|
Packet input_output_dim_packet =
|
||||||
|
MakePacket<std::pair<int, int>>(output_dim);
|
||||||
|
std::vector<std::string> scale_modes{"FIT", "STRETCH"};
|
||||||
|
for (const auto& scale_mode : scale_modes) {
|
||||||
|
CalculatorGraphConfig::Node node_config =
|
||||||
|
ParseTextProtoOrDie<CalculatorGraphConfig::Node>(
|
||||||
|
absl::Substitute(R"(
|
||||||
|
calculator: "ImageTransformationCalculator"
|
||||||
|
input_stream: "IMAGE:input_image"
|
||||||
|
input_stream: "OUTPUT_DIMENSIONS:image_size"
|
||||||
|
output_stream: "IMAGE:output_image"
|
||||||
|
options: {
|
||||||
|
[mediapipe.ImageTransformationCalculatorOptions.ext]: {
|
||||||
|
scale_mode: $0
|
||||||
|
interpolation_mode: NEAREST
|
||||||
|
}
|
||||||
|
})",
|
||||||
|
scale_mode));
|
||||||
|
|
||||||
|
CalculatorRunner runner(node_config);
|
||||||
|
runner.MutableInputs()->Tag("IMAGE").packets.push_back(
|
||||||
|
input_image_packet.At(Timestamp(0)));
|
||||||
|
runner.MutableInputs()
|
||||||
|
->Tag("OUTPUT_DIMENSIONS")
|
||||||
|
.packets.push_back(input_output_dim_packet.At(Timestamp(0)));
|
||||||
|
|
||||||
|
MP_ASSERT_OK(runner.Run());
|
||||||
|
const auto& outputs = runner.Outputs();
|
||||||
|
ASSERT_EQ(outputs.NumEntries(), 1);
|
||||||
|
const std::vector<Packet>& packets = outputs.Tag("IMAGE").packets;
|
||||||
|
ASSERT_EQ(packets.size(), 1);
|
||||||
|
const auto& result = packets[0].Get<ImageFrame>();
|
||||||
|
ASSERT_EQ(output_dim.first, result.Width());
|
||||||
|
ASSERT_EQ(output_dim.second, result.Height());
|
||||||
|
|
||||||
|
auto unique_input_values = computeUniqueValues(input_mat);
|
||||||
|
auto unique_output_values =
|
||||||
|
computeUniqueValues(formats::MatView(&result));
|
||||||
|
EXPECT_THAT(unique_input_values,
|
||||||
|
::testing::ContainerEq(unique_output_values));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ImageTransformationCalculatorTest, NearestNeighborResizingGpu) {
|
||||||
|
cv::Mat input_mat;
|
||||||
|
cv::cvtColor(cv::imread(file::JoinPath("./",
|
||||||
|
"/mediapipe/calculators/"
|
||||||
|
"image/testdata/binary_mask.png")),
|
||||||
|
input_mat, cv::COLOR_BGR2RGBA);
|
||||||
|
|
||||||
|
std::vector<std::pair<int, int>> output_dims{
|
||||||
|
{256, 333}, {512, 512}, {1024, 1024}};
|
||||||
|
|
||||||
|
for (auto& output_dim : output_dims) {
|
||||||
|
std::vector<std::string> scale_modes{"FIT"}; //, "STRETCH"};
|
||||||
|
for (const auto& scale_mode : scale_modes) {
|
||||||
|
CalculatorGraphConfig graph_config =
|
||||||
|
ParseTextProtoOrDie<CalculatorGraphConfig>(
|
||||||
|
absl::Substitute(R"(
|
||||||
|
input_stream: "input_image"
|
||||||
|
input_stream: "image_size"
|
||||||
|
output_stream: "output_image"
|
||||||
|
|
||||||
|
node {
|
||||||
|
calculator: "ImageFrameToGpuBufferCalculator"
|
||||||
|
input_stream: "input_image"
|
||||||
|
output_stream: "input_image_gpu"
|
||||||
|
}
|
||||||
|
|
||||||
|
node {
|
||||||
|
calculator: "ImageTransformationCalculator"
|
||||||
|
input_stream: "IMAGE_GPU:input_image_gpu"
|
||||||
|
input_stream: "OUTPUT_DIMENSIONS:image_size"
|
||||||
|
output_stream: "IMAGE_GPU:output_image_gpu"
|
||||||
|
options: {
|
||||||
|
[mediapipe.ImageTransformationCalculatorOptions.ext]: {
|
||||||
|
scale_mode: $0
|
||||||
|
interpolation_mode: NEAREST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node {
|
||||||
|
calculator: "GpuBufferToImageFrameCalculator"
|
||||||
|
input_stream: "output_image_gpu"
|
||||||
|
output_stream: "output_image"
|
||||||
|
})",
|
||||||
|
scale_mode));
|
||||||
|
ImageFrame input_image(ImageFormat::SRGBA, input_mat.size().width,
|
||||||
|
input_mat.size().height);
|
||||||
|
input_mat.copyTo(formats::MatView(&input_image));
|
||||||
|
|
||||||
|
std::vector<Packet> output_image_packets;
|
||||||
|
tool::AddVectorSink("output_image", &graph_config, &output_image_packets);
|
||||||
|
|
||||||
|
CalculatorGraph graph(graph_config);
|
||||||
|
MP_ASSERT_OK(graph.StartRun({}));
|
||||||
|
|
||||||
|
MP_ASSERT_OK(graph.AddPacketToInputStream(
|
||||||
|
"input_image",
|
||||||
|
MakePacket<ImageFrame>(std::move(input_image)).At(Timestamp(0))));
|
||||||
|
MP_ASSERT_OK(graph.AddPacketToInputStream(
|
||||||
|
"image_size",
|
||||||
|
MakePacket<std::pair<int, int>>(output_dim).At(Timestamp(0))));
|
||||||
|
|
||||||
|
MP_ASSERT_OK(graph.WaitUntilIdle());
|
||||||
|
ASSERT_THAT(output_image_packets, testing::SizeIs(1));
|
||||||
|
|
||||||
|
const auto& output_image = output_image_packets[0].Get<ImageFrame>();
|
||||||
|
ASSERT_EQ(output_dim.first, output_image.Width());
|
||||||
|
ASSERT_EQ(output_dim.second, output_image.Height());
|
||||||
|
|
||||||
|
auto unique_input_values = computeUniqueValues(input_mat);
|
||||||
|
auto unique_output_values =
|
||||||
|
computeUniqueValues(formats::MatView(&output_image));
|
||||||
|
EXPECT_THAT(unique_input_values,
|
||||||
|
::testing::ContainerEq(unique_output_values));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace mediapipe
|
1
mediapipe/calculators/image/testdata/BUILD
vendored
1
mediapipe/calculators/image/testdata/BUILD
vendored
|
@ -18,6 +18,7 @@ licenses(["notice"])
|
||||||
filegroup(
|
filegroup(
|
||||||
name = "test_images",
|
name = "test_images",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
"binary_mask.png",
|
||||||
"dino.jpg",
|
"dino.jpg",
|
||||||
"dino_quality_50.jpg",
|
"dino_quality_50.jpg",
|
||||||
"dino_quality_80.jpg",
|
"dino_quality_80.jpg",
|
||||||
|
|
BIN
mediapipe/calculators/image/testdata/binary_mask.png
vendored
Normal file
BIN
mediapipe/calculators/image/testdata/binary_mask.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 771 B |
Loading…
Reference in New Issue
Block a user