C++ 如何旋转图像中的特定部分?

C++ 如何旋转图像中的特定部分?,c++,opencv,image-processing,computer-vision,warp,C++,Opencv,Image Processing,Computer Vision,Warp,正如我在我的文章中提到的,我需要做一个应用程序,将正面的面部图像制作成动画图像。在这种情况下,主要部分是旋转头部。我曾经旋转图像 我所做的是选择面部区域,将其保存为图像并将其传递给旋转代码。然后我将旋转后的图像复制到原始图像。下面是旋转后的图像 我实际上需要的是避免黑色区域并进行扭曲。如何避免代码中的黑色区域 对于这种情况,您可以使用修补,我认为它会很好地工作 但也看看薄板样条曲线(在web中有几种实现)和分段仿射扭曲 我指的是下一步: 1) 使网格均匀或进行面三角剖分,然后根据需要变换网格节点

正如我在我的文章中提到的,我需要做一个应用程序,将正面的面部图像制作成动画图像。在这种情况下,主要部分是旋转头部。我曾经旋转图像

我所做的是选择面部区域,将其保存为图像并将其传递给旋转代码。然后我将旋转后的图像复制到原始图像。下面是旋转后的图像

我实际上需要的是避免黑色区域并进行扭曲。如何避免代码中的黑色区域


对于这种情况,您可以使用修补,我认为它会很好地工作

但也看看薄板样条曲线(在web中有几种实现)和分段仿射扭曲

我指的是下一步:

1) 使网格均匀或进行面三角剖分,然后根据需要变换网格节点(旋转面区域中的节点)

2) 应用变换(薄板样条曲线或分段仿射扭曲)

对不起,俄文的评论太长了,我无法全部翻译:)如果你有问题问我,我会尽力解释

库的包装器:

/#包括“triangle_wrapper.h”
#包括
使用名称空间std;
使用名称空间cv;
布尔myfunction(向量i,向量j)
{
返回(i[0]==j[0]&&i[1]==j[1]&&i[2]==j[2]);
}
布尔函数2(向量i)
{
返回值(i.size()==0);
}
//---------------------------------------------
// триангуляция. на входе вектор точек, на выходе вектор индексов точек по треугольникам
//--------------------------------------------- 
矢量三角化(矢量和pts)
{
矢量三角形;
结构三角形输入、输出、输出;
in.numberofpoints=pts.size();
in.numberofpointattributes=0;
in.pointlist=(REAL*)malloc(in.numberofpoints*2*sizeof(REAL));
in.pointmarkerlist=(int*)malloc(in.numberofpoints*sizeof(int));

for(inti=0;我想知道答案。你能解释一下什么是“修复”吗?我认为链接丢失了?在opencv的示例中,您可以找到inpaint.cpp示例。非常感谢。我将对此进行研究,并让您知道结果。我将等待一天来接受这个答案,因为其他专家可以查看并发布他们的观点。正如您在尝试这种旋转之前所说,我尝试了薄板样条()和分段仿射扭曲(),但当我移植到IPAD之类的设备时,我面临性能问题。所以我采用了这种方法。你知道薄板样条线或分段仿射扭曲的任何实现在性能上都很好吗?抱歉,麻烦更多了!我添加了一些代码来回答。有俄文注释,但我懒得翻译:)简单地说,它的工作原理如下:1)为每个图像指定一组点。2)对其中一组点进行三角剖分,从而获得点集的一组边。3)分别将参数为s0和s1的WarpAffine应用于源图像和目标图像的点集。三角形-每个三角形中点的索引。我很久以前就写过这篇文章了它工作正确,速度很快,但可能需要一些额外的边界检查。
    //#include "triangle_wrapper.h"
    #include <algorithm>
    using namespace std;
    using namespace cv;

    bool myfunction (vector<size_t> i, vector<size_t> j) 
    {
      return (i[0]==j[0] && i[1]==j[1] && i[2]==j[2]);
    }

    bool myfunction2 (vector<size_t> i) 
    {
      return (i.size()==0);
    }
    //---------------------------------------------
    // триангуляция. на входе вектор точек, на выходе вектор индексов точек по треугольникам
    //--------------------------------------------- 
    vector<vector<size_t>> Triangulate(vector<Point2d>& pts)
    {
        vector<vector<size_t>> triangles;


        struct triangulateio in, out, vorout;

        in.numberofpoints = pts.size();

        in.numberofpointattributes = 0;
        in.pointlist = (REAL *) malloc(in.numberofpoints * 2 * sizeof(REAL));
        in.pointmarkerlist = (int *) malloc(in.numberofpoints * sizeof(int));

        for(int i=0;i<pts.size();i++)
        {
            in.pointlist[2*i] = pts[i].x;
            in.pointlist[2*i+1] = pts[i].y;
            in.pointmarkerlist[i]=0;
        }
        in.numberofsegments = 0;
        in.numberofholes = 0;
        in.numberofregions = 0;

        out.pointlist = (REAL *) NULL;
        out.pointattributelist = (REAL *) NULL;
        out.pointmarkerlist = (int *) NULL;
        out.trianglelist = (int *) NULL;
        out.triangleattributelist = (REAL *) NULL;
        out.neighborlist = (int *) NULL;
        out.segmentlist = (int *) NULL;
        out.segmentmarkerlist = (int *) NULL;
        out.edgelist = (int *) NULL;
        out.edgemarkerlist = (int *) NULL;
        vorout.pointlist = (REAL *) NULL;
        vorout.pointattributelist = (REAL *) NULL;
        vorout.edgelist = (int *) NULL;
        vorout.normlist = (REAL *) NULL;

        // хелп по переключателям здесь:
        // http://www.cs.cmu.edu/~quake/triangle.switch.html

        triangulate("pczeQ", &in, &out, &vorout);

        for (int i = 0; i < out.numberoftriangles; i++) 
        {
            vector<size_t> idx(3);
            for (int j = 0; j < out.numberofcorners; j++) 
            {
                idx[j]=out.trianglelist[i * out.numberofcorners + j];
            }
            triangles.push_back(idx);
        }

        free(in.pointlist);
        free(in.pointmarkerlist);

        free(out.pointlist);
        free(out.pointattributelist);
        free(out.trianglelist);
        free(out.triangleattributelist);

        cout << "triangles.size()" <<triangles.size() << endl;

        return triangles;
    }
#include "warpaffine.h"
#include <omp.h>
using namespace std;
using namespace cv;

// --------------------------------------------------------------
// Вычисление габаритного прямоугольника для точек типа Point2d
// --------------------------------------------------------------
cv::Rect_<double> boundingRect(vector<Point2d>& pts)
{
    cv::Rect_<double> r;
    double minx=FLT_MAX,maxx=FLT_MIN,miny=FLT_MAX,maxy=FLT_MIN;

    for(int i=0;i<pts.size();i++)
    {
        double px=pts[i].x;
        double py=pts[i].y;
        if(minx>px){minx=px;}
        if(miny>py){miny=py;}
        if(maxx<px){maxx=px;}
        if(maxy<py){maxy=py;}
    }

    r.x=minx;
    r.y=miny;
    r.width=maxx-minx;
    r.height=maxy-miny;

    return r;
}

// --------------------------------------------------------------
// Создаем разметку точек, по принадлежности к треугольникам
// --------------------------------------------------------------
void DrawLabelsMask(Mat& imgLabel,vector<Point2d>& points,vector<vector<size_t>>& triangles)
{
    for(int i=0;i<triangles.size();i++)
    {
        Point t[3];
        int ind1=triangles[i][0];
        int ind2=triangles[i][1];
        int ind3=triangles[i][2];
        t[0].x=cvRound(points[ind1].x);
        t[0].y=cvRound(points[ind1].y);
        t[1].x=cvRound(points[ind2].x);
        t[1].y=cvRound(points[ind2].y);
        t[2].x=cvRound(points[ind3].x);
        t[2].y=cvRound(points[ind3].y);
        cv::fillConvexPoly(imgLabel, t, 3, cv::Scalar_<int>((i+1)));
    }
}
// --------------------------------------------------------------
// Предварительный расчет коэффициентов преобразования для пар треугольников
// --------------------------------------------------------------
void CalcCoeffs(vector<Point2d>& s_0,vector<Point2d>& s_1, vector<vector<size_t>>& triangles, Mat& Coeffs)
{
    Rect_<double> Bound_0;
    Rect_<double> Bound_1;
    // Вычислили габариты
    Bound_0=boundingRect(s_0);
    Bound_1=boundingRect(s_1);
    // Предварительный расчет коэффициентов преобразования для пар треугольников
    Coeffs=Mat(triangles.size(),6,CV_64FC1);
#ifdef _OPENMP
#pragma omp parallel for
#endif
    for(int i=0;i<triangles.size();i++)
    {
        int ind1=triangles[i][0];
        int ind2=triangles[i][1];
        int ind3=triangles[i][2];
        // Исходные точки (откуда берем)
        Point2d t_0[3];
        t_0[0]=s_0[ind1]-Bound_0.tl(); // i
        t_0[1]=s_0[ind2]-Bound_0.tl(); // j
        t_0[2]=s_0[ind3]-Bound_0.tl(); // k
        // Целевые точки (куда кладем)
        Point2d t_1[3];
        t_1[0]=s_1[ind1]-Bound_1.tl(); // i
        t_1[1]=s_1[ind2]-Bound_1.tl(); // j
        t_1[2]=s_1[ind3]-Bound_1.tl(); // k

        double denom=(t_1[0].x * t_1[1].y + t_1[2].y * t_1[1].x - t_1[0].x * t_1[2].y - t_1[2].x * t_1[1].y - t_1[0].y * t_1[1].x + t_1[0].y * t_1[2].x);

        Coeffs.at<double>(i,0)= -(-t_1[2].y * t_0[1].x + t_1[2].y * t_0[0].x + t_1[1].y * t_0[2].x - t_1[1].y * t_0[0].x - t_1[0].y * t_0[2].x + t_1[0].y * t_0[1].x) / denom;
        Coeffs.at<double>(i,1)= -(t_1[2].x * t_0[1].x - t_1[2].x * t_0[0].x - t_1[1].x * t_0[2].x + t_1[1].x * t_0[0].x + t_1[0].x * t_0[2].x - t_1[0].x * t_0[1].x) / denom;
        Coeffs.at<double>(i,2)= -(t_1[2].x * t_1[1].y * t_0[0].x - t_1[2].x * t_1[0].y * t_0[1].x - t_1[1].x * t_1[2].y * t_0[0].x + t_1[1].x * t_1[0].y * t_0[2].x + t_1[0].x * t_1[2].y * t_0[1].x - t_1[0].x * t_1[1].y * t_0[2].x)/denom;
        Coeffs.at<double>(i,3)= -(t_1[1].y * t_0[2].y - t_1[0].y * t_0[2].y - t_1[2].y * t_0[1].y + t_1[2].y * t_0[0].y - t_0[0].y * t_1[1].y + t_0[1].y * t_1[0].y) / denom;
        Coeffs.at<double>(i,4)= -(-t_1[2].x * t_0[0].y + t_1[0].x * t_0[2].y + t_1[2].x * t_0[1].y - t_0[1].y * t_1[0].x - t_1[1].x * t_0[2].y + t_0[0].y * t_1[1].x) / denom;
        Coeffs.at<double>(i,5)= -(t_0[0].y * t_1[1].y * t_1[2].x - t_0[2].y * t_1[0].x * t_1[1].y - t_0[1].y * t_1[0].y * t_1[2].x + t_0[1].y * t_1[0].x * t_1[2].y + t_0[2].y * t_1[0].y * t_1[1].x - t_0[0].y * t_1[1].x * t_1[2].y) / denom;
    }
}

// --------------------------------------------------------------
// Переносит изображение из img с сеткой на точках s_0
// в изображение dst с сеткой на точках s_1
// Сетка задается треугольниками.
// triangles - вектор треугольников.
// Каждый треугольник - 3 индекса вершин.
// --------------------------------------------------------------
void WarpAffine(Mat& img,vector<Point2d>& s_0,vector<Point2d>& s_1, vector<vector<size_t>>& triangles, Mat& dstLabelsMask,Mat& dst)
{
    Rect_<double> Bound_0;
    Rect_<double> Bound_1;

    // ROI (все точки должны лежать в пределах своих изображений)
    // Вычислили габариты
    Bound_0=boundingRect(s_0);
    Bound_1=boundingRect(s_1);  

    Bound_1.width=cvRound(Bound_1.width);
    Bound_1.height=cvRound(Bound_1.height);

    Bound_0.width=cvRound(Bound_0.width);
    Bound_0.height=cvRound(Bound_0.height);

    if(Bound_0.br().x>img.cols-1){Bound_0.width=(double)img.cols-1-Bound_0.x;}
    if(Bound_0.br().y>img.rows-1){Bound_0.height=(double)img.rows-1-Bound_0.y;}


    Mat I_0=img(Bound_0);

    // Переводим координаты точек в систему координат ROI
    for(int i=0;i<s_1.size();i++)
    {
    s_1[i]-=Bound_1.tl();
    }

    // Корректируем границы
    if(Bound_1.x<0)
    {
        Bound_1.x=0;
    }

    if(Bound_1.y<0)
    {
        Bound_1.y=0;
    }

    if(Bound_1.br().x>dst.cols-1)
    {
        Bound_1.width=(double)dst.cols-1-Bound_1.x;
    }

    if(Bound_1.br().y>dst.rows-1)
    {
        Bound_1.height=(double)dst.rows-1-Bound_1.y;
    }


    // Назначаем ROI
    Mat I_1=dst(Bound_1);

    // Предварительный расчет коэффициентов преобразования для пар треугольников
    Mat Coeffs;
    CalcCoeffs(s_0,s_1,triangles,Coeffs);

    // Сканируем изображение и переносим с него точки на шаблон
    #ifdef _OPENMP
    #pragma omp parallel for
    #endif
    for(int i=0;i<I_1.rows;i++)
    {
        Point2d W(0,0);
        for(int j=0;j<I_1.cols;j++)
        {
            double x=j;
            double y=i;
            int Label=dstLabelsMask.at<int>(i,j)-1;
            if(Label!=(-1))
            {               
                W.x=Coeffs.at<double>(Label,0)*x+Coeffs.at<double>(Label,1)*y+Coeffs.at<double>(Label,2);
                W.y=Coeffs.at<double>(Label,3)*x+Coeffs.at<double>(Label,4)*y+Coeffs.at<double>(Label,5);
                if(cvRound(W.x)>0 && cvRound(W.x)<I_0.cols && cvRound(W.y)>0 && cvRound(W.y)<I_0.rows)
                {
                    I_1.at<Vec3b>(i,j)=I_0.at<Vec3b>(cvRound(W.y),cvRound(W.x));
                }
            }
        }
    }
    cv::GaussianBlur(I_1,I_1,Size(3,3),0.5);    
}