Separate color conversion, affine transform
This commit is contained in:
parent
c980f0432c
commit
51ed94633e
|
@ -39,10 +39,12 @@ void SetColorChannel(int channel, uint8 value, cv::Mat* mat) {
|
|||
constexpr char kRgbaInTag[] = "RGBA_IN";
|
||||
constexpr char kRgbInTag[] = "RGB_IN";
|
||||
constexpr char kBgraInTag[] = "BGRA_IN";
|
||||
constexpr char kBgrInTag[] = "BGR_IN";
|
||||
constexpr char kGrayInTag[] = "GRAY_IN";
|
||||
constexpr char kRgbaOutTag[] = "RGBA_OUT";
|
||||
constexpr char kRgbOutTag[] = "RGB_OUT";
|
||||
constexpr char kBgraOutTag[] = "BGRA_OUT";
|
||||
constexpr char kBgrOutTag[] = "BGR_OUT";
|
||||
constexpr char kGrayOutTag[] = "GRAY_OUT";
|
||||
} // namespace
|
||||
|
||||
|
@ -57,6 +59,7 @@ constexpr char kGrayOutTag[] = "GRAY_OUT";
|
|||
// RGB -> RGBA
|
||||
// RGBA -> BGRA
|
||||
// BGRA -> RGBA
|
||||
// BGRA -> RGB
|
||||
//
|
||||
// This calculator only supports a single input stream and output stream at a
|
||||
// time. If more than one input stream or output stream is present, the
|
||||
|
@ -122,6 +125,10 @@ absl::Status ColorConvertCalculator::GetContract(CalculatorContract* cc) {
|
|||
cc->Inputs().Tag(kBgraInTag).Set<ImageFrame>();
|
||||
}
|
||||
|
||||
if (cc->Inputs().HasTag(kBgrInTag)) {
|
||||
cc->Inputs().Tag(kBgrInTag).Set<ImageFrame>();
|
||||
}
|
||||
|
||||
if (cc->Outputs().HasTag(kRgbOutTag)) {
|
||||
cc->Outputs().Tag(kRgbOutTag).Set<ImageFrame>();
|
||||
}
|
||||
|
@ -138,6 +145,10 @@ absl::Status ColorConvertCalculator::GetContract(CalculatorContract* cc) {
|
|||
cc->Outputs().Tag(kBgraOutTag).Set<ImageFrame>();
|
||||
}
|
||||
|
||||
if (cc->Outputs().HasTag(kBgrOutTag)) {
|
||||
cc->Outputs().Tag(kBgrOutTag).Set<ImageFrame>();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
|
@ -157,6 +168,7 @@ absl::Status ColorConvertCalculator::ConvertAndOutput(
|
|||
if (open_cv_convert_code == cv::COLOR_RGB2RGBA) {
|
||||
SetColorChannel(3, 255, &output_mat);
|
||||
}
|
||||
|
||||
cc->Outputs()
|
||||
.Tag(output_tag)
|
||||
.Add(output_frame.release(), cc->InputTimestamp());
|
||||
|
@ -194,6 +206,21 @@ absl::Status ColorConvertCalculator::Process(CalculatorContext* cc) {
|
|||
return ConvertAndOutput(kRgbaInTag, kBgraOutTag, ImageFormat::SBGRA,
|
||||
cv::COLOR_RGBA2BGRA, cc);
|
||||
}
|
||||
// RGBA -> BGR
|
||||
if (cc->Inputs().HasTag(kRgbaInTag) && cc->Outputs().HasTag(kBgrOutTag)) {
|
||||
return ConvertAndOutput(kRgbaInTag, kBgrOutTag, ImageFormat::SBGR,
|
||||
cv::COLOR_RGBA2BGR, cc);
|
||||
}
|
||||
// BGR -> RGBA
|
||||
if (cc->Inputs().HasTag(kBgrInTag) && cc->Outputs().HasTag(kRgbaOutTag)) {
|
||||
return ConvertAndOutput(kBgrInTag, kRgbaOutTag, ImageFormat::SRGBA,
|
||||
cv::COLOR_BGR2RGBA, cc);
|
||||
}
|
||||
// BGRA -> RGB
|
||||
if (cc->Inputs().HasTag(kBgraInTag) && cc->Outputs().HasTag(kRgbOutTag)) {
|
||||
return ConvertAndOutput(kBgraInTag, kRgbOutTag, ImageFormat::SRGB,
|
||||
cv::COLOR_BGRA2RGB, cc);
|
||||
}
|
||||
|
||||
return mediapipe::InvalidArgumentErrorBuilder(MEDIAPIPE_LOC)
|
||||
<< "Unsupported image format conversion.";
|
||||
|
|
|
@ -73,5 +73,7 @@ message ImageFormat {
|
|||
// sBGRA, interleaved: one byte for B, one byte for G, one byte for R,
|
||||
// one byte for alpha or unused. This is the N32 format for Skia.
|
||||
SBGRA = 11;
|
||||
|
||||
SBGR = 13;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -283,6 +283,8 @@ int ImageFrame::NumberOfChannelsForFormat(ImageFormat::Format format) {
|
|||
return 3;
|
||||
case ImageFormat::SBGRA:
|
||||
return 4;
|
||||
case ImageFormat::SBGR:
|
||||
return 3;
|
||||
default:
|
||||
LOG(FATAL) << InvalidFormatString(format);
|
||||
}
|
||||
|
@ -312,6 +314,8 @@ int ImageFrame::ChannelSizeForFormat(ImageFormat::Format format) {
|
|||
return sizeof(uint8);
|
||||
case ImageFormat::SBGRA:
|
||||
return sizeof(uint8);
|
||||
case ImageFormat::SBGR:
|
||||
return sizeof(uint8);
|
||||
default:
|
||||
LOG(FATAL) << InvalidFormatString(format);
|
||||
}
|
||||
|
@ -341,6 +345,8 @@ int ImageFrame::ByteDepthForFormat(ImageFormat::Format format) {
|
|||
return 1;
|
||||
case ImageFormat::SBGRA:
|
||||
return 1;
|
||||
case ImageFormat::SBGR:
|
||||
return 1;
|
||||
default:
|
||||
LOG(FATAL) << InvalidFormatString(format);
|
||||
}
|
||||
|
|
|
@ -25,12 +25,15 @@ cc_library(
|
|||
name = "mobile_calculators" ,
|
||||
deps = [
|
||||
"//mediapipe/graphs/deformation/calculators:face_processor_calculator" ,
|
||||
"//mediapipe/graphs/deformation/calculators:warp_affine_calculator" ,
|
||||
"//mediapipe/calculators/core:flow_limiter_calculator" ,
|
||||
"//mediapipe/calculators/image:image_transformation_calculator" ,
|
||||
"//mediapipe/modules/face_landmark:face_landmark_front_gpu" ,
|
||||
"//mediapipe/calculators/core:constant_side_packet_calculator" ,
|
||||
"//mediapipe/gpu:gpu_buffer_to_image_frame_calculator" ,
|
||||
"//mediapipe/gpu:image_frame_to_gpu_buffer_calculator" ,
|
||||
"//mediapipe/calculators/image:color_convert_calculator",
|
||||
"//mediapipe/calculators/image:image_properties_calculator",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -38,10 +41,14 @@ cc_library(
|
|||
name = "desktop_calculators" ,
|
||||
deps = [
|
||||
"//mediapipe/graphs/deformation/calculators:face_processor_calculator" ,
|
||||
"//mediapipe/graphs/deformation/calculators:warp_affine_calculator" ,
|
||||
"//mediapipe/calculators/core:flow_limiter_calculator" ,
|
||||
"//mediapipe/calculators/image:image_transformation_calculator" ,
|
||||
"//mediapipe/modules/face_landmark:face_landmark_front_cpu" ,
|
||||
"//mediapipe/calculators/core:constant_side_packet_calculator" ,
|
||||
"//mediapipe/calculators/image:color_convert_calculator",
|
||||
"//mediapipe/calculators/image:image_properties_calculator",
|
||||
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -19,10 +19,7 @@ bazel-bin/mediapipe/examples/desktop/deformation/deformation_cpu
|
|||
```
|
||||
Run with (using video):
|
||||
```
|
||||
bazel-bin/mediapipe/examples/desktop//deformation/deformation_cpu
|
||||
--calculator_graph_config_file=mediapipe/graphs/deformation/deformation_cpu.pbtxt
|
||||
--input_video_path=/path/video.mp4
|
||||
--output_video_path=/path/outvideo.mp4
|
||||
bazel-bin/mediapipe/examples/desktop/deformation/deformation_cpu --calculator_graph_config_file=mediapipe/graphs/deformation/deformation_cpu.pbtxt --input_video_path=/path/video.mp4 --output_video_path=/path/outvideo.mp4
|
||||
```
|
||||
|
||||
2. Mobile (Android)
|
||||
|
|
|
@ -47,5 +47,23 @@ cc_library(
|
|||
alwayslink = 1,
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "warp_affine_calculator",
|
||||
srcs = ["warp_affine_calculator.cc"],
|
||||
hdrs = ["Tensor.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//mediapipe/framework:calculator_framework",
|
||||
"//mediapipe/framework/api2:node",
|
||||
"//mediapipe/framework/formats:image_frame",
|
||||
"//mediapipe/framework/formats:image_frame_opencv",
|
||||
"//mediapipe/framework/port:opencv_core",
|
||||
"//mediapipe/framework/port:opencv_imgproc",
|
||||
"//mediapipe/framework/port:opencv_highgui",
|
||||
],
|
||||
alwayslink = 1,
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -64,6 +64,10 @@ public:
|
|||
return vector<T>(M.ptr<T>(0), M.ptr<T>(0) + dims[1]);
|
||||
}
|
||||
|
||||
/* vector<int> get_dims() {
|
||||
return dims;
|
||||
} */
|
||||
|
||||
T at(vector<int> _indexes) {
|
||||
return M.at<T>(_indexes.data());
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
#include "mediapipe/framework/port/opencv_imgproc_inc.h"
|
||||
#include "mediapipe/framework/port/opencv_highgui_inc.h"
|
||||
#include "mediapipe/framework/port/status.h"
|
||||
#include "mediapipe/framework/port/logging.h"
|
||||
#include "mediapipe/framework/port/vector.h"
|
||||
#include "mediapipe/framework/port/file_helpers.h"
|
||||
#include "mediapipe/framework/deps/file_path.h"
|
||||
|
@ -46,20 +45,12 @@ namespace mediapipe
|
|||
namespace
|
||||
{
|
||||
|
||||
constexpr char kImageFrameTag[] = "IMAGE";
|
||||
constexpr char kImageSizeTag[] = "SIZE";
|
||||
constexpr char kVectorTag[] = "VECTOR";
|
||||
constexpr char kLandmarksTag[] = "LANDMARKS";
|
||||
constexpr char kNormLandmarksTag[] = "NORM_LANDMARKS";
|
||||
|
||||
tuple<int, int> _normalized_to_pixel_coordinates(float normalized_x,
|
||||
float normalized_y, int image_width, int image_height)
|
||||
{
|
||||
// Converts normalized value pair to pixel coordinates
|
||||
int x_px = min<int>(floor(normalized_x * image_width), image_width - 1);
|
||||
int y_px = min<int>(floor(normalized_y * image_height), image_height - 1);
|
||||
|
||||
return {x_px, y_px};
|
||||
};
|
||||
constexpr char kSrcTensorTag[] = "SRC_TENSOR";
|
||||
constexpr char kDstTensorTag[] = "DST_TENSOR";
|
||||
|
||||
inline bool HasImageTag(mediapipe::CalculatorContext *cc) { return false; }
|
||||
|
||||
|
@ -82,7 +73,6 @@ namespace mediapipe
|
|||
*x_px = static_cast<double>(normalized_x) * image_width;
|
||||
*y_px = static_cast<double>(normalized_y) * image_height;
|
||||
*z_px = static_cast<double>(normalized_z) * image_width;
|
||||
// 2280
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -123,18 +113,12 @@ namespace mediapipe
|
|||
absl::Status Close(CalculatorContext *cc) override;
|
||||
|
||||
private:
|
||||
absl::Status CreateRenderTargetCpu(CalculatorContext *cc,
|
||||
unique_ptr<Mat> &image_mat,
|
||||
ImageFormat::Format *target_format);
|
||||
|
||||
absl::Status RenderToCpu(
|
||||
CalculatorContext *cc, const ImageFormat::Format &target_format,
|
||||
uchar *data_image, unique_ptr<Mat> &image_mat);
|
||||
CalculatorContext *cc);
|
||||
|
||||
absl::Status SetData(CalculatorContext *cc);
|
||||
|
||||
absl::Status ProcessImage(CalculatorContext *cc,
|
||||
ImageFormat::Format &target_format);
|
||||
absl::Status ProcessImage(CalculatorContext *cc);
|
||||
|
||||
static absl::StatusOr<string> ReadContentBlobFromFile(
|
||||
const string &unresolved_path)
|
||||
|
@ -151,33 +135,16 @@ namespace mediapipe
|
|||
return content_blob;
|
||||
}
|
||||
|
||||
// Indicates if image frame is available as input.
|
||||
bool image_frame_available_ = false;
|
||||
|
||||
unique_ptr<Mat> image_mat;
|
||||
vector<string> index_names;
|
||||
map<string, vector<int>> indexes;
|
||||
|
||||
map<string, Tensor<double>> masks;
|
||||
vector<vector<int>> _trianglesIndexes;
|
||||
Tensor<double> __facePts;
|
||||
|
||||
int image_width_;
|
||||
int image_height_;
|
||||
|
||||
Mat mat_image_;
|
||||
};
|
||||
|
||||
absl::Status FaceProcessorCalculator::GetContract(CalculatorContract *cc)
|
||||
{
|
||||
CHECK_GE(cc->Inputs().NumEntries(), 1);
|
||||
|
||||
if (cc->Inputs().HasTag(kImageFrameTag))
|
||||
{
|
||||
cc->Inputs().Tag(kImageFrameTag).Set<ImageFrame>();
|
||||
CHECK(cc->Outputs().HasTag(kImageFrameTag));
|
||||
}
|
||||
|
||||
RET_CHECK(cc->Inputs().HasTag(kLandmarksTag) ||
|
||||
cc->Inputs().HasTag(kNormLandmarksTag))
|
||||
<< "None of the input streams are provided.";
|
||||
|
@ -195,9 +162,17 @@ namespace mediapipe
|
|||
cc->Inputs().Tag(kNormLandmarksTag).Set<vector<NormalizedLandmarkList>>();
|
||||
}
|
||||
|
||||
if (cc->Outputs().HasTag(kImageFrameTag))
|
||||
if (cc->Inputs().HasTag(kImageSizeTag))
|
||||
{
|
||||
cc->Outputs().Tag(kImageFrameTag).Set<ImageFrame>();
|
||||
cc->Inputs().Tag(kImageSizeTag).Set<pair<int, int>>();
|
||||
}
|
||||
if (cc->Outputs().HasTag(kSrcTensorTag))
|
||||
{
|
||||
cc->Outputs().Tag(kSrcTensorTag).Set<Tensor<double>>();
|
||||
}
|
||||
if (cc->Outputs().HasTag(kDstTensorTag))
|
||||
{
|
||||
cc->Outputs().Tag(kDstTensorTag).Set<Tensor<double>>();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
|
@ -207,52 +182,19 @@ namespace mediapipe
|
|||
{
|
||||
cc->SetOffset(TimestampDiff(0));
|
||||
|
||||
if (cc->Inputs().HasTag(kImageFrameTag) || HasImageTag(cc))
|
||||
{
|
||||
image_frame_available_ = true;
|
||||
}
|
||||
|
||||
// Set the output header based on the input header (if present).
|
||||
const char *tag = kImageFrameTag;
|
||||
if (image_frame_available_ && !cc->Inputs().Tag(tag).Header().IsEmpty())
|
||||
{
|
||||
const auto &input_header =
|
||||
cc->Inputs().Tag(tag).Header().Get<VideoHeader>();
|
||||
auto *output_video_header = new VideoHeader(input_header);
|
||||
cc->Outputs().Tag(tag).SetHeader(Adopt(output_video_header));
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status FaceProcessorCalculator::Process(CalculatorContext *cc)
|
||||
{
|
||||
if (cc->Inputs().HasTag(kImageFrameTag) &&
|
||||
cc->Inputs().Tag(kImageFrameTag).IsEmpty())
|
||||
{
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// Initialize render target, drawn with OpenCV.
|
||||
ImageFormat::Format target_format;
|
||||
|
||||
MP_RETURN_IF_ERROR(CreateRenderTargetCpu(cc, image_mat, &target_format));
|
||||
|
||||
mat_image_ = *image_mat.get();
|
||||
image_width_ = image_mat->cols;
|
||||
image_height_ = image_mat->rows;
|
||||
|
||||
MP_RETURN_IF_ERROR(SetData(cc));
|
||||
|
||||
if (cc->Inputs().HasTag(kNormLandmarksTag) &&
|
||||
!cc->Inputs().Tag(kNormLandmarksTag).IsEmpty())
|
||||
{
|
||||
MP_RETURN_IF_ERROR(ProcessImage(cc, target_format));
|
||||
MP_RETURN_IF_ERROR(ProcessImage(cc));
|
||||
}
|
||||
|
||||
uchar *image_mat_ptr = image_mat->data;
|
||||
MP_RETURN_IF_ERROR(RenderToCpu(cc, target_format, image_mat_ptr, image_mat));
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
|
@ -261,87 +203,10 @@ namespace mediapipe
|
|||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status FaceProcessorCalculator::RenderToCpu(
|
||||
CalculatorContext *cc, const ImageFormat::Format &target_format,
|
||||
uchar *data_image, unique_ptr<Mat> &image_mat)
|
||||
{
|
||||
|
||||
auto output_frame = absl::make_unique<ImageFrame>(
|
||||
target_format, mat_image_.cols, mat_image_.rows);
|
||||
|
||||
output_frame->CopyPixelData(target_format, mat_image_.cols, mat_image_.rows, data_image,
|
||||
ImageFrame::kDefaultAlignmentBoundary);
|
||||
|
||||
if (cc->Outputs().HasTag(kImageFrameTag))
|
||||
{
|
||||
cc->Outputs()
|
||||
.Tag(kImageFrameTag)
|
||||
.Add(output_frame.release(), cc->InputTimestamp());
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status FaceProcessorCalculator::CreateRenderTargetCpu(
|
||||
CalculatorContext *cc, unique_ptr<Mat> &image_mat,
|
||||
ImageFormat::Format *target_format)
|
||||
{
|
||||
if (image_frame_available_)
|
||||
{
|
||||
const auto &input_frame =
|
||||
cc->Inputs().Tag(kImageFrameTag).Get<ImageFrame>();
|
||||
|
||||
int target_mat_type;
|
||||
switch (input_frame.Format())
|
||||
{
|
||||
case ImageFormat::SRGBA:
|
||||
*target_format = ImageFormat::SRGBA;
|
||||
target_mat_type = CV_8UC4;
|
||||
break;
|
||||
case ImageFormat::SRGB:
|
||||
*target_format = ImageFormat::SRGB;
|
||||
target_mat_type = CV_8UC3;
|
||||
break;
|
||||
case ImageFormat::GRAY8:
|
||||
*target_format = ImageFormat::SRGB;
|
||||
target_mat_type = CV_8UC3;
|
||||
break;
|
||||
default:
|
||||
return absl::UnknownError("Unexpected image frame format.");
|
||||
break;
|
||||
}
|
||||
|
||||
image_mat = absl::make_unique<Mat>(
|
||||
input_frame.Height(), input_frame.Width(), target_mat_type);
|
||||
|
||||
auto input_mat = formats::MatView(&input_frame);
|
||||
|
||||
if (input_frame.Format() == ImageFormat::GRAY8)
|
||||
{
|
||||
Mat rgb_mat;
|
||||
cvtColor(input_mat, rgb_mat, CV_GRAY2RGBA);
|
||||
rgb_mat.copyTo(*image_mat);
|
||||
}
|
||||
else
|
||||
{
|
||||
input_mat.copyTo(*image_mat);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
image_mat = absl::make_unique<Mat>(
|
||||
1920, 1280, CV_8UC4,
|
||||
Scalar::all(255.0));
|
||||
*target_format = ImageFormat::SRGBA;
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status FaceProcessorCalculator::SetData(CalculatorContext *cc)
|
||||
{
|
||||
masks.clear();
|
||||
_trianglesIndexes.clear();
|
||||
masks = {};
|
||||
_trianglesIndexes = {};
|
||||
|
||||
string filename = "mediapipe/graphs/deformation/config/triangles.txt";
|
||||
string content_blob;
|
||||
|
@ -416,15 +281,16 @@ namespace mediapipe
|
|||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status FaceProcessorCalculator::ProcessImage(CalculatorContext *cc,
|
||||
ImageFormat::Format &target_format)
|
||||
absl::Status FaceProcessorCalculator::ProcessImage(CalculatorContext *cc)
|
||||
{
|
||||
double alfaNose = 0.7;
|
||||
double alfaLips = 0.2;
|
||||
double alfaCheekbones = 0.2;
|
||||
double alfaNose = 2.7;
|
||||
double alfaLips = 0.7;
|
||||
double alfaCheekbones = 0.7;
|
||||
|
||||
if (cc->Inputs().HasTag(kNormLandmarksTag))
|
||||
{
|
||||
const auto [image_width_, image_height_] = cc->Inputs().Tag(kImageSizeTag).Get<pair<int, int>>();
|
||||
|
||||
const vector<NormalizedLandmarkList> &landmarks =
|
||||
cc->Inputs().Tag(kNormLandmarksTag).Get<vector<NormalizedLandmarkList>>();
|
||||
|
||||
|
@ -460,10 +326,6 @@ namespace mediapipe
|
|||
_points[i][2] = z;
|
||||
}
|
||||
__facePts = Tensor<double>(_points, n, m);
|
||||
}
|
||||
|
||||
cvtColor(mat_image_, mat_image_, COLOR_BGRA2RGB);
|
||||
Mat clone_image = mat_image_.clone();
|
||||
|
||||
Tensor<double> ___facePts = __facePts - 0;
|
||||
|
||||
|
@ -519,74 +381,20 @@ namespace mediapipe
|
|||
|
||||
Tensor<double> _src = __facePts.index(_trianglesIndexes).index(_order);
|
||||
Tensor<double> _dst = ___facePts.index(_trianglesIndexes).index(_order);
|
||||
// cout << _src.get_dims().size() << endl;
|
||||
auto srcPtr = absl::make_unique<Tensor<double>>(_src);
|
||||
cc->Outputs().Tag(kSrcTensorTag).Add(srcPtr.release(), cc->InputTimestamp());
|
||||
|
||||
Mat outImage = mat_image_.clone();
|
||||
|
||||
for (int i = 0; i < 854; ++i)
|
||||
{
|
||||
if (i == 246)
|
||||
{
|
||||
int pointer = 0;
|
||||
}
|
||||
|
||||
Tensor<double> __t1 = _src.index(vector<int>{i});
|
||||
Tensor<double> __t2 = _dst.index(vector<int>{i});
|
||||
|
||||
vector<Point> t1;
|
||||
vector<Point> t2;
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
t1.push_back(Point(
|
||||
(int)(__t1.at(vector<int>{0, 3 * i})),
|
||||
(int)(__t1.at(vector<int>{0, 3 * i + 1}))));
|
||||
t2.push_back(Point(
|
||||
(int)(__t2.at(vector<int>{0, 3 * i})),
|
||||
(int)(__t2.at(vector<int>{0, 3 * i + 1}))));
|
||||
}
|
||||
|
||||
Rect r1 = boundingRect(t1);
|
||||
Rect r2 = boundingRect(t2);
|
||||
Point2f srcTri[3];
|
||||
Point2f dstTri[3];
|
||||
vector<Point> t1Rect;
|
||||
vector<Point> t2Rect;
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
srcTri[i] = Point2f(t1[i].x - r1.x, t1[i].y - r1.y);
|
||||
dstTri[i] = Point2f(t2[i].x - r2.x, t2[i].y - r2.y);
|
||||
t1Rect.push_back(Point(t1[i].x - r1.x, t1[i].y - r1.y));
|
||||
t2Rect.push_back(Point(t2[i].x - r2.x, t2[i].y - r2.y));
|
||||
}
|
||||
|
||||
Mat _dst;
|
||||
Mat mask = Mat::zeros(r2.height, r2.width, CV_8U);
|
||||
fillConvexPoly(mask, t2Rect, Scalar(1.0, 1.0, 1.0), 16, 0);
|
||||
|
||||
if (r1.x + r1.width < clone_image.cols && r1.x >= 0 && r1.x + r1.width >= 0 && r1.y >= 0 && r1.y
|
||||
< clone_image.rows && r1.y + r1.height < clone_image.rows)
|
||||
{
|
||||
Mat imgRect = mat_image_(Range(r1.y, r1.y + r1.height), Range(r1.x, r1.x + r1.width));
|
||||
Mat warpMat = getAffineTransform(srcTri, dstTri);
|
||||
warpAffine(imgRect, _dst, warpMat, mask.size());
|
||||
|
||||
for (int i = r2.y; i < r2.y + r2.height; ++i)
|
||||
{
|
||||
for (int j = r2.x; j < r2.x + r2.width; ++j)
|
||||
{
|
||||
if ((int)mask.at<uchar>(i - r2.y, j - r2.x) > 0)
|
||||
{
|
||||
outImage.at<Vec3b>(i, j) = _dst.at<Vec3b>(i - r2.y, j - r2.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cvtColor(outImage, *image_mat, COLOR_RGB2BGRA);
|
||||
auto dstPtr = absl::make_unique<Tensor<double>>(_dst);
|
||||
cc->Outputs().Tag(kDstTensorTag).Add(dstPtr.release(), cc->InputTimestamp());
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
else
|
||||
{
|
||||
return absl::OkStatus();
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_CALCULATOR(FaceProcessorCalculator);
|
||||
} // namespace mediapipe
|
||||
|
|
|
@ -0,0 +1,285 @@
|
|||
// Copyright 2019 The MediaPipe Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "Tensor.h"
|
||||
#include "mediapipe/framework/calculator_framework.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/logging.h"
|
||||
#include "mediapipe/framework/port/opencv_core_inc.h"
|
||||
#include "mediapipe/framework/port/opencv_imgproc_inc.h"
|
||||
#include "mediapipe/framework/port/opencv_highgui_inc.h"
|
||||
#include "mediapipe/framework/port/status.h"
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
namespace mediapipe
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr char kImageFrameTag[] = "IMAGE";
|
||||
constexpr char kSrcTag[] = "SRC_TENSOR";
|
||||
constexpr char kDstTag[] = "DST_TENSOR";
|
||||
|
||||
inline bool HasImageTag(mediapipe::CalculatorContext *cc) { return false; }
|
||||
} // namespace
|
||||
|
||||
class WarpAffineCalculator : public CalculatorBase
|
||||
{
|
||||
public:
|
||||
WarpAffineCalculator() = default;
|
||||
~WarpAffineCalculator() override = default;
|
||||
|
||||
static absl::Status GetContract(CalculatorContract *cc);
|
||||
|
||||
absl::Status Open(CalculatorContext *cc) override;
|
||||
absl::Status Process(CalculatorContext *cc) override;
|
||||
absl::Status Close(CalculatorContext *cc) override;
|
||||
|
||||
private:
|
||||
absl::Status CreateRenderTargetCpu(CalculatorContext *cc,
|
||||
std::unique_ptr<cv::Mat> &image_mat,
|
||||
ImageFormat::Format *target_format);
|
||||
|
||||
absl::Status RenderToCpu(
|
||||
CalculatorContext *cc, const ImageFormat::Format &target_format,
|
||||
uchar *data_image, std::unique_ptr<cv::Mat> &image_mat);
|
||||
|
||||
absl::Status AffineTransform(CalculatorContext *cc, std::unique_ptr<cv::Mat> &image_mat, Tensor<double> _src, Tensor<double> _dst);
|
||||
|
||||
bool image_frame_available_ = false;
|
||||
std::unique_ptr<Mat> image_mat;
|
||||
};
|
||||
|
||||
absl::Status WarpAffineCalculator::GetContract(CalculatorContract *cc)
|
||||
{
|
||||
RET_CHECK(cc->Inputs().HasTag(kImageFrameTag));
|
||||
|
||||
if (cc->Inputs().HasTag(kImageFrameTag))
|
||||
{
|
||||
cc->Inputs().Tag(kImageFrameTag).Set<ImageFrame>();
|
||||
}
|
||||
if (cc->Inputs().HasTag(kSrcTag))
|
||||
{
|
||||
cc->Inputs().Tag(kSrcTag).Set<Tensor<double>>();
|
||||
}
|
||||
if (cc->Inputs().HasTag(kDstTag))
|
||||
{
|
||||
cc->Inputs().Tag(kDstTag).Set<Tensor<double>>();
|
||||
}
|
||||
if (cc->Outputs().HasTag(kImageFrameTag))
|
||||
{
|
||||
cc->Outputs().Tag(kImageFrameTag).Set<ImageFrame>();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status WarpAffineCalculator::Open(CalculatorContext *cc)
|
||||
{
|
||||
cc->SetOffset(TimestampDiff(0));
|
||||
|
||||
if (cc->Inputs().HasTag(kImageFrameTag) || HasImageTag(cc))
|
||||
{
|
||||
image_frame_available_ = true;
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status WarpAffineCalculator::Process(CalculatorContext *cc)
|
||||
{
|
||||
if (cc->Inputs().Tag(kImageFrameTag).IsEmpty())
|
||||
{
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
ImageFormat::Format target_format;
|
||||
|
||||
MP_RETURN_IF_ERROR(CreateRenderTargetCpu(cc, image_mat, &target_format));
|
||||
|
||||
if (!cc->Inputs().Tag(kSrcTag).IsEmpty() && !cc->Inputs().Tag(kDstTag).IsEmpty())
|
||||
{
|
||||
const Tensor<double> _src = cc->Inputs().Tag(kSrcTag).Get<Tensor<double>>();
|
||||
const Tensor<double> _dst = cc->Inputs().Tag(kDstTag).Get<Tensor<double>>();
|
||||
MP_RETURN_IF_ERROR(AffineTransform(cc, image_mat, _src, _dst));
|
||||
}
|
||||
|
||||
// Copy the rendered image to output.
|
||||
uchar *image_mat_ptr = image_mat->data;
|
||||
MP_RETURN_IF_ERROR(RenderToCpu(cc, target_format, image_mat_ptr, image_mat));
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status WarpAffineCalculator::Close(CalculatorContext *cc)
|
||||
{
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status WarpAffineCalculator::RenderToCpu(
|
||||
CalculatorContext *cc, const ImageFormat::Format &target_format,
|
||||
uchar *data_image, std::unique_ptr<cv::Mat> &image_mat)
|
||||
{
|
||||
auto output_frame = absl::make_unique<ImageFrame>(
|
||||
target_format, image_mat->cols, image_mat->rows);
|
||||
|
||||
output_frame->CopyPixelData(target_format, image_mat->cols, image_mat->rows, data_image,
|
||||
ImageFrame::kDefaultAlignmentBoundary);
|
||||
|
||||
if (cc->Outputs().HasTag(kImageFrameTag))
|
||||
{
|
||||
cc->Outputs()
|
||||
.Tag(kImageFrameTag)
|
||||
.Add(output_frame.release(), cc->InputTimestamp());
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status WarpAffineCalculator::CreateRenderTargetCpu(
|
||||
CalculatorContext *cc, std::unique_ptr<cv::Mat> &image_mat,
|
||||
ImageFormat::Format *target_format)
|
||||
{
|
||||
if (image_frame_available_)
|
||||
{
|
||||
const auto &input_frame =
|
||||
cc->Inputs().Tag(kImageFrameTag).Get<ImageFrame>();
|
||||
|
||||
int target_mat_type;
|
||||
switch (input_frame.Format())
|
||||
{
|
||||
case ImageFormat::SRGBA:
|
||||
*target_format = ImageFormat::SRGBA;
|
||||
target_mat_type = CV_8UC4;
|
||||
break;
|
||||
case ImageFormat::SRGB:
|
||||
*target_format = ImageFormat::SRGB;
|
||||
target_mat_type = CV_8UC3;
|
||||
break;
|
||||
case ImageFormat::SBGR:
|
||||
*target_format = ImageFormat::SBGR;
|
||||
target_mat_type = CV_8UC3;
|
||||
break;
|
||||
case ImageFormat::GRAY8:
|
||||
*target_format = ImageFormat::SRGB;
|
||||
target_mat_type = CV_8UC3;
|
||||
break;
|
||||
default:
|
||||
return absl::UnknownError("Unexpected image frame format.");
|
||||
break;
|
||||
}
|
||||
|
||||
image_mat = absl::make_unique<cv::Mat>(
|
||||
input_frame.Height(), input_frame.Width(), target_mat_type);
|
||||
|
||||
auto input_mat = formats::MatView(&input_frame);
|
||||
|
||||
if (input_frame.Format() == ImageFormat::GRAY8)
|
||||
{
|
||||
cv::Mat rgb_mat;
|
||||
cv::cvtColor(input_mat, rgb_mat, CV_GRAY2RGB);
|
||||
rgb_mat.copyTo(*image_mat);
|
||||
}
|
||||
else
|
||||
{
|
||||
input_mat.copyTo(*image_mat);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
image_mat = absl::make_unique<cv::Mat>(
|
||||
1920, 1080, CV_8UC4,
|
||||
cv::Scalar(cv::Scalar::all(255)));
|
||||
*target_format = ImageFormat::SRGBA;
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status WarpAffineCalculator::AffineTransform(CalculatorContext *cc, std::unique_ptr<cv::Mat> &image_mat, Tensor<double> _src, Tensor<double> _dst)
|
||||
{
|
||||
Mat mat_image_ = *image_mat.get();
|
||||
Mat clone_image = mat_image_.clone();
|
||||
|
||||
Mat outImage = Mat(mat_image_.size(), mat_image_.type());
|
||||
Mat out = mat_image_.clone();
|
||||
|
||||
for (int i = 0; i < 854; ++i)
|
||||
{
|
||||
if (i == 246)
|
||||
{
|
||||
int pointer = 0;
|
||||
}
|
||||
Tensor<double> __t1 = _src.index(vector<int>{i});
|
||||
Tensor<double> __t2 = _dst.index(vector<int>{i});
|
||||
|
||||
vector<Point> t1;
|
||||
vector<Point> t2;
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
t1.push_back(Point(
|
||||
(int)(__t1.at(vector<int>{0, 3 * i})),
|
||||
(int)(__t1.at(vector<int>{0, 3 * i + 1}))));
|
||||
t2.push_back(Point(
|
||||
(int)(__t2.at(vector<int>{0, 3 * i})),
|
||||
(int)(__t2.at(vector<int>{0, 3 * i + 1}))));
|
||||
}
|
||||
|
||||
cv::Rect r1 = cv::boundingRect(t1);
|
||||
cv::Rect r2 = cv::boundingRect(t2);
|
||||
cv::Point2f srcTri[3];
|
||||
cv::Point2f dstTri[3];
|
||||
std::vector<cv::Point> t1Rect;
|
||||
std::vector<cv::Point> t2Rect;
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
srcTri[i] = Point2f(t1[i].x - r1.x, t1[i].y - r1.y);
|
||||
dstTri[i] = Point2f(t2[i].x - r2.x, t2[i].y - r2.y);
|
||||
t1Rect.push_back(Point(t1[i].x - r1.x, t1[i].y - r1.y));
|
||||
t2Rect.push_back(Point(t2[i].x - r2.x, t2[i].y - r2.y));
|
||||
}
|
||||
|
||||
Mat _dst;
|
||||
Mat mask = Mat::zeros(r2.height, r2.width, CV_8U);
|
||||
cv::fillConvexPoly(mask, t2Rect, Scalar(1.0, 1.0, 1.0), 16, 0);
|
||||
|
||||
if (r1.x + r1.width < clone_image.cols && r1.x >= 0 && r1.x + r1.width >= 0 && r1.y >= 0 && r1.y < clone_image.rows && r1.y + r1.height < clone_image.rows)
|
||||
{
|
||||
Mat imgRect = mat_image_(Range(r1.y, r1.y + r1.height), Range(r1.x, r1.x + r1.width));
|
||||
Mat warpMat = getAffineTransform(srcTri, dstTri);
|
||||
warpAffine(imgRect, _dst, warpMat, mask.size());
|
||||
|
||||
for (int i = r2.y; i < r2.y + r2.height; ++i)
|
||||
{
|
||||
for (int j = r2.x; j < r2.x + r2.width; ++j)
|
||||
{
|
||||
if ((int)mask.at<uchar>(i - r2.y, j - r2.x) > 0)
|
||||
{
|
||||
out.at<Vec3b>(i, j) = _dst.at<Vec3b>(i - r2.y, j - r2.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out.copyTo(*image_mat);
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
REGISTER_CALCULATOR(WarpAffineCalculator);
|
||||
} // namespace mediapipe
|
|
@ -6,6 +6,13 @@ input_stream: "input_video"
|
|||
# Output image with rendered results. (ImageFrame)
|
||||
output_stream: "output_video"
|
||||
|
||||
profiler_config {
|
||||
trace_enabled: true
|
||||
enable_profiler: true
|
||||
trace_log_interval_count: 200
|
||||
trace_log_path: "/home/mslight/Work/clone/mediapipe/mediapipe/logs/deformation/"
|
||||
}
|
||||
|
||||
node {
|
||||
calculator: "FlowLimiterCalculator"
|
||||
input_stream: "input_video"
|
||||
|
@ -40,9 +47,36 @@ node {
|
|||
output_stream: "LANDMARKS:multi_face_landmarks"
|
||||
}
|
||||
|
||||
node {
|
||||
calculator: "ColorConvertCalculator"
|
||||
input_stream: "RGBA_IN:throttled_input_video"
|
||||
output_stream: "BGR_OUT:throttled_input_video_bgr"
|
||||
}
|
||||
|
||||
node {
|
||||
calculator: "ImagePropertiesCalculator"
|
||||
input_stream: "IMAGE:throttled_input_video"
|
||||
output_stream: "SIZE:size"
|
||||
}
|
||||
|
||||
node {
|
||||
calculator: "FaceProcessorCalculator"
|
||||
input_stream: "NORM_LANDMARKS:multi_face_landmarks"
|
||||
input_stream: "IMAGE:throttled_input_video"
|
||||
output_stream: "IMAGE:output_video"
|
||||
input_stream: "SIZE:size"
|
||||
output_stream: "SRC_TENSOR:src"
|
||||
output_stream: "DST_TENSOR:dst"
|
||||
}
|
||||
|
||||
node {
|
||||
calculator: "WarpAffineCalculator"
|
||||
input_stream: "IMAGE:throttled_input_video_bgr"
|
||||
input_stream: "SRC_TENSOR:src"
|
||||
input_stream: "DST_TENSOR:dst"
|
||||
output_stream: "IMAGE:output_video_bgr"
|
||||
}
|
||||
|
||||
node{
|
||||
calculator: "ColorConvertCalculator"
|
||||
input_stream: "BGR_IN:output_video_bgr"
|
||||
output_stream: "RGBA_OUT:output_video"
|
||||
}
|
||||
|
|
|
@ -44,16 +44,43 @@ node {
|
|||
output_stream: "LANDMARKS:multi_face_landmarks"
|
||||
}
|
||||
|
||||
node {
|
||||
calculator: "ColorConvertCalculator"
|
||||
input_stream: "RGBA_IN:throttled_input_video_cpu"
|
||||
output_stream: "BGR_OUT:throttled_input_video_bgr"
|
||||
}
|
||||
|
||||
node {
|
||||
calculator: "ImagePropertiesCalculator"
|
||||
input_stream: "IMAGE_GPU:throttled_input_video"
|
||||
output_stream: "SIZE:size"
|
||||
}
|
||||
|
||||
node {
|
||||
calculator: "FaceProcessorCalculator"
|
||||
input_stream: "NORM_LANDMARKS:multi_face_landmarks"
|
||||
input_stream: "IMAGE:throttled_input_video_cpu"
|
||||
output_stream: "IMAGE:out_image_frame"
|
||||
input_stream: "SIZE:size"
|
||||
output_stream: "SRC_TENSOR:src"
|
||||
output_stream: "DST_TENSOR:dst"
|
||||
}
|
||||
|
||||
node {
|
||||
calculator: "WarpAffineCalculator"
|
||||
input_stream: "IMAGE:throttled_input_video_bgr"
|
||||
input_stream: "SRC_TENSOR:src"
|
||||
input_stream: "DST_TENSOR:dst"
|
||||
output_stream: "IMAGE:output_video_bgr"
|
||||
}
|
||||
|
||||
node{
|
||||
calculator: "ColorConvertCalculator"
|
||||
input_stream: "BGR_IN:output_video_bgr"
|
||||
output_stream: "RGBA_OUT:output_video_cpu"
|
||||
}
|
||||
|
||||
# Defines side packets for further use in the graph.
|
||||
node {
|
||||
calculator: "ImageFrameToGpuBufferCalculator"
|
||||
input_stream: "out_image_frame"
|
||||
input_stream: "output_video_cpu"
|
||||
output_stream: "output_video"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user