c++图像处理程序编译成.so动态链接库供Java调用(包含jni文件与引用第三方动态链接库opencv)

一、linux编译so文件需要准备的环境

1、安装JDK(注意:不能安装openjdk,因为openjdk没有include目录,编译时需要用到include目录的头文件)

2、安装gcc和g++  ( yum install gcc-c++) 、(yum install cmake3)

3、本案例使用到第三方opencv动态链接库,所以需要安装opencv
3.1:首先官网下载opencv,地址:https://opencv.org/releases/

3.2:tar -zxvf opencv-4.5.0.zip -C /opt/module/

3.3:cd opencv-4.5.0  ->mkdir build->cd build->cmake -D CMAKE_BUILD_TYPE=RELEASE -D OPENCV_GENERATE_PKGCONFIG=ON -D CMAKE_INSTALL_PREFIX=/usr/local ..(此操作不能丢失..,编译时间比较久)

3.4:make install,头文件被放在/usr/local/include/opencv4/opencv2/目录下,我们的库文件被放在/usr/local/lib64/目录下

 

 查看opencv的动态库文件存在地址

 

 3.5:查看opencv安装版本

 3.6:建立软连接:sudo ln -s /usr/local/lib64/pkgconfig/opencv4.pc /usr/share/pkgconfig/

 3.7:pkg-config --cflags --libs opencv4

 

4、为防止生成的so文件找不到依赖库,使用vim /etc/ld.so.conf.d/opencv4.conf,保存并使用此命令让其生效:sudo ldconfig(/etc/Id.so.conf详解:https://www.cnblogs.com/chris-cp/p/3591306.html)

 注意出现:undefined symbol,就是因为找不到opencv的一些so依赖库导致的

/usr/local/lib64

5、需要使用到的cpp文件

5.1:com_htzs_insar_jni_Registration.cpp

#include<stdio.h>
#include"com_htzs_insar_jni_Registration.h"
#include"ComplexMat.h"
#include"Utils.h"
#include"Registration.h"

using namespace InSAR;
using namespace cv;
static const char* const Pointclasspath = "com/htzs/insar/jni/Point";
static const char* const Fitclasspath = "com/htzs/insar/jni/Fit";
/** @brief 抛出异常

@param env                JNIEnv指针
@param msg                异常情况
*/
void ThrowException(JNIEnv* env, const char* msg)
{
    jclass cls = env->FindClass("com/htzs/insar/jni/InSAR_JNIException");
    if (!cls) return;
    jmethodID methodID = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;)V");
    if (!methodID) return;
    char buffer[512] = { 0 };
    sprintf(buffer, "InSAR_JNIException !: %s", msg);
    jstring str = env->NewStringUTF(buffer);
    jthrowable thr = (jthrowable)env->NewObject(cls, methodID, str);
    env->Throw(thr);
    env->DeleteLocalRef(str);
    env->DeleteLocalRef(thr);
}
/** @brief 插值并计算偏移量

@param lineSize                     每行元素数量
@param master_real                  主图像实部数组
@param master_imag                  主图像虚部数组
@param slave_real                   辅图像实部数组
@param slave_imag                   辅图像虚部数组
@return offset                      偏移量
*/
JNIEXPORT jobject JNICALL Java_com_htzs_insar_jni_Registration_calcOffset(
    JNIEnv* env,
    jclass,
    jint lineSize,
    jshortArray master_real,
    jshortArray master_imag,
    jshortArray slave_real,
    jshortArray slave_imag
)
{
    char msg[512];
    //获取主辅图像数据
    jint num = env->GetArrayLength(master_real);
    jint rows = num / lineSize;
    ComplexMat master(rows, lineSize), slave(rows, lineSize);
    master.convertTo(master, CV_16S);
    slave.convertTo(slave, CV_16S);
    jshort* data = NULL;
    data = env->GetShortArrayElements(master_real, NULL);
    memcpy(master.re.data, data, sizeof(short) * num);
    env->ReleaseShortArrayElements(master_real, data, 0);

    data = env->GetShortArrayElements(master_imag, NULL);
    memcpy(master.im.data, data, sizeof(short) * num);
    env->ReleaseShortArrayElements(master_imag, data, 0);

    data = env->GetShortArrayElements(slave_real, NULL);
    memcpy(slave.re.data, data, sizeof(short) * num);
    env->ReleaseShortArrayElements(slave_real, data, 0);

    data = env->GetShortArrayElements(slave_imag, NULL);
    memcpy(slave.im.data, data, sizeof(short) * num);
    env->ReleaseShortArrayElements(slave_imag, data, 0);

    //short型转换至double型
    master.convertTo(master, CV_64F);
    slave.convertTo(slave, CV_64F);

    //插值8倍并计算偏移量

    Registration regis; Utils util;
    ComplexMat master_tmp, slave_tmp;
    Mat coherence; double coh = 0.0; int offset_row, offset_col;
    int ret;

    //粗配准
    ComplexMat master_temp, slave_temp;
    int move_r, move_c;
    ret = regis.registration_pixel(master, slave, &move_r, &move_c);
    if (ret < 0)
    {
        sprintf(msg, "(line %d in file %s)!\n", __LINE__, __FILE__);
        ThrowException(env, msg);
        return jobject();
    }
    ret = util.complex_coherence(master, slave, coherence);
    if (ret < 0)
    {
        sprintf(msg, "(line %d in file %s)!\n", __LINE__, __FILE__);
        ThrowException(env, msg);
        return jobject();
    }
    coh = mean(coherence)[0];
    int nr = master.GetRows(); int nc = master.GetCols();
    if (nr == 1 || nc == 1)
    {
        sprintf(msg, "(line %d in file %s)!\n", __LINE__, __FILE__);
        ThrowException(env, msg);
        return jobject();
    }
    nr = nr % 2 == 0 ? nr : nr - 1;
    nc = nc % 2 == 0 ? nc : nc - 1;
    master = master(Range(0, nr), Range(0, nc));
    slave = slave(Range(0, nr), Range(0, nc));

    ret = regis.interp_paddingzero(master, master_temp, 8);
    if (ret < 0)
    {
        sprintf(msg, "(line %d in file %s)!\n", __LINE__, __FILE__);
        ThrowException(env, msg);
        return jobject();
    }
    ret = regis.interp_paddingzero(slave, slave_temp, 8);
    if (ret < 0)
    {
        sprintf(msg, "(line %d in file %s)!\n", __LINE__, __FILE__);
        ThrowException(env, msg);
        return jobject();
    }
    ret = regis.real_coherent(master_temp, slave_temp, &offset_row, &offset_col);
    if (ret < 0)
    {
        sprintf(msg, "(line %d in file %s)!\n", __LINE__, __FILE__);
        ThrowException(env, msg);
        return jobject();
    }


    jclass Point = env->FindClass(Pointclasspath);
    jmethodID methodID = env->GetMethodID(Point, "<init>", "()V");
    jobject p = env->NewObject(Point, methodID);

    jfieldID fid = env->GetFieldID(Point, "row", "D");
    jfieldID fid2 = env->GetFieldID(Point, "col", "D");
    jfieldID fid3 = env->GetFieldID(Point, "coherence", "D");
    env->SetDoubleField(p, fid, (double)offset_row / 8.0 + (double)move_r);
    env->SetDoubleField(p, fid2, (double)offset_col / 8.0 + (double)move_c);
    env->SetDoubleField(p, fid3, coh);

    return p;

}


/** @brief 根据各子块中心坐标和偏移量拟合整张图每个像素的偏移量

@param offset                            各子块偏移量数组
@param center_coordinate                 各子块中心坐标数组
@return Fit                              偏移量拟合系数(a0-a5,b0-b5)
*/
JNIEXPORT jobject JNICALL Java_com_htzs_insar_jni_Registration_fitting
(JNIEnv* env, jclass job, jobjectArray offset_array, jobjectArray center_coordinate_array, jdouble coh_thresh)
{
    //读取子块中心坐标和偏移量数据
    char msg[512];
    jint num_obj = env->GetArrayLength(offset_array);
    if (num_obj < 7)
    {
        sprintf(msg, "(line %d in file %s)!\n", __LINE__, __FILE__);
        ThrowException(env, msg);
        return jobject();
    }
    Mat offset_r, offset_c, offset_coord_row, offset_coord_col, coh_array;
    offset_r.create(num_obj, 1, CV_64F); offset_c.create(num_obj, 1, CV_64F);
    offset_coord_row.create(num_obj, 1, CV_64F); offset_coord_col.create(num_obj, 1, CV_64F); coh_array.create(num_obj, 1, CV_64F);
    jclass Point = env->FindClass(Pointclasspath);
    jfieldID offset_row_ID = env->GetFieldID(Point, "row", "D");
    jfieldID offset_col_ID = env->GetFieldID(Point, "col", "D");
    jfieldID coh_ID = env->GetFieldID(Point, "coherence", "D");
    jobject offset;
    jdouble offset_col, offset_row, coh;
    double count = 0;
    coh_thresh = coh_thresh > 0.85 ? 0.85 : coh_thresh;
    for (jint i = 0; i < num_obj; i++)
    {
        offset = env->GetObjectArrayElement(offset_array, i);
        offset_row = env->GetDoubleField(offset, offset_row_ID);
        offset_col = env->GetDoubleField(offset, offset_col_ID);
        coh = env->GetDoubleField(offset, coh_ID);
        env->DeleteLocalRef(offset);

        offset_r.at<double>(i, 0) = offset_row;
        offset_c.at<double>(i, 0) = offset_col;
        coh_array.at<double>(i, 0) = coh;
        if (coh > coh_thresh) count++;

        offset = env->GetObjectArrayElement(center_coordinate_array, i);
        offset_row = env->GetDoubleField(offset, offset_row_ID);
        offset_col = env->GetDoubleField(offset, offset_col_ID);
        env->DeleteLocalRef(offset);

        offset_coord_row.at<double>(i, 0) = offset_row;
        offset_coord_col.at<double>(i, 0) = offset_col;
    }
    if (count < 7)
    {
        sprintf(msg, "(line %d in file %s)!\n", __LINE__, __FILE__);
        ThrowException(env, msg);
        return jobject();
    }


    //利用相关系数筛掉配准异常子块的偏移量

    Mat offset_coord_row_sifted, offset_coord_col_sifted, offset_r_sifted, offset_c_sifted;
    offset_coord_row_sifted.create(count, 1, CV_64F);
    offset_coord_col_sifted.create(count, 1, CV_64F);
    offset_r_sifted.create(count, 1, CV_64F);
    offset_c_sifted.create(count, 1, CV_64F);
    count = 0;
    for (int i = 0; i < num_obj; i++)
    {
        if (coh_array.at<double>(i, 0) > coh_thresh)
        {
            offset_coord_row_sifted.at<double>(count, 0) = offset_coord_row.at<double>(i, 0);
            offset_coord_col_sifted.at<double>(count, 0) = offset_coord_col.at<double>(i, 0);
            offset_r_sifted.at<double>(count, 0) = offset_r.at<double>(i, 0);
            offset_c_sifted.at<double>(count, 0) = offset_c.at<double>(i, 0);
            count++;
        }
    }
    offset_coord_row_sifted.copyTo(offset_coord_row);
    offset_coord_col_sifted.copyTo(offset_coord_col);
    offset_r_sifted.copyTo(offset_r);
    offset_c_sifted.copyTo(offset_c);
    num_obj = count;
    Mat A = Mat::ones(num_obj, 6, CV_64F);
    Mat temp, A_t;
    offset_coord_col.copyTo(A(Range(0, num_obj), Range(1, 2)));

    offset_coord_row.copyTo(A(Range(0, num_obj), Range(2, 3)));

    temp = offset_coord_col.mul(offset_coord_row);
    temp.copyTo(A(Range(0, num_obj), Range(3, 4)));

    temp = offset_coord_col.mul(offset_coord_col);
    temp.copyTo(A(Range(0, num_obj), Range(4, 5)));

    temp = offset_coord_row.mul(offset_coord_row);
    temp.copyTo(A(Range(0, num_obj), Range(5, 6)));

    transpose(A, A_t);

    Mat b_r, b_c, coef_r, coef_c, error_r, error_c, b_t, a, a_t;

    A.copyTo(a);
    cv::transpose(a, a_t);
    offset_r.copyTo(b_r);
    b_r = A_t * b_r;

    offset_c.copyTo(b_c);
    b_c = A_t * b_c;

    A = A_t * A;

    double rms1 = -1.0; double rms2 = -1.0;
    Mat eye = Mat::zeros(num_obj, num_obj, CV_64F);
    for (int i = 0; i < num_obj; i++)
    {
        eye.at<double>(i, i) = 1.0;
    }
    if (cv::invert(A, error_r, cv::DECOMP_LU) > 0)
    {
        cv::transpose(offset_r, b_t);
        error_r = b_t * (eye - a * error_r * a_t) * offset_r;
        rms1 = sqrt(error_r.at<double>(0, 0) / double(num_obj));
    }
    if (cv::invert(A, error_c, cv::DECOMP_LU) > 0)
    {
        cv::transpose(offset_c, b_t);
        error_c = b_t * (eye - a * error_c * a_t) * offset_c;
        rms2 = sqrt(error_c.at<double>(0, 0) / double(num_obj));
    }
    if (!cv::solve(A, b_r, coef_r, cv::DECOMP_NORMAL))
    {
        sprintf(msg, "(line %d in file %s)!\n", __LINE__, __FILE__);
        ThrowException(env, msg);
        return jobject();
    }
    if (!cv::solve(A, b_c, coef_c, cv::DECOMP_NORMAL))
    {
        sprintf(msg, "(line %d in file %s)!\n", __LINE__, __FILE__);
        ThrowException(env, msg);
        return jobject();
    }

    jclass Fit = env->FindClass(Fitclasspath);
    jmethodID init_ID = env->GetMethodID(Fit, "<init>", "()V");
    jobject fit = env->NewObject(Fit, init_ID);
    jfieldID a0 = env->GetFieldID(Fit, "a0", "D");
    jfieldID a1 = env->GetFieldID(Fit, "a1", "D");
    jfieldID a2 = env->GetFieldID(Fit, "a2", "D");
    jfieldID a3 = env->GetFieldID(Fit, "a3", "D");
    jfieldID a4 = env->GetFieldID(Fit, "a4", "D");
    jfieldID a5 = env->GetFieldID(Fit, "a5", "D");
    jfieldID b0 = env->GetFieldID(Fit, "b0", "D");
    jfieldID b1 = env->GetFieldID(Fit, "b1", "D");
    jfieldID b2 = env->GetFieldID(Fit, "b2", "D");
    jfieldID b3 = env->GetFieldID(Fit, "b3", "D");
    jfieldID b4 = env->GetFieldID(Fit, "b4", "D");
    jfieldID b5 = env->GetFieldID(Fit, "b5", "D");

    env->SetDoubleField(fit, a0, coef_r.at<double>(0, 0));
    env->SetDoubleField(fit, a1, coef_r.at<double>(1, 0));
    env->SetDoubleField(fit, a2, coef_r.at<double>(2, 0));
    env->SetDoubleField(fit, a3, coef_r.at<double>(3, 0));
    env->SetDoubleField(fit, a4, coef_r.at<double>(4, 0));
    env->SetDoubleField(fit, a5, coef_r.at<double>(5, 0));
    env->SetDoubleField(fit, b0, coef_c.at<double>(0, 0));
    env->SetDoubleField(fit, b1, coef_c.at<double>(1, 0));
    env->SetDoubleField(fit, b2, coef_c.at<double>(2, 0));
    env->SetDoubleField(fit, b3, coef_c.at<double>(3, 0));
    env->SetDoubleField(fit, b4, coef_c.at<double>(4, 0));
    env->SetDoubleField(fit, b5, coef_c.at<double>(5, 0));

    return fit;
}

/** @brief 根据偏移量拟合系数插值得到配准后的SAR图像

@param lineSize                       每行元素个数
@param real                           实部数据
@param imag                           虚部数据
@param center                         子块中心点坐标
@param fit                            12个偏移量拟合系数
@param overlapSize                    瓦片四周重叠部分长度
@return 配准后的SAR复图像(实部为前一半数据,虚部为后一半数据)
*/
JNIEXPORT jshortArray JNICALL Java_com_htzs_insar_jni_Registration_registration
(JNIEnv* env, jclass job, jint lineSize, jshortArray real, jshortArray imag, jobject center, jobject fit, jint overlapSize)
{
    jint num = env->GetArrayLength(real);
    jint rows = num / lineSize;
    jint cols = lineSize;
    ComplexMat slave(rows, lineSize);
    slave.convertTo(slave, CV_16S);
    jshort* data = env->GetShortArrayElements(real, NULL);
    memcpy(slave.re.data, data, sizeof(short) * num);
    env->ReleaseShortArrayElements(real, data, 0);
    data = env->GetShortArrayElements(imag, NULL);
    memcpy(slave.im.data, data, sizeof(short) * num);
    env->ReleaseShortArrayElements(imag, data, 0);

    jclass Point = env->FindClass(Pointclasspath);
    jfieldID row_ID = env->GetFieldID(Point, "row", "D");
    jfieldID col_ID = env->GetFieldID(Point, "col", "D");

    double center_rows, center_cols;
    center_rows = env->GetDoubleField(center, row_ID);
    center_cols = env->GetDoubleField(center, col_ID);

    int row_start = (int)round(center_rows - (double)rows / 2.0);
    int col_start = (int)round(center_cols - (double)cols / 2.0);

    jclass Fit = env->FindClass(Fitclasspath);
    jfieldID a0 = env->GetFieldID(Fit, "a0", "D");
    jfieldID a1 = env->GetFieldID(Fit, "a1", "D");
    jfieldID a2 = env->GetFieldID(Fit, "a2", "D");
    jfieldID a3 = env->GetFieldID(Fit, "a3", "D");
    jfieldID a4 = env->GetFieldID(Fit, "a4", "D");
    jfieldID a5 = env->GetFieldID(Fit, "a5", "D");
    jfieldID b0 = env->GetFieldID(Fit, "b0", "D");
    jfieldID b1 = env->GetFieldID(Fit, "b1", "D");
    jfieldID b2 = env->GetFieldID(Fit, "b2", "D");
    jfieldID b3 = env->GetFieldID(Fit, "b3", "D");
    jfieldID b4 = env->GetFieldID(Fit, "b4", "D");
    jfieldID b5 = env->GetFieldID(Fit, "b5", "D");

    Mat coef_r(6, 1, CV_64F), coef_c(6, 1, CV_64F);
    coef_r.at<double>(0, 0) = env->GetDoubleField(fit, a0);
    coef_r.at<double>(1, 0) = env->GetDoubleField(fit, a1);
    coef_r.at<double>(2, 0) = env->GetDoubleField(fit, a2);
    coef_r.at<double>(3, 0) = env->GetDoubleField(fit, a3);
    coef_r.at<double>(4, 0) = env->GetDoubleField(fit, a4);
    coef_r.at<double>(5, 0) = env->GetDoubleField(fit, a5);

    coef_c.at<double>(0, 0) = env->GetDoubleField(fit, b0);
    coef_c.at<double>(1, 0) = env->GetDoubleField(fit, b1);
    coef_c.at<double>(2, 0) = env->GetDoubleField(fit, b2);
    coef_c.at<double>(3, 0) = env->GetDoubleField(fit, b3);
    coef_c.at<double>(4, 0) = env->GetDoubleField(fit, b4);
    coef_c.at<double>(5, 0) = env->GetDoubleField(fit, b5);

    ComplexMat slave_tmp;
    slave_tmp = slave;
#pragma omp parallel for schedule(guided)
    for (int i = overlapSize; i < rows - overlapSize; i++)
    {
        double x, y, ii, jj; Mat tmp(1, 6, CV_64F); Mat result;
        int mm, nn, mm1, nn1;
        double offset_rows, offset_cols, upper, lower;
        for (int j = overlapSize; j < cols - overlapSize; j++)
        {
            x = (double)j + (double)col_start;
            y = (double)i + (double)row_start;
            ii = (double)i; jj = (double)j;
            tmp.at<double>(0, 0) = 1.0;
            tmp.at<double>(0, 1) = x;
            tmp.at<double>(0, 2) = y;
            tmp.at<double>(0, 3) = x * y;
            tmp.at<double>(0, 4) = x * x;
            tmp.at<double>(0, 5) = y * y;
            result = tmp * coef_r;
            offset_rows = result.at<double>(0, 0);
            result = tmp * coef_c;
            offset_cols = result.at<double>(0, 0);

            ii += offset_rows;
            jj += offset_cols;

            mm = (int)floor(ii); nn = (int)floor(jj);
            if (mm < 0 || nn < 0 || mm > rows - 1 || nn > cols - 1)
            {
                slave_tmp.re.at<short>(i, j) = 0.0;
                slave_tmp.im.at<short>(i, j) = 0.0;
            }
            else
            {
                mm1 = mm + 1; nn1 = nn + 1;
                mm1 = mm1 >= rows - 1 ? rows - 1 : mm1;
                nn1 = nn1 >= cols - 1 ? cols - 1 : nn1;
                //实部插值
                upper = slave.re.at<short>(mm, nn) + double(slave.re.at<short>(mm, nn1) - slave.re.at<short>(mm, nn)) * (jj - (double)nn);
                lower = slave.re.at<short>(mm1, nn) + double(slave.re.at<short>(mm1, nn1) - slave.re.at<short>(mm1, nn)) * (jj - (double)nn);
                slave_tmp.re.at<short>(i, j) = upper + (lower - upper) * (ii - (double)mm);
                //虚部插值
                upper = slave.im.at<short>(mm, nn) + double(slave.im.at<short>(mm, nn1) - slave.im.at<short>(mm, nn)) * (jj - (double)nn);
                lower = slave.im.at<short>(mm1, nn) + double(slave.im.at<short>(mm1, nn1) - slave.im.at<short>(mm1, nn)) * (jj - (double)nn);
                slave_tmp.im.at<short>(i, j) = upper + (lower - upper) * (ii - (double)mm);
            }

        }
    }
    slave = slave_tmp(Range(overlapSize, rows - overlapSize), Range(overlapSize, cols - overlapSize));
    int size = slave.GetCols() * slave.GetRows();
    jshortArray arr = env->NewShortArray(size * 2);
    env->SetShortArrayRegion(arr, 0, size, (short*)slave.re.data);
    env->SetShortArrayRegion(arr, size, size, (short*)slave.im.data);
    return arr;
}
View Code

5.2:ComplexMat.cpp

#include"ComplexMat.h"

using InSAR::ComplexMat;
ComplexMat::ComplexMat()
{
    Mat tmp = Mat::zeros(1, 1, CV_64F);
    tmp.copyTo(this->re);
    tmp.copyTo(this->im);
}

ComplexMat::ComplexMat(Mat& re, Mat& im)
{
    re.copyTo(this->re);
    im.copyTo(this->im);
}

ComplexMat::ComplexMat(int rows, int cols)
{
    if (rows > 0 && cols > 0)
    {
        Mat tmp = Mat::zeros(rows, cols, CV_64F);
        tmp.copyTo(this->im);
        tmp.copyTo(this->re);
    }
}

ComplexMat::ComplexMat(const ComplexMat& b)
{
    b.re.copyTo(this->re);
    b.im.copyTo(this->im);
}

ComplexMat::~ComplexMat()
{

}

int ComplexMat::type() const
{
    if (re.type() != im.type()) return -1;
    return this->re.type();
}

int ComplexMat::Mul(const ComplexMat& Src, ComplexMat& Dst, bool bConj) const
{
    ComplexMat result;
    if (this->GetRows() < 1 ||
        this->GetCols() < 1 ||
        Src.GetRows() < 1 ||
        Src.GetCols() < 1 ||
        this->GetCols() != Src.GetCols() ||
        this->GetRows() != Src.GetRows() ||
        this->type() != Src.type())
    {
        fprintf(stderr, "ComplexMat::Mul(): input check failed!\n\n");
        return -1;
    }

    if (bConj)
    {
        result.re = this->re.mul(Src.re) + this->im.mul(Src.im);
        result.im = Src.re.mul(this->im) - this->re.mul(Src.im);
    }
    else
    {
        result.re = this->re.mul(Src.re) - this->im.mul(Src.im);
        result.im = Src.re.mul(this->im) + this->re.mul(Src.im);
    }
    Dst.SetRe(result.re);
    Dst.SetIm(result.im);
    return 0;
}

ComplexMat ComplexMat::operator*(const ComplexMat& b) const
{
    ComplexMat result;
    if (this->GetRows() < 1 ||
        this->GetCols() < 1 ||
        b.GetRows() < 1 ||
        b.GetCols() < 1 ||
        (this->GetCols() != b.GetCols()) && b.GetCols() != 1 ||
        (this->GetRows() != b.GetRows()) && b.GetRows() != 1 ||
        this->type() != b.type())
    {
        fprintf(stderr, "ComplexMat::Mul(): input check failed!\n\n");
        return ComplexMat();
    }
    if (b.GetCols() == 1 && b.GetRows() == 1)
    {
        result.re = this->re * b.re.at<double>(0, 0) - this->im * b.im.at<double>(0, 0);
        result.im = this->im * b.re.at<double>(0, 0) + this->re * b.im.at<double>(0, 0);
    }
    else
    {
        result.re = this->re.mul(b.re) - this->im.mul(b.im);
        result.im = b.re.mul(this->im) + this->re.mul(b.im);
    }
    return result;
}

ComplexMat ComplexMat::operator*(const Mat& a) const
{
    if (a.cols != this->GetCols() ||
        a.rows != this->GetRows() ||
        a.type() != this->type() ||
        a.channels() != 1)
    {
        fprintf(stderr, "ComplexMat::operator*(const Mat& a): input check failed!\n\n");
        return ComplexMat();
    }
    Mat out_re, out_im;
    ComplexMat out;
    out_re = this->re.mul(a);
    out_im = this->im.mul(a);
    out.SetRe(out_re);
    out.SetIm(out_im);
    return out;
}

ComplexMat ComplexMat::operator*(const double& a) const
{
    Mat out_re, out_im;
    ComplexMat out;
    out_re = this->re * a;
    out_im = this->im * a;
    out.SetRe(out_re);
    out.SetIm(out_im);
    return out;
}

ComplexMat ComplexMat::operator()(cv::Range _rowRange, cv::Range _colRange) const
{
    if (_rowRange.start < 0 ||
        _rowRange.end > this->GetRows() ||
        _colRange.start < 0 ||
        _colRange.end > this->GetCols())
    {
        fprintf(stderr, "ComplexMat::operator()(cv::Range _rowRange, cv::Range _colRange): \n Range exceeds legal value!\n\n");
        return ComplexMat();
    }
    ComplexMat out;
    this->re(cv::Range(_rowRange.start, _rowRange.end), cv::Range(_colRange.start, _colRange.end)).copyTo(out.re);
    this->im(cv::Range(_rowRange.start, _rowRange.end), cv::Range(_colRange.start, _colRange.end)).copyTo(out.im);
    return out;
}

int ComplexMat::SetValue(cv::Range _rowRange, cv::Range _colRange, ComplexMat& src)
{
    if ((_rowRange.end - _rowRange.start) != src.GetRows() ||
        (_colRange.end - _colRange.start) != src.GetCols() ||
        _rowRange.start < 0 ||
        _rowRange.end > this->GetRows() ||
        _colRange.start < 0 ||
        _colRange.end > this->GetCols() ||
        src.type() != this->type() ||
        (src.type() != CV_64F && src.type() != CV_16S)
        )
    {
        fprintf(stderr, "ComplexMat::SetValue(): input check failed!\n\n");
        return -1;
    }
    if (src.type() == CV_64F)
    {
        for (int i = _rowRange.start; i < _rowRange.end; i++)
        {
            for (int j = _colRange.start; j < _colRange.end; j++)
            {

                this->re.at<double>(i, j) = src.re.at<double>(i - _rowRange.start, j - _colRange.start);
                this->im.at<double>(i, j) = src.im.at<double>(i - _rowRange.start, j - _colRange.start);
            }
        }
    }
    if (src.type() == CV_16S)
    {
        for (int i = _rowRange.start; i < _rowRange.end; i++)
        {
            for (int j = _colRange.start; j < _colRange.end; j++)
            {

                this->re.at<short>(i, j) = src.re.at<short>(i - _rowRange.start, j - _colRange.start);
                this->im.at<short>(i, j) = src.im.at<short>(i - _rowRange.start, j - _colRange.start);
            }
        }
    }

    return 0;
}

Mat ComplexMat::GetIm() const
{
    return this->im;
}

Mat ComplexMat::GetMod() const
{
    Mat tmp;
    magnitude(this->re, this->im, tmp);
    return tmp;
}

Mat ComplexMat::GetPhase()
{
    int nr = this->GetRows();
    int nc = this->GetCols();
    if (nr < 1 || nc < 1)
    {
        return Mat::zeros(1, 1, CV_64F);
    }
    Mat phase(nr, nc, CV_64F);
#pragma omp parallel for schedule(guided)
    for (int i = 0; i < nr; i++)
    {
        for (int j = 0; j < nc; j++)
        {
            phase.at <double>(i, j) = atan2(this->im.at<double>(i, j), this->re.at<double>(i, j));
        }
    }
    return phase;
}

Mat ComplexMat::GetRe() const
{
    return this->re;
}

void ComplexMat::SetIm(Mat& im)
{
    im.copyTo(this->im);

}

void ComplexMat::SetRe(Mat& re)
{
    re.copyTo(this->re);
}

int ComplexMat::GetCols() const
{
    if (re.cols != im.cols) return -1;
    return this->re.cols;
}

int ComplexMat::GetRows() const
{
    if (re.rows != im.rows) return -1;
    return this->re.rows;
}

ComplexMat ComplexMat::operator+(const ComplexMat& b) const
{
    if (this->GetCols() != b.GetCols() ||
        b.GetRows() != b.GetRows() ||
        this->type() != b.type() ||
        this->GetCols() < 1 ||
        this->GetRows() < 1
        )
    {
        fprintf(stderr, "ComplexMat operator+: input check failed!\n\n");
        return *this;
    }
    ComplexMat out;
    Mat out_re, out_im;
    out_re = this->re + b.re;
    out_im = this->im + b.im;
    out.SetIm(out_im);
    out.SetRe(out_re);
    return out;
}

ComplexMat ComplexMat::operator=(const ComplexMat& b)
{
    b.re.copyTo(this->re);
    b.im.copyTo(this->im);
    return *this;
}

ComplexMat ComplexMat::sum(int dim) const
{
    ComplexMat out;
    Mat re, im;
    int nr = this->GetRows();
    int nc = this->GetCols();
    if (nr < 0 || nc < 0)
    {
        fprintf(stderr, "ComplexMat::sum(): rows or cols < 0!\n\n");
        return ComplexMat();
    }
    if (dim == 1)
    {
        re = Mat::zeros(nr, 1, CV_64F);
        im = Mat::zeros(nr, 1, CV_64F);
#pragma omp parallel for schedule(guided)
        for (int i = 0; i < nr; i++)
        {
            double tmp_re, tmp_im;
            tmp_re = 0.0; tmp_im = 0.0;
            for (int j = 0; j < nc; j++)
            {
                tmp_re += this->re.at<double>(i, j);
                tmp_im += this->im.at<double>(i, j);
            }
            re.at<double>(i, 0) = tmp_re;
            im.at<double>(i, 0) = tmp_im;
        }
    }
    else
    {
        re = Mat::zeros(1, nc, CV_64F);
        im = Mat::zeros(1, nc, CV_64F);

#pragma omp parallel for schedule(guided)
        for (int j = 0; j < nc; j++)
        {
            double tmp_re, tmp_im;
            tmp_re = 0.0; tmp_im = 0.0;
            for (int i = 0; i < nr; i++)
            {
                tmp_re += this->re.at<double>(i, j);
                tmp_im += this->im.at<double>(i, j);
            }
            re.at<double>(0, j) = tmp_re;
            im.at<double>(0, j) = tmp_im;
        }
    }
    out.SetRe(re);
    out.SetIm(im);
    return out;
}

ComplexMat ComplexMat::conj() const
{
    ComplexMat out;
    Mat im, re;
    this->re.copyTo(re);
    this->im.copyTo(im);
    im = -im;
    out.SetRe(re);
    out.SetIm(im);
    return out;
}

int ComplexMat::countNonzero() const
{
    if (this->re.rows == 0 ||
        this->im.rows == 0 ||
        this->re.cols == 0 ||
        this->im.cols == 0
        )
    {
        return 0;
    }
    int count = 0;
    int nr = GetRows();
    int nc = GetCols();
    for (int i = 0; i < nr; i++)
    {
        for (int j = 0; j < nc; j++)
        {
            if (fabs(this->re.at<double>(i, j)) > DBL_EPSILON || fabs(this->im.at<double>(i, j)) > DBL_EPSILON)
            {
                count++;
            }
        }
    }
    return count;
}

bool ComplexMat::isempty() const
{
    if (this->GetRows() < 1 || this->GetCols() < 1) return true;
    return false;
}

void ComplexMat::convertTo(ComplexMat& out, int type) const
{
    if (this->type() == type)
    {
        out = *this;
    }
    else
    {
        this->re.convertTo(out.re, type);
        this->im.convertTo(out.im, type);
    }
}
View Code

5.3:Registration.cpp

#include<string.h>
#include<math.h>
#include"Registration.h"

using namespace cv;
using namespace InSAR;
inline bool return_check(int ret, const char* detail_info, const char* error_head)
{
    if (ret < 0)
    {
        fprintf(stderr, "%s %s\n\n", error_head, detail_info);
        return true;
    }
    else
    {
        return false;
    }
}

inline bool parallel_check(volatile bool parallel_flag, const char* detail_info, const char* parallel_error_head)
{
    if (!parallel_flag)
    {
        fprintf(stderr, "%s %s\n\n", parallel_error_head, detail_info);
        return true;
    }
    else
    {
        return false;
    }
}

inline bool parallel_flag_change(volatile bool parallel_flag, int ret)
{
    if (ret < 0)
    {
        parallel_flag = false;
        return true;
    }
    else
    {
        return false;
    }
}

Registration::Registration()
{
    memset(this->error_head, 0, 256);
    memset(this->parallel_error_head, 0, 256);
    strcpy(this->error_head, "REGISTRATION_DLL_ERROR: error happens when using ");
    strcpy(this->parallel_error_head, "REGISTRATION_DLL_ERROR: error happens when using parallel computing in function: ");
}

Registration::~Registration()
{
}
int Registration::fft2(Mat& Src, Mat& Dst)
{
    if (Src.rows < 1 ||
        Src.cols < 1 ||
        Src.channels() != 1 ||
        Src.type() != CV_64F)
    {
        fprintf(stderr, "fft2(): input check failed!\n\n");
        return -1;
    }
    Mat planes[] = { Mat_<double>(Src), Mat::zeros(Src.size(), CV_64F) };
    Mat complexImg;
    merge(planes, 2, complexImg);
    dft(complexImg, Dst);
    return 0;
}

int Registration::fftshift2(Mat& matrix)
{
    if (matrix.rows < 2 ||
        matrix.cols < 2 ||
        matrix.channels() != 1)
    {
        fprintf(stderr, "fftshift2(): input check failed!\n\n");
        return -1;
    }
    matrix = matrix(Rect(0, 0, matrix.cols & -2, matrix.rows & -2));
    int cx = matrix.cols / 2;
    int cy = matrix.rows / 2;
    Mat tmp;
    Mat q0(matrix, Rect(0, 0, cx, cy));
    Mat q1(matrix, Rect(cx, 0, cx, cy));
    Mat q2(matrix, Rect(0, cy, cx, cy));
    Mat q3(matrix, Rect(cx, cy, cx, cy));

    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);

    q1.copyTo(tmp);
    q2.copyTo(q1);
    tmp.copyTo(q2);
    return 0;
}

int Registration::real_coherent(ComplexMat& Master, ComplexMat& Slave, int* offset_row, int* offset_col)
{
    if (Master.GetRows() < 1 ||
        Master.GetCols() < 1 ||
        Master.GetRows() != Slave.GetRows() ||
        Master.GetCols() != Slave.GetCols())
    {
        fprintf(stderr, "real_coherent(): input check failed!\n\n");
        return -1;
    }
    int ret;
    Mat img1;
    Mat img2;
    img1 = Master.GetMod();
    img2 = Slave.GetMod();
    Mat im1fft;
    Mat im2fft;
    ret = fft2(img1, im1fft);
    if (return_check(ret, "fft2(*, *)", error_head)) return -1;
    ret = fft2(img2, im2fft);
    if (return_check(ret, "fft2(*, *)", error_head)) return -1;
    Mat spectrum;
    mulSpectrums(im1fft, im2fft, spectrum, 0, true);

    Mat result;
    idft(spectrum, result, DFT_REAL_OUTPUT);//需要显示图像时可以用DFT_SCALE

    ret = fftshift2(result);
    if (return_check(ret, "fftshift2(*)", error_head)) return -1;
    normalize(result, result, 0, 1, NORM_MINMAX);

    int r = result.rows / 2;
    int c = result.cols / 2;
    Point peak_loc;
    minMaxLoc(result, NULL, NULL, NULL, &peak_loc);

    *offset_row = r - peak_loc.y;
    *offset_col = c - peak_loc.x;
    return 0;
}

int Registration::registration_pixel(ComplexMat& Master, ComplexMat& Slave, int* move_r, int* move_c)
{
    if (Master.GetRows() < 1 ||
        Master.GetCols() < 1 ||
        Master.GetRows() != Slave.GetRows() ||
        Master.GetCols() != Slave.GetCols())
    {
        fprintf(stderr, "registration_pixel(): input check failed!\n\n");
        return -1;
    }
    int offset_rows, offset_cols, ret;
    offset_cols = 0;
    offset_rows = 0;
    ret = real_coherent(Master, Slave, &offset_rows, &offset_cols);//相关函数求取偏移量
    if (move_r)*move_r = offset_rows;
    if (move_c)*move_c = offset_cols;
    if (return_check(ret, "real_coherent(*, *, *, *)", error_head)) return -1;
    ////搬移与裁剪

    //行偏移(竖直移动)
    ComplexMat image_master_mid;
    ComplexMat image_slave_mid;
    int nr = Slave.GetRows();
    int nc = Slave.GetCols();
    //////////////////////////////检查粗配准偏移量是否超过图像大小////////////////////////////////////
    if ((offset_rows > 0 ? offset_rows : -offset_rows) >= nr ||
        (offset_cols > 0 ? offset_cols : -offset_cols) >= nc)
    {
        fprintf(stderr, "registration_pixel(): registration offset exceed size of images!\n\n");
        return -1;
    }
    if (offset_rows >= 0)
    {
        //实部
        Slave.re(Range(offset_rows, nr), Range(0, nc)).copyTo(image_slave_mid.re);//辅图像向上搬移

        Master.re(Range(0, nr - offset_rows), Range(0, nc)).copyTo(image_master_mid.re);//裁剪主图像


        //虚部
        Slave.im(Range(offset_rows, nr), Range(0, nc)).copyTo(image_slave_mid.im);//辅图像向上搬移

        Master.im(Range(0, nr - offset_rows), Range(0, nc)).copyTo(image_master_mid.im);//裁剪

    }

    else
    {
        //实部
        Slave.re(Range(0, nr + offset_rows), Range(0, nc)).copyTo(image_slave_mid.re);//辅图像向下搬移

        Master.re(Range(-offset_rows, nr), Range(0, nc)).copyTo(image_master_mid.re);//裁剪


        //虚部
        Slave.im(Range(0, nr + offset_rows), Range(0, nc)).copyTo(image_slave_mid.im);//辅图像向下搬移

        Master.im(Range(-offset_rows, nr), Range(0, nc)).copyTo(image_master_mid.im);//裁剪

    }

    ComplexMat image_master_regis;
    ComplexMat image_slave_regis;
    int nr1 = image_master_mid.re.rows;
    int nc1 = image_master_mid.re.cols;
    //列偏移(水平移动)
    if (offset_cols >= 0)//辅图像向左搬移
    {
        //实部
        image_slave_mid.re(Range(0, nr1), Range(offset_cols, nc1)).copyTo(image_slave_regis.re);

        image_master_mid.re(Range(0, nr1), Range(0, nc1 - offset_cols)).copyTo(image_master_regis.re);

        //虚部
        image_slave_mid.im(Range(0, nr1), Range(offset_cols, nc1)).copyTo(image_slave_regis.im);

        image_master_mid.im(Range(0, nr1), Range(0, nc1 - offset_cols)).copyTo(image_master_regis.im);

    }

    else//辅图像向右搬移
    {
        //实部
        image_slave_mid.re(Range(0, nr1), Range(0, nc1 + offset_cols)).copyTo(image_slave_regis.re);

        image_master_mid.re(Range(0, nr1), Range(-offset_cols, nc1)).copyTo(image_master_regis.re);

        //虚部
        image_slave_mid.im(Range(0, nr1), Range(0, nc1 + offset_cols)).copyTo(image_slave_regis.im);

        image_master_mid.im(Range(0, nr1), Range(-offset_cols, nc1)).copyTo(image_master_regis.im);
    }

    Master.re = image_master_regis.re;

    Master.im = image_master_regis.im;

    Slave.re = image_slave_regis.re;

    Slave.im = image_slave_regis.im;
    return 0;
}

int Registration::interp_paddingzero(ComplexMat& InputMatrix, ComplexMat& OutputMatrix, int interp_times)
{
    if (InputMatrix.GetRows() < 2 ||
        InputMatrix.GetCols() < 2 ||
        interp_times < 2)
    {
        fprintf(stderr, "interp_paddingzero(): input check failed!\n\n");
        return -1;
    }

    int nr = InputMatrix.GetRows();
    int nc = InputMatrix.GetCols();

    OutputMatrix.re = Mat::zeros(interp_times * nr, interp_times * nc, CV_64F);

    OutputMatrix.im = Mat::zeros(interp_times * nr, interp_times * nc, CV_64F);
    Mat re, im;
    InputMatrix.re.copyTo(re);
    InputMatrix.im.copyTo(im);
    Mat planes[] = { Mat_<double>(re), Mat_<double>(im) };

    Mat complexImg;

    merge(planes, 2, complexImg);

    dft(complexImg, complexImg, DFT_COMPLEX_OUTPUT);

    split(complexImg, planes);

    planes[0](Range(0, nr / 2), Range(0, nc / 2)).copyTo(OutputMatrix.re(Range(0, nr / 2), Range(0, nc / 2)));
    planes[1](Range(0, nr / 2), Range(0, nc / 2)).copyTo(OutputMatrix.im(Range(0, nr / 2), Range(0, nc / 2)));

    planes[0](Range(nr / 2, nr), Range(0, nc / 2)).copyTo(OutputMatrix.re(Range(nr * interp_times - nr / 2, nr * interp_times), Range(0, nc / 2)));
    planes[1](Range(nr / 2, nr), Range(0, nc / 2)).copyTo(OutputMatrix.im(Range(nr * interp_times - nr / 2, nr * interp_times), Range(0, nc / 2)));

    planes[0](Range(0, nr / 2), Range(nc / 2, nc)).copyTo(OutputMatrix.re(Range(0, nr / 2), Range(nc * interp_times - nc / 2, nc * interp_times)));
    planes[1](Range(0, nr / 2), Range(nc / 2, nc)).copyTo(OutputMatrix.im(Range(0, nr / 2), Range(nc * interp_times - nc / 2, nc * interp_times)));

    planes[0](Range(nr / 2, nr), Range(nc / 2, nc)).copyTo(OutputMatrix.re(Range(nr * interp_times - nr / 2, nr * interp_times), Range(nc * interp_times - nc / 2, nc * interp_times)));
    planes[1](Range(nr / 2, nr), Range(nc / 2, nc)).copyTo(OutputMatrix.im(Range(nr * interp_times - nr / 2, nr * interp_times), Range(nc * interp_times - nc / 2, nc * interp_times)));

    Mat planes1[] = { Mat_<double>(OutputMatrix.re), Mat_<double>(OutputMatrix.im) };

    merge(planes1, 2, complexImg);

    idft(complexImg, complexImg);

    split(complexImg, planes1);

    OutputMatrix.re = planes1[0];
    OutputMatrix.im = planes1[1];
    return 0;
}

int Registration::interp_cubic(ComplexMat& InputMatrix, ComplexMat& OutputMatrix, double offset_row, double offset_col)
{
    int nr = InputMatrix.GetRows();
    int nc = InputMatrix.GetCols();//输入矩阵尺寸
    if (nr < 2 || nc < 2 || InputMatrix.re.type() != CV_64F)
    {
        fprintf(stderr, "interp_cubic(): input check failed!\n\n");
        return -1;
    }
    ComplexMat new_image_slave;
    new_image_slave.re = Mat::zeros(nr + 3, nc + 3, CV_64F);
    new_image_slave.im = Mat::zeros(nr + 3, nc + 3, CV_64F);

    //扩充矩阵(扩展三行三列)

    //实部

    InputMatrix.re(Range(0, 1), Range(0, 1)).copyTo(new_image_slave.re(Range(0, 1), Range(0, 1)));

    InputMatrix.re(Range(0, 1), Range(0, nc)).copyTo(new_image_slave.re(Range(0, 1), Range(1, nc + 1)));

    InputMatrix.re(Range(0, 1), Range(nc - 1, nc)).copyTo(new_image_slave.re(Range(0, 1), Range(nc + 1, nc + 2)));

    InputMatrix.re(Range(0, 1), Range(nc - 1, nc)).copyTo(new_image_slave.re(Range(0, 1), Range(nc + 2, nc + 3)));

    InputMatrix.re(Range(0, nr), Range(0, 1)).copyTo(new_image_slave.re(Range(1, nr + 1), Range(0, 1)));

    InputMatrix.re(Range(nr - 1, nr), Range(0, 1)).copyTo(new_image_slave.re(Range(nr + 1, nr + 2), Range(0, 1)));

    InputMatrix.re(Range(nr - 1, nr), Range(0, 1)).copyTo(new_image_slave.re(Range(nr + 2, nr + 3), Range(0, 1)));

    InputMatrix.re(Range(nr - 1, nr), Range(0, nc)).copyTo(new_image_slave.re(Range(nr + 1, nr + 2), Range(1, nc + 1)));

    InputMatrix.re(Range(nr - 1, nr), Range(nc - 1, nc)).copyTo(new_image_slave.re(Range(nr + 1, nr + 2), Range(nc + 2, nc + 3)));

    new_image_slave.re(Range(nr + 1, nr + 2), Range(0, nc + 3)).copyTo(new_image_slave.re(Range(nr + 2, nr + 3), Range(0, nc + 3)));

    InputMatrix.re(Range(0, nr), Range(nc - 1, nc)).copyTo(new_image_slave.re(Range(1, nr + 1), Range(nc + 1, nc + 2)));

    InputMatrix.re(Range(nr - 1, nr), Range(nc - 1, nc)).copyTo(new_image_slave.re(Range(nr + 2, nr + 3), Range(nc + 1, nc + 2)));

    InputMatrix.re(Range(nr - 1, nr), Range(nc - 1, nc)).copyTo(new_image_slave.re(Range(nr + 2, nr + 3), Range(nc + 2, nc + 3)));

    new_image_slave.re(Range(0, nr + 3), Range(nc + 1, nc + 2)).copyTo(new_image_slave.re(Range(0, nr + 3), Range(nc + 2, nc + 3)));



    InputMatrix.re(Range(0, nr), Range(0, nc)).copyTo(new_image_slave.re(Range(1, nr + 1), Range(1, nc + 1)));


    //虚部

    InputMatrix.im(Range(0, 1), Range(0, 1)).copyTo(new_image_slave.im(Range(0, 1), Range(0, 1)));

    InputMatrix.im(Range(0, 1), Range(0, nc)).copyTo(new_image_slave.im(Range(0, 1), Range(1, nc + 1)));

    InputMatrix.im(Range(0, 1), Range(nc - 1, nc)).copyTo(new_image_slave.im(Range(0, 1), Range(nc + 1, nc + 2)));

    InputMatrix.im(Range(0, 1), Range(nc - 1, nc)).copyTo(new_image_slave.im(Range(0, 1), Range(nc + 2, nc + 3)));

    InputMatrix.im(Range(0, nr), Range(0, 1)).copyTo(new_image_slave.im(Range(1, nr + 1), Range(0, 1)));

    InputMatrix.im(Range(nr - 1, nr), Range(0, 1)).copyTo(new_image_slave.im(Range(nr + 1, nr + 2), Range(0, 1)));

    InputMatrix.im(Range(nr - 1, nr), Range(0, 1)).copyTo(new_image_slave.im(Range(nr + 2, nr + 3), Range(0, 1)));

    InputMatrix.im(Range(nr - 1, nr), Range(0, nc)).copyTo(new_image_slave.im(Range(nr + 1, nr + 2), Range(1, nc + 1)));

    InputMatrix.im(Range(nr - 1, nr), Range(nc - 1, nc)).copyTo(new_image_slave.im(Range(nr + 1, nr + 2), Range(nc + 2, nc + 3)));

    new_image_slave.im(Range(nr + 1, nr + 2), Range(0, nc + 3)).copyTo(new_image_slave.im(Range(nr + 2, nr + 3), Range(0, nc + 3)));

    InputMatrix.im(Range(0, nr), Range(nc - 1, nc)).copyTo(new_image_slave.im(Range(1, nr + 1), Range(nc + 1, nc + 2)));

    InputMatrix.im(Range(nr - 1, nr), Range(nc - 1, nc)).copyTo(new_image_slave.im(Range(nr + 2, nr + 3), Range(nc + 1, nc + 2)));

    InputMatrix.im(Range(nr - 1, nr), Range(nc - 1, nc)).copyTo(new_image_slave.im(Range(nr + 2, nr + 3), Range(nc + 2, nc + 3)));

    new_image_slave.im(Range(0, nr + 3), Range(nc + 1, nc + 2)).copyTo(new_image_slave.im(Range(0, nr + 3), Range(nc + 2, nc + 3)));

    //内点
    InputMatrix.im(Range(0, nr), Range(0, nc)).copyTo(new_image_slave.im(Range(1, nr + 1), Range(1, nc + 1)));


    //行权
    double row_weight[4];
    row_weight[0] = WeightCalculation(1.0 + offset_row);
    row_weight[1] = WeightCalculation(offset_row);
    row_weight[2] = WeightCalculation(1.0 - offset_row);
    row_weight[3] = WeightCalculation(2.0 - offset_row);

    Mat Row_weight(4, 1, CV_64F, row_weight);

    //列权
    double col_weight[4];
    col_weight[0] = WeightCalculation(1.0 + offset_col);
    col_weight[1] = WeightCalculation(offset_col);
    col_weight[2] = WeightCalculation(1.0 - offset_col);
    col_weight[3] = WeightCalculation(2.0 - offset_col);

    Mat Col_weight(1, 4, CV_64F, col_weight);


    //权矩阵
    Mat Weight = Row_weight * Col_weight;

    //实部虚部分别插值
    Mat image_slave_regis_re = Mat::zeros(nr, nc, CV_64F);
    Mat image_slave_regis_im = Mat::zeros(nr, nc, CV_64F);

    double temp;
#pragma omp parallel for schedule(guided) \
    private(temp)
    for (int i = 0; i <= nr - 1; i++)
    {
        for (int j = 0; j <= nc - 1; j++)
        {
            temp = new_image_slave.re(Range(i, i + 4), Range(j, j + 4)).dot(Weight);

            image_slave_regis_re.at<double>(i, j) = temp;


            temp = new_image_slave.im(Range(i, i + 4), Range(j, j + 4)).dot(Weight);

            image_slave_regis_im.at<double>(i, j) = temp;
        }

    }
    image_slave_regis_re.copyTo(OutputMatrix.re);
    image_slave_regis_im.copyTo(OutputMatrix.im);
    return 0;
}

int Registration::interp_cubic(ComplexMat& InputMatrix, ComplexMat& OutputMatrix, Mat& Coefficient)
{
    int nr = InputMatrix.GetRows();
    int nc = InputMatrix.GetCols();//输入矩阵尺寸
    if (nr < 2 || nc < 2 || InputMatrix.re.type() != CV_64F)
    {
        fprintf(stderr, "interp_cubic(): input check failed!\n\n");
        return -1;
    }

    ComplexMat new_image_slave;
    new_image_slave.re = Mat::zeros(nr + 3, nc + 3, CV_64F);
    new_image_slave.im = Mat::zeros(nr + 3, nc + 3, CV_64F);

    //扩充矩阵(扩展三行三列)

    //实部

    InputMatrix.re(Range(0, 1), Range(0, 1)).copyTo(new_image_slave.re(Range(0, 1), Range(0, 1)));

    InputMatrix.re(Range(0, 1), Range(0, nc)).copyTo(new_image_slave.re(Range(0, 1), Range(1, nc + 1)));

    InputMatrix.re(Range(0, 1), Range(nc - 1, nc)).copyTo(new_image_slave.re(Range(0, 1), Range(nc + 1, nc + 2)));

    InputMatrix.re(Range(0, 1), Range(nc - 1, nc)).copyTo(new_image_slave.re(Range(0, 1), Range(nc + 2, nc + 3)));

    InputMatrix.re(Range(0, nr), Range(0, 1)).copyTo(new_image_slave.re(Range(1, nr + 1), Range(0, 1)));

    InputMatrix.re(Range(nr - 1, nr), Range(0, 1)).copyTo(new_image_slave.re(Range(nr + 1, nr + 2), Range(0, 1)));

    InputMatrix.re(Range(nr - 1, nr), Range(0, 1)).copyTo(new_image_slave.re(Range(nr + 2, nr + 3), Range(0, 1)));

    InputMatrix.re(Range(nr - 1, nr), Range(0, nc)).copyTo(new_image_slave.re(Range(nr + 1, nr + 2), Range(1, nc + 1)));

    InputMatrix.re(Range(nr - 1, nr), Range(nc - 1, nc)).copyTo(new_image_slave.re(Range(nr + 1, nr + 2), Range(nc + 2, nc + 3)));

    new_image_slave.re(Range(nr + 1, nr + 2), Range(0, nc + 3)).copyTo(new_image_slave.re(Range(nr + 2, nr + 3), Range(0, nc + 3)));

    InputMatrix.re(Range(0, nr), Range(nc - 1, nc)).copyTo(new_image_slave.re(Range(1, nr + 1), Range(nc + 1, nc + 2)));

    InputMatrix.re(Range(nr - 1, nr), Range(nc - 1, nc)).copyTo(new_image_slave.re(Range(nr + 2, nr + 3), Range(nc + 1, nc + 2)));

    InputMatrix.re(Range(nr - 1, nr), Range(nc - 1, nc)).copyTo(new_image_slave.re(Range(nr + 2, nr + 3), Range(nc + 2, nc + 3)));

    new_image_slave.re(Range(0, nr + 3), Range(nc + 1, nc + 2)).copyTo(new_image_slave.re(Range(0, nr + 3), Range(nc + 2, nc + 3)));



    InputMatrix.re(Range(0, nr), Range(0, nc)).copyTo(new_image_slave.re(Range(1, nr + 1), Range(1, nc + 1)));


    //虚部

    InputMatrix.im(Range(0, 1), Range(0, 1)).copyTo(new_image_slave.im(Range(0, 1), Range(0, 1)));

    InputMatrix.im(Range(0, 1), Range(0, nc)).copyTo(new_image_slave.im(Range(0, 1), Range(1, nc + 1)));

    InputMatrix.im(Range(0, 1), Range(nc - 1, nc)).copyTo(new_image_slave.im(Range(0, 1), Range(nc + 1, nc + 2)));

    InputMatrix.im(Range(0, 1), Range(nc - 1, nc)).copyTo(new_image_slave.im(Range(0, 1), Range(nc + 2, nc + 3)));

    InputMatrix.im(Range(0, nr), Range(0, 1)).copyTo(new_image_slave.im(Range(1, nr + 1), Range(0, 1)));

    InputMatrix.im(Range(nr - 1, nr), Range(0, 1)).copyTo(new_image_slave.im(Range(nr + 1, nr + 2), Range(0, 1)));

    InputMatrix.im(Range(nr - 1, nr), Range(0, 1)).copyTo(new_image_slave.im(Range(nr + 2, nr + 3), Range(0, 1)));

    InputMatrix.im(Range(nr - 1, nr), Range(0, nc)).copyTo(new_image_slave.im(Range(nr + 1, nr + 2), Range(1, nc + 1)));

    InputMatrix.im(Range(nr - 1, nr), Range(nc - 1, nc)).copyTo(new_image_slave.im(Range(nr + 1, nr + 2), Range(nc + 2, nc + 3)));

    new_image_slave.im(Range(nr + 1, nr + 2), Range(0, nc + 3)).copyTo(new_image_slave.im(Range(nr + 2, nr + 3), Range(0, nc + 3)));

    InputMatrix.im(Range(0, nr), Range(nc - 1, nc)).copyTo(new_image_slave.im(Range(1, nr + 1), Range(nc + 1, nc + 2)));

    InputMatrix.im(Range(nr - 1, nr), Range(nc - 1, nc)).copyTo(new_image_slave.im(Range(nr + 2, nr + 3), Range(nc + 1, nc + 2)));

    InputMatrix.im(Range(nr - 1, nr), Range(nc - 1, nc)).copyTo(new_image_slave.im(Range(nr + 2, nr + 3), Range(nc + 2, nc + 3)));

    new_image_slave.im(Range(0, nr + 3), Range(nc + 1, nc + 2)).copyTo(new_image_slave.im(Range(0, nr + 3), Range(nc + 2, nc + 3)));

    //内点
    InputMatrix.im(Range(0, nr), Range(0, nc)).copyTo(new_image_slave.im(Range(1, nr + 1), Range(1, nc + 1)));





    Mat image_slave_regis_re = Mat::zeros(nr, nc, CV_64F);
    Mat image_slave_regis_im = Mat::zeros(nr, nc, CV_64F);


    int ret;
    volatile bool parallel_flag = true;
#pragma omp parallel for schedule(guided) \
    private(ret)
    for (int i = 0; i <= nr - 1; i++)
    {
        if (!parallel_flag) continue;

        for (int j = 0; j <= nc - 1; j++)
        {
            if (!parallel_flag) continue;
            double temp, offset_row, offset_col;
            Mat Row_weight = Mat::zeros(4, 1, CV_64F);
            Mat Col_weight = Mat::zeros(1, 4, CV_64F);
            offset_row = 0;
            offset_col = 0;
            ret = every_subpixel_move(i + 1, j + 1, Coefficient, &offset_row, &offset_col);
            if (ret < 0)
            {
                parallel_flag = false;
                continue;
            }
            Row_weight.at<double>(0, 0) = WeightCalculation(offset_row + 1.0);
            Row_weight.at<double>(1, 0) = WeightCalculation(offset_row);
            Row_weight.at<double>(2, 0) = WeightCalculation(1.0 - offset_row);
            Row_weight.at<double>(3, 0) = WeightCalculation(2.0 - offset_row);

            Col_weight.at<double>(0, 0) = WeightCalculation(1.0 + offset_col);
            Col_weight.at<double>(0, 1) = WeightCalculation(offset_col);
            Col_weight.at<double>(0, 2) = WeightCalculation(1.0 - offset_col);
            Col_weight.at<double>(0, 3) = WeightCalculation(2.0 - offset_col);
            temp = new_image_slave.re(Range(i, i + 4), Range(j, j + 4)).dot(Row_weight * Col_weight);

            image_slave_regis_re.at<double>(i, j) = temp;


            temp = new_image_slave.im(Range(i, i + 4), Range(j, j + 4)).dot(Row_weight * Col_weight);

            image_slave_regis_im.at<double>(i, j) = temp;
        }

    }
    if (parallel_check(parallel_flag, "interp_cubic()", parallel_error_head)) return -1;
    image_slave_regis_re.copyTo(OutputMatrix.re);
    image_slave_regis_im.copyTo(OutputMatrix.im);
    return 0;
}

double Registration::WeightCalculation(double offset)
{
    double weight = 0;
    if (offset > 0)
    {
        offset = offset;
    }
    else
    {
        offset = -offset;
    }

    if (offset < 1.0)
    {
        weight = 1.0 - 2.0 * offset * offset + offset * offset * offset;
    }
    else if (offset >= 1.0 && offset <= 2.0)
    {
        weight = 4.0 - 8.0 * offset + 5.0 * offset * offset - offset * offset * offset;
    }
    else
    {
        weight = 0.0;
    }

    return weight;
}

int Registration::registration_subpixel(ComplexMat& Master, ComplexMat& Slave, int blocksize, int interp_times)
{
    if (Master.GetRows() < 1 ||
        Master.GetCols() < 1 ||
        Master.GetRows() != Slave.GetRows() ||
        Master.GetCols() != Slave.GetCols() ||
        blocksize < 1 ||
        interp_times < 1)
    {
        fprintf(stderr, "%s\n\n", "registration_subpixel(): input check failed!\n\n");
        return -1;
    }

    int nsubr = Master.GetRows() / blocksize; //子块行数
    int nsubc = Master.GetCols() / blocksize; //子块列数
    int nsub = nsubr * nsubc;  //子块总数
    if (nsubc < 1 || nsubr < 1)
    {
        fprintf(stderr, "%s\n\n", "registration_subpixel: subblockszie, nsubc < 1 || nsubr < 1");
        return -1;
    }
    Mat sub_r_offset = Mat::zeros(nsub, 1, CV_64F); //行亚像素偏移量
    Mat sub_c_offset = Mat::zeros(nsub, 1, CV_64F); //列亚像素偏移量

    Mat m = Mat::zeros(nsub, 1, CV_32S); //子块中心行坐标
    Mat n = Mat::zeros(nsub, 1, CV_32S); //子块中心列坐标
    Mat indx = Mat::zeros(nsub, 1, CV_32S); //索引


    int count = 0;
    int ret;
    Mat coherence;
    Utils util;
    volatile bool parallel_flag = true;
#pragma omp parallel for schedule(guided) \
    private(ret)
    for (int i = 0; i < nsubc; i++)
    {
        if (!parallel_flag) continue;

        for (int j = 0; j < nsubr; j++)
        {
            if (!parallel_flag) continue;
            ComplexMat temp_in_slave, temp_out_slave, temp_in_master, temp_out_master;
            int offset_row, offset_col;
            //辅图像子块插值
            Slave.re(Range(j * blocksize, (j + 1) * blocksize), Range(i * blocksize, (i + 1) * blocksize)).copyTo(temp_in_slave.re);
            Slave.im(Range(j * blocksize, (j + 1) * blocksize), Range(i * blocksize, (i + 1) * blocksize)).copyTo(temp_in_slave.im);
            //util.cvmat2bin("E:\\zgb1\\InSAR\\InSAR\\bin\\re.bin", temp_in_slave.re);
            ret = interp_paddingzero(temp_in_slave, temp_out_slave, interp_times);
            if (ret < 0)
            {
                parallel_flag = false;
                continue;
            }

            //主图像子块插值
            Master.re(Range(j * blocksize, (j + 1) * blocksize), Range(i * blocksize, (i + 1) * blocksize)).copyTo(temp_in_master.re);
            Master.im(Range(j * blocksize, (j + 1) * blocksize), Range(i * blocksize, (i + 1) * blocksize)).copyTo(temp_in_master.im);
            ret = interp_paddingzero(temp_in_master, temp_out_master, interp_times);
            if (ret < 0)
            {
                parallel_flag = false;
                continue;
            }

            //实相关函数求取亚像素偏移量
            ret = real_coherent(temp_out_master, temp_out_slave, &offset_row, &offset_col);
            if (ret < 0)
            {
                parallel_flag = false;
                continue;
            }
            double offset_row_sub, offset_col_sub, mean_coh;
            //ret = util.real_coherence(temp_out_master, temp_out_slave, coherence);
            //if (ret < 0)
            //{
            //    parallel_flag = false;
            //    continue;
            //}
            //mean_coh = mean(coherence)[0];
            offset_row_sub = double(offset_row) / double(interp_times);
            offset_col_sub = double(offset_col) / double(interp_times);
            sub_r_offset.at<double>(i * nsubr + j, 0) = offset_row_sub;
            sub_c_offset.at<double>(i * nsubr + j, 0) = offset_col_sub;

            //子块中心坐标
            n.at<int>(i * nsubr + j, 0) = blocksize / 2 + i * blocksize;
            m.at<int>(i * nsubr + j, 0) = blocksize / 2 + j * blocksize;

            ret = interp_cubic(temp_in_slave, temp_in_slave, sub_r_offset.at<double>(i * nsubr + j, 0), sub_c_offset.at<double>(i * nsubr + j, 0));//子辅图像插值
            if (ret < 0)
            {
                parallel_flag = false;
                continue;
            }
            ret = util.real_coherence(temp_in_master, temp_in_slave, coherence);
            if (ret < 0)
            {
                parallel_flag = false;
                continue;
            }
            if (mean(coherence)[0] > 0.4)
            {
                indx.at<int>(i * nsubr + j, 0) = 1;
            }

        }
    }
    if (parallel_check(parallel_flag, "registration_subpixel()", parallel_error_head)) return -1;
    int NoneZero = countNonZero(indx);//非零元素个数
    count = 0;
    if (NoneZero == 0)
    {
        fprintf(stderr, "registration_subpixel(): NoneZero == 0\n\n");
        return -1;
    }
    Mat sub_r_offset_sifted = Mat::zeros(NoneZero, 1, CV_64F); //筛选后行亚像素偏移量
    Mat sub_c_offset_sifted = Mat::zeros(NoneZero, 1, CV_64F); //筛选后列亚像素偏移量

    Mat m_sifted = Mat::zeros(NoneZero, 1, CV_32S); //筛选后子块中心行坐标
    Mat n_sifted = Mat::zeros(NoneZero, 1, CV_32S); //筛选后子块中心列坐标


    for (int k = 0; k < nsub; k++)
    {
        if (indx.at<int>(k, 0) > 0)
        {

            sub_r_offset_sifted.at<double>(count, 0) = sub_r_offset.at<double>(k, 0);
            sub_c_offset_sifted.at<double>(count, 0) = sub_c_offset.at<double>(k, 0);
            m_sifted.at<int>(count, 0) = m.at<int>(k, 0);
            n_sifted.at<int>(count, 0) = n.at<int>(k, 0);

            count++;
        }
    }


    Mat para;
    ret = all_subpixel_move(m_sifted, n_sifted, sub_r_offset_sifted, sub_c_offset_sifted, para);//拟合辅图像偏移量
    //测试
    //cout << para << "\n";
    //
    if (return_check(ret, "all_subpixel_move(*, *, *, *)", error_head)) return -1;
    ret = interp_cubic(Slave, Slave, para);
    if (return_check(ret, "interp_cubic(*, *, *)", error_head)) return -1;
    return 0;
}

int Registration::coregistration_subpixel(ComplexMat& master, ComplexMat& slave, int blocksize, int interp_times)
{
    if (master.isempty() ||
        slave.GetCols() != master.GetCols() ||
        slave.GetRows() != slave.GetRows() ||
        blocksize * 5 > (slave.GetCols() < slave.GetRows() ? slave.GetCols() : slave.GetRows()) ||
        blocksize < 8 || interp_times < 1
        )
    {
        fprintf(stderr, "coregistration_pixel(): input check failed!\n");
        return -1;
    }

    /*---------------------------------------*/
    /*              求取偏移量矩阵           */
    /*---------------------------------------*/

    Utils util;
    int m = (master.GetRows()) / blocksize;
    int n = (master.GetCols()) / blocksize;
    Mat offset_r = Mat::zeros(m, n, CV_64F); Mat offset_c = Mat::zeros(m, n, CV_64F);
    Mat offset_coord_row = Mat::zeros(m, n, CV_64F);
    Mat offset_coord_col = Mat::zeros(m, n, CV_64F);
    //子块中心坐标
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
        {
            offset_coord_row.at<double>(i, j) = ((double)blocksize) / 2 * (double)(2 * i + 1);
            offset_coord_col.at<double>(i, j) = ((double)blocksize) / 2 * (double)(2 * j + 1);
        }
    }
#pragma omp parallel for schedule(guided)
    for (int i = 0; i < m; i++)
    {
        ComplexMat master_sub, slave_sub, master_sub_interp, slave_sub_interp;
        int offset_row, offset_col;
        for (int j = 0; j < n; j++)
        {
            //主图像子块插值
            master.re(Range(i * blocksize, (i + 1) * blocksize), Range(j * blocksize, (j + 1) * blocksize)).copyTo(master_sub.re);
            master.im(Range(i * blocksize, (i + 1) * blocksize), Range(j * blocksize, (j + 1) * blocksize)).copyTo(master_sub.im);
            interp_paddingzero(master_sub, master_sub_interp, interp_times);
            //辅图像子块插值
            slave.re(Range(i * blocksize, (i + 1) * blocksize), Range(j * blocksize, (j + 1) * blocksize)).copyTo(slave_sub.re);
            slave.im(Range(i * blocksize, (i + 1) * blocksize), Range(j * blocksize, (j + 1) * blocksize)).copyTo(slave_sub.im);
            interp_paddingzero(slave_sub, slave_sub_interp, interp_times);
            //求取偏移量
            real_coherent(master_sub_interp, slave_sub_interp, &offset_row, &offset_col);
            offset_r.at<double>(i, j) = (double)offset_row / (double)interp_times;
            offset_c.at<double>(i, j) = (double)offset_col / (double)interp_times;

        }
    }

    /*---------------------------------------*/
    /*    拟合偏移量(将坐标做归一化处理)   */
    /*---------------------------------------*/

    /*
    * 拟合公式为 offser_row/offser_col = a0 + a1*x + a2*y + a3*x*y + a4*x*x + a5*y*y + a6*x*x*y + a7*x*y*y + a8*x*x*x + a9*y*y*y
    */
    //util.cvmat2bin("E:\\working_dir\\projects\\software\\InSAR\\bin\\offset_r.bin", offset_r);
    //util.cvmat2bin("E:\\working_dir\\projects\\software\\InSAR\\bin\\offset_c.bin", offset_c);

    //剔除outliers
    Mat sentinel = Mat::zeros(m, n, CV_64F);
    int ix, iy, count = 0, c = 0; double delta, thresh = 2.0;
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
        {
            count = 0;
            //
            ix = j;
            iy = i - 1; iy = iy < 0 ? 0 : iy;
            delta = fabs(offset_c.at<double>(i, j) - offset_c.at<double>(iy, ix));
            delta += fabs(offset_r.at<double>(i, j) - offset_r.at<double>(iy, ix));
            if (fabs(delta) >= thresh) count++;
            //
            ix = j;
            iy = i + 1; iy = iy > m - 1 ? m - 1 : iy;
            delta = fabs(offset_c.at<double>(i, j) - offset_c.at<double>(iy, ix));
            delta += fabs(offset_r.at<double>(i, j) - offset_r.at<double>(iy, ix));
            if (fabs(delta) >= thresh) count++;
            //
            ix = j - 1; ix = ix < 0 ? 0 : ix;
            iy = i;
            delta = fabs(offset_c.at<double>(i, j) - offset_c.at<double>(iy, ix));
            delta += fabs(offset_r.at<double>(i, j) - offset_r.at<double>(iy, ix));
            if (fabs(delta) >= thresh) count++;
            //
            ix = j + 1; ix = ix > n - 1 ? n - 1 : ix;
            iy = i;
            delta = fabs(offset_c.at<double>(i, j) - offset_c.at<double>(iy, ix));
            delta += fabs(offset_r.at<double>(i, j) - offset_r.at<double>(iy, ix));
            if (fabs(delta) >= thresh) count++;

            if (count > 2) { sentinel.at<double>(i, j) = 1.0; c++; }
        }
    }
    Mat offset_c_0, offset_r_0, offset_coord_row_0, offset_coord_col_0;
    offset_c_0 = Mat::zeros(m * n - c, 1, CV_64F);
    offset_r_0 = Mat::zeros(m * n - c, 1, CV_64F);
    offset_coord_row_0 = Mat::zeros(m * n - c, 1, CV_64F);
    offset_coord_col_0 = Mat::zeros(m * n - c, 1, CV_64F);
    count = 0;
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
        {
            if (sentinel.at<double>(i, j) < 0.5)
            {
                offset_r_0.at<double>(count, 0) = offset_r.at<double>(i, j);
                offset_c_0.at<double>(count, 0) = offset_c.at<double>(i, j);
                offset_coord_row_0.at<double>(count, 0) = offset_coord_row.at<double>(i, j);
                offset_coord_col_0.at<double>(count, 0) = offset_coord_col.at<double>(i, j);
                count++;
            }
        }
    }


    offset_c = offset_c_0;
    offset_r = offset_r_0;
    offset_coord_row = offset_coord_row_0;
    offset_coord_col = offset_coord_col_0;
    m = 1; n = count;
    if (count < 11)
    {
        fprintf(stderr, "coregistration_pixel(): insufficient valide sub blocks!\n");
        return -1;
    }
    double offset_x = (double)master.GetCols() / 2;
    double offset_y = (double)master.GetRows() / 2;
    double scale_x = (double)master.GetCols();
    double scale_y = (double)master.GetRows();
    offset_coord_row -= offset_y;
    offset_coord_col -= offset_x;
    offset_coord_row /= scale_y;
    offset_coord_col /= scale_x;
    Mat A = Mat::ones(m * n, 10, CV_64F);
    Mat temp, A_t;
    offset_coord_col.copyTo(A(Range(0, m * n), Range(1, 2)));

    offset_coord_row.copyTo(A(Range(0, m * n), Range(2, 3)));

    temp = offset_coord_col.mul(offset_coord_row);
    temp.copyTo(A(Range(0, m * n), Range(3, 4)));

    temp = offset_coord_col.mul(offset_coord_col);
    temp.copyTo(A(Range(0, m * n), Range(4, 5)));

    temp = offset_coord_row.mul(offset_coord_row);
    temp.copyTo(A(Range(0, m * n), Range(5, 6)));

    temp = offset_coord_col.mul(offset_coord_col);
    temp = temp.mul(offset_coord_row);
    temp.copyTo(A(Range(0, m * n), Range(6, 7)));

    temp = offset_coord_row.mul(offset_coord_row);
    temp = temp.mul(offset_coord_col);
    temp.copyTo(A(Range(0, m * n), Range(7, 8)));

    temp = offset_coord_col.mul(offset_coord_col);
    temp = temp.mul(offset_coord_col);
    temp.copyTo(A(Range(0, m * n), Range(8, 9)));

    temp = offset_coord_row.mul(offset_coord_row);
    temp = temp.mul(offset_coord_row);
    temp.copyTo(A(Range(0, m * n), Range(9, 10)));

    transpose(A, A_t);

    Mat b_r, b_c, coef_r, coef_c, error_r, error_c, b_t, a, a_t;

    A.copyTo(a);
    cv::transpose(a, a_t);
    offset_r.copyTo(b_r);
    b_r = A_t * b_r;

    offset_c.copyTo(b_c);
    b_c = A_t * b_c;

    A = A_t * A;

    double rms1 = -1.0; double rms2 = -1.0;
    Mat eye = Mat::zeros(m * n, m * n, CV_64F);
    for (int i = 0; i < m * n; i++)
    {
        eye.at<double>(i, i) = 1.0;
    }
    if (cv::invert(A, error_r, cv::DECOMP_LU) > 0)
    {
        cv::transpose(offset_r, b_t);
        error_r = b_t * (eye - a * error_r * a_t) * offset_r;
        rms1 = sqrt(error_r.at<double>(0, 0) / double(m * n));
    }
    if (cv::invert(A, error_c, cv::DECOMP_LU) > 0)
    {
        cv::transpose(offset_c, b_t);
        error_c = b_t * (eye - a * error_c * a_t) * offset_c;
        rms2 = sqrt(error_c.at<double>(0, 0) / double(m * n));
    }
    if (!cv::solve(A, b_r, coef_r, cv::DECOMP_NORMAL))
    {
        fprintf(stderr, "coregistration_subpixel(): matrix defficiency!\n");
        return -1;
    }
    if (!cv::solve(A, b_c, coef_c, cv::DECOMP_NORMAL))
    {
        fprintf(stderr, "coregistration_subpixel(): matrix defficiency!\n");
        return -1;
    }

    /*---------------------------------------*/
    /*    双线性插值获取重采样后的辅图像     */
    /*---------------------------------------*/
    int rows = master.GetRows(); int cols = master.GetCols();
    ComplexMat slave_tmp;
    slave_tmp = master;
#pragma omp parallel for schedule(guided)
    for (int i = 0; i < rows; i++)
    {
        double x, y, ii, jj; Mat tmp(1, 10, CV_64F); Mat result;
        int mm, nn, mm1, nn1;
        double offset_rows, offset_cols, upper, lower;
        for (int j = 0; j < cols; j++)
        {
            jj = (double)j;
            ii = (double)i;
            x = (jj - offset_x) / scale_x;
            y = (ii - offset_y) / scale_y;
            tmp.at<double>(0, 0) = 1.0;
            tmp.at<double>(0, 1) = x;
            tmp.at<double>(0, 2) = y;
            tmp.at<double>(0, 3) = x * y;
            tmp.at<double>(0, 4) = x * x;
            tmp.at<double>(0, 5) = y * y;
            tmp.at<double>(0, 6) = x * x * y;
            tmp.at<double>(0, 7) = x * y * y;
            tmp.at<double>(0, 8) = x * x * x;
            tmp.at<double>(0, 9) = y * y * y;
            result = tmp * coef_r;
            offset_rows = result.at<double>(0, 0);
            result = tmp * coef_c;
            offset_cols = result.at<double>(0, 0);

            ii += offset_rows;
            jj += offset_cols;

            mm = (int)floor(ii); nn = (int)floor(jj);
            if (mm < 0 || nn < 0 || mm > rows - 1 || nn > cols - 1)
            {
                slave_tmp.re.at<double>(i, j) = 0.0;
                slave_tmp.im.at<double>(i, j) = 0.0;
            }
            else
            {
                mm1 = mm + 1; nn1 = nn + 1;
                mm1 = mm1 >= rows - 1 ? rows - 1 : mm1;
                nn1 = nn1 >= cols - 1 ? cols - 1 : nn1;
                //实部插值
                upper = slave.re.at<double>(mm, nn) + (slave.re.at<double>(mm, nn1) - slave.re.at<double>(mm, nn)) * (jj - (double)nn);
                lower = slave.re.at<double>(mm1, nn) + (slave.re.at<double>(mm1, nn1) - slave.re.at<double>(mm1, nn)) * (jj - (double)nn);
                slave_tmp.re.at<double>(i, j) = upper + (lower - upper) * (ii - (double)mm);
                //虚部插值
                upper = slave.im.at<double>(mm, nn) + (slave.im.at<double>(mm, nn1) - slave.im.at<double>(mm, nn)) * (jj - (double)nn);
                lower = slave.im.at<double>(mm1, nn) + (slave.im.at<double>(mm1, nn1) - slave.im.at<double>(mm1, nn)) * (jj - (double)nn);
                slave_tmp.im.at<double>(i, j) = upper + (lower - upper) * (ii - (double)mm);
            }

        }
    }
    slave = slave_tmp;
    return 0;
}

int Registration::every_subpixel_move(int i, int j, Mat& coefficient, double* offset_row, double* offset_col)
{
    if (i < 1 || j < 1 || coefficient.cols < 1 || coefficient.rows < 12 || coefficient.type() != CV_64F)
    {
        fprintf(stderr, "every_subpixel_move(): input check failed!\n\n");
        return -1;
    }
    double a1, a2, b1, b2, c1, c2, d1, d2, e1, e2, f1, f2;
    a1 = coefficient.at<double>(0, 0);
    b1 = coefficient.at<double>(1, 0);
    c1 = coefficient.at<double>(2, 0);
    d1 = coefficient.at<double>(3, 0);
    e1 = coefficient.at<double>(4, 0);
    f1 = coefficient.at<double>(5, 0);
    a2 = coefficient.at<double>(6, 0);
    b2 = coefficient.at<double>(7, 0);
    c2 = coefficient.at<double>(8, 0);
    d2 = coefficient.at<double>(9, 0);
    e2 = coefficient.at<double>(10, 0);
    f2 = coefficient.at<double>(11, 0);

    *offset_row = a1 + b1 * i + c1 * j + d1 * i * i + e1 * j * j + f1 * i * j;
    *offset_col = a2 + b2 * i + c2 * j + d2 * i * i + e2 * j * j + f2 * i * j;
    return 0;
}

int Registration::all_subpixel_move(Mat& Coordinate_x, Mat& Coordinate_y, Mat& offset_row, Mat& offset_col, Mat& para)
{
    if (Coordinate_x.rows < 1 ||
        Coordinate_x.cols < 1 ||
        Coordinate_x.rows != Coordinate_y.rows ||
        Coordinate_x.cols != Coordinate_y.cols ||
        offset_row.rows != offset_col.rows ||
        offset_row.cols != offset_col.cols)
    {
        fprintf(stderr, "all_subpixel_move(): input size/type check failed!\n\n");
        return -1;
    }
    int N = Coordinate_x.rows;
    Coordinate_x.convertTo(Coordinate_x, CV_64F);
    Coordinate_y.convertTo(Coordinate_y, CV_64F);
    offset_row.convertTo(offset_row, CV_64F);
    offset_col.convertTo(offset_col, CV_64F);

    Mat connect_h_x[] = { Mat::ones(N, 1, CV_64F), Coordinate_x, Coordinate_y, Coordinate_x.mul(Coordinate_x), Coordinate_y.mul(Coordinate_y),
        Coordinate_y.mul(Coordinate_x) };
    Mat matrix, matrix_t;
    hconcat(connect_h_x, 6, matrix);
    Mat para1;
    transpose(matrix, matrix_t);
    if (!solve(matrix_t * matrix, matrix_t * offset_row, para1, DECOMP_LU))
    {
        fprintf(stderr, "all_subpixel_move(): cant' solve least square problem!\n");
        return -1;
    }

    Mat connect_h_y[] = { Mat::ones(N, 1, CV_64F), Coordinate_x, Coordinate_y, Coordinate_x.mul(Coordinate_x), Coordinate_y.mul(Coordinate_y),
        Coordinate_y.mul(Coordinate_x) };
    hconcat(connect_h_y, 6, matrix);
    transpose(matrix, matrix_t);
    Mat para2;
    if (!solve(matrix_t * matrix, matrix_t * offset_col, para2, DECOMP_LU))
    {
        fprintf(stderr, "all_subpixel_move(): cant' solve least square problem!\n");
        return -1;
    }
    Mat connect[] = { para1, para2 };
    vconcat(connect, 2, para);
    return 0;
}

int Registration::gcps_sift(int rows, int cols, int move_rows, int move_cols, Mat& gcps)
{
    if (fabs(move_rows) > rows ||
        fabs(move_cols) > cols ||
        rows < 1 ||
        cols < 1 ||
        gcps.cols != 5 ||
        gcps.type() != CV_64F ||
        gcps.channels() != 1 ||
        gcps.rows < 3)
    {
        fprintf(stderr, "gcps_sift(): input check failed!\n\n");
        return -1;
    }
    Mat gcps_tmp, GCP;
    gcps(Range(0, 2), Range(0, 5)).copyTo(GCP);
    gcps.copyTo(gcps_tmp);
    Mat index = Mat::zeros(gcps.rows, 1, CV_64F);
    int count = 0;
    //////////////////////////Scenario 1///////////////////////////////
    if (move_rows > 0 && move_cols > 0)
    {
        for (int i = 0; i < gcps_tmp.rows; i++)
        {
            if (gcps_tmp.at<double>(i, 0) <= double(rows - move_rows) &&
                gcps_tmp.at<double>(i, 1) <= double(cols - move_cols))
            {
                index.at<double>(i, 0) = 1.0;
                count++;
            }
        }
        if (count == 0)
        {
            fprintf(stderr, "all ground control points have been sifted out!\n\n");
            return -1;
        }
        gcps_tmp = Mat::zeros(count, gcps.cols, CV_64F);
        count = 0;
        for (int i = 0; i < gcps.rows; i++)
        {
            if (index.at<double>(i, 0) > 0.5)
            {
                gcps(Range(i, i + 1), Range(0, gcps.cols)).copyTo(gcps_tmp(Range(count, count + 1), Range(0, gcps.cols)));
                count++;
            }
        }
    }
    //////////////////////////Scenario 2///////////////////////////////
    if (move_rows > 0 && move_cols <= 0)
    {
        for (int i = 0; i < gcps_tmp.rows; i++)
        {
            if (gcps_tmp.at<double>(i, 0) <= double(rows - move_rows) &&
                gcps_tmp.at<double>(i, 1) >= double(1 - move_cols))
            {
                index.at<double>(i, 0) = 1.0;
                gcps_tmp.at<double>(i, 1) = gcps_tmp.at<double>(i, 1) + move_cols;
                count++;
            }
        }
        if (count == 0)
        {
            fprintf(stderr, "all ground control points have been sifted out!\n\n");
            return -1;
        }
        Mat gcps_tmp1 = Mat::zeros(count, gcps.cols, CV_64F);
        count = 0;
        for (int i = 0; i < gcps.rows; i++)
        {
            if (index.at<double>(i, 0) > 0.5)
            {
                gcps_tmp(Range(i, i + 1), Range(0, gcps.cols)).copyTo(gcps_tmp1(Range(count, count + 1), Range(0, gcps.cols)));
                count++;
            }
        }
        gcps = gcps_tmp1;
    }
    //////////////////////////Scenario 3///////////////////////////////
    if (move_rows <= 0 && move_cols > 0)
    {
        for (int i = 0; i < gcps_tmp.rows; i++)
        {
            if (gcps_tmp.at<double>(i, 0) >= double(1 - move_rows) &&
                gcps_tmp.at<double>(i, 1) <= double(cols - move_cols))
            {
                index.at<double>(i, 0) = 1.0;
                gcps_tmp.at<double>(i, 0) = gcps_tmp.at<double>(i, 0) + move_rows;
                count++;
            }
        }
        if (count == 0)
        {
            fprintf(stderr, "all ground control points have been sifted out!\n\n");
            return -1;
        }
        Mat gcps_tmp1 = Mat::zeros(count, gcps.cols, CV_64F);
        count = 0;
        for (int i = 0; i < gcps.rows; i++)
        {
            if (index.at<double>(i, 0) > 0.5)
            {
                gcps_tmp(Range(i, i + 1), Range(0, gcps.cols)).copyTo(gcps_tmp1(Range(count, count + 1), Range(0, gcps.cols)));
                count++;
            }
        }
        gcps = gcps_tmp1;
    }
    //////////////////////////Scenario 4///////////////////////////////
    if (move_rows <= 0 && move_cols <= 0)
    {
        for (int i = 0; i < gcps_tmp.rows; i++)
        {
            if (gcps_tmp.at<double>(i, 0) >= double(1 - move_rows) &&
                gcps_tmp.at<double>(i, 1) >= double(1 - move_cols))
            {
                index.at<double>(i, 0) = 1.0;
                gcps_tmp.at<double>(i, 0) = gcps_tmp.at<double>(i, 0) + move_rows;
                gcps_tmp.at<double>(i, 1) = gcps_tmp.at<double>(i, 1) + move_cols;
                count++;
            }
        }
        if (count == 0)
        {
            fprintf(stderr, "all ground control points have been sifted out!\n\n");
            return -1;
        }
        Mat gcps_tmp1 = Mat::zeros(count, gcps.cols, CV_64F);
        count = 0;
        for (int i = 0; i < gcps.rows; i++)
        {
            if (index.at<double>(i, 0) > 0.5)
            {
                gcps_tmp(Range(i, i + 1), Range(0, gcps.cols)).copyTo(gcps_tmp1(Range(count, count + 1), Range(0, gcps.cols)));
                count++;
            }
        }
        gcps = gcps_tmp1;
    }
    Mat GCPS(2 + gcps.rows, 5, CV_64F);
    for (int i = 0; i < 2; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            GCPS.at<double>(i, j) = GCP.at<double>(i, j);
        }
    }
    for (int i = 2; i < GCPS.rows; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            GCPS.at<double>(i, j) = gcps.at<double>(i - 2, j);
        }
    }
    GCPS.copyTo(gcps);
    return 0;
}
View Code

5.4:Utils.cpp

// Utils.cpp : 定义 DLL 应用程序的导出函数。
//
#include<complex.h>
#include<fstream>
#include<iostream>
#include<queue>
#include"Utils.h"


using namespace cv;
using namespace std;
using namespace InSAR;


/*宏定义*/
#define RETURN_MSG \
{ \
    if( fp ) fclose( fp ); \
    return( -1 ); \
}

#define GET_NEXT_LINE \
{ \
    if( !fgets( instring, 256, fp ) ) \
        ch = 0; \
        else \
        ch = *instring; \
}
inline bool return_check(int ret, const char* detail_info, const char* error_head)
{
    if (ret < 0)
    {
        fprintf(stderr, "%s %s\n\n", error_head, detail_info);
        return true;
    }
    else
    {
        return false;
    }
}

inline bool read_check(long ret, long ret_ref, const char* detail_info, const char* error_head)
{
    if (ret != ret_ref)
    {
        fprintf(stderr, "%s %s\n\n", error_head, detail_info);
        return true;
    }
    return false;
}

inline bool parallel_check(volatile bool parallel_flag, const char* detail_info, const char* parallel_error_head)
{
    if (!parallel_flag)
    {
        fprintf(stderr, "%s %s\n\n", parallel_error_head, detail_info);
        return true;
    }
    else
    {
        return false;
    }
}

inline bool parallel_flag_change(volatile bool parallel_flag, int ret)
{
    if (ret < 0)
    {
        parallel_flag = false;
        return true;
    }
    else
    {
        return false;
    }
}
Utils::Utils()
{
    memset(this->error_head, 0, 256);
    memset(this->parallel_error_head, 0, 256);
    strcpy(this->error_head, "UTILS_DLL_ERROR: error happens when using ");
    strcpy(this->parallel_error_head, "UTILS_DLL_ERROR: error happens when using parallel computing in function: ");
}

Utils::~Utils()
{
}

int Utils::get_mode_index(const Mat& input, int* out)
{
    if (input.empty() || input.type() != CV_32S || out == NULL)
    {
        fprintf(stderr, "get_mode_index(): input check failed!\n");
        return -1;
    }
    Mat temp; input.copyTo(temp);
    int nr = temp.rows; int nc = temp.cols;
    temp = temp.reshape(0, 1);
    cv::sort(temp, temp, cv::SORT_EVERY_ROW + cv::SORT_ASCENDING);
    int total = nr * nc;
    int max_count = 0; int max_count_ix = 0; int count = 0, i = 0, j = 0;
    while (i < total - 1)
    {
        count = 0;
        for (j = i; j < total - 1; j++)
        {
            if (temp.at<int>(0, j) == temp.at<int>(0, j + 1)) count++;
            else break;
        }
        if (max_count < count)
        {
            max_count = count;
            max_count_ix = j;
        }
        j++;
        i = j;
    }
    *out = temp.at<int>(0, max_count_ix);
    return 0;
}

int Utils::diff(Mat& Src, Mat& diff1, Mat& diff2, bool same)
{
    int nr = Src.rows;
    int nc = Src.cols;
    if (nr < 2 || nc < 2 || Src.type() != CV_64F)
    {
        fprintf(stderr, "diff(): input check failed!\n\n");
        return -1;
    }

    diff1 = Src(Range(1, nr), Range(0, nc)) - Src(Range(0, nr - 1), Range(0, nc));
    diff2 = Src(Range(0, nr), Range(1, nc)) - Src(Range(0, nr), Range(0, nc - 1));
    if (same)
    {
        copyMakeBorder(diff1, diff1, 0, 1, 0, 0, BORDER_CONSTANT, Scalar(0.0));
        copyMakeBorder(diff2, diff2, 0, 0, 0, 1, BORDER_CONSTANT, Scalar(0.0));
    }
    return 0;
}



int Utils::generate_phase(const ComplexMat& Master, const ComplexMat& Slave, Mat& phase)
{
    if (Master.GetRows() < 1 ||
        Master.GetCols() < 1 ||
        Slave.GetRows() != Master.GetRows() ||
        Slave.GetCols() != Master.GetCols())
    {
        fprintf(stderr, "generate_phase(): input check failed!\n\n");
        return -1;
    }
    ComplexMat tmp;
    int ret = Master.Mul(Slave, tmp, true);
    phase = tmp.GetPhase();
    return 0;
}

int Utils::write_DIMACS(const char* DIMACS_file_problem, triangle* tri, int num_triangle, vector<tri_node>& nodes, tri_edge* edges, long num_edges, Mat& cost)
{
    if (DIMACS_file_problem == NULL ||
        tri == NULL ||
        num_triangle < 1 ||
        nodes.size() < 3 ||
        edges == NULL ||
        num_edges < 3 ||
        cost.rows < 2 ||
        cost.cols < 2 ||
        cost.channels() != 1 ||
        cost.type() != CV_64F
        )
    {
        fprintf(stderr, "write_DIMACS(): input check failed!\n\n");
        return -1;
    }
    FILE* fp = NULL;
    fp = fopen(DIMACS_file_problem, "wt");
    if (fp == NULL)
    {
        fprintf(stderr, "write_DIMACS(): can't open %s\n", DIMACS_file_problem);
        return -1;
    }

    int ret, num_nodes;

    num_nodes = nodes.size();
    long num_arcs = 0;
    for (int i = 0; i < num_triangle; i++)
    {
        if ((tri + i) != NULL)
        {
            if ((tri + i)->neigh1 > 0) num_arcs++;
            if ((tri + i)->neigh2 > 0) num_arcs++;
            if ((tri + i)->neigh3 > 0) num_arcs++;
        }
    }

    //统计正负残差点并写入节点信息
    int positive, negative, total;
    positive = 0;
    negative = 0;
    double thresh = 0.7;
    for (int i = 0; i < num_triangle; i++)
    {
        if ((tri + i)->residue > thresh)
        {
            positive++;
        }
        if ((tri + i)->residue < -thresh)
        {
            negative++;
        }
    }
    bool b_balanced = (positive == negative);
    if (negative == 0 || positive == 0)
    {
        if (fp) fclose(fp);
        fprintf(stderr, "write_DIMACS(): no residue point!\n\n");
        return -1;
    }
    fprintf(fp, "c This is MCF problem file.\n");
    fprintf(fp, "c Problem line(nodes, links)\n");
    //统计边缘三角形个数
    long boundry_tri = 0;
    for (int i = 0; i < num_triangle; i++)
    {
        if ((tri + i) != NULL)
        {
            if ((edges + (tri + i)->edge1 - 1)->isBoundry ||
                (edges + (tri + i)->edge2 - 1)->isBoundry ||
                (edges + (tri + i)->edge3 - 1)->isBoundry)
            {
                boundry_tri++;
            }
        }
    }
    long n;
    if (!b_balanced)
    {
        n = num_triangle + 1;
        fprintf(fp, "p min %ld %ld\n", n, num_arcs + boundry_tri * 2);
    }
    else
    {
        n = num_triangle;
        fprintf(fp, "p min %ld %ld\n", n, num_arcs);
    }
    fprintf(fp, "c Node descriptor lines\n");
    positive = 0;
    negative = 0;
    int count = 0;
    bool b_positive, b_negative, is_residue;
    double sum = 0.0;
    for (int i = 0; i < num_triangle; i++)
    {
        if ((tri + i) != NULL && (tri + i)->residue > thresh)
        {
            fprintf(fp, "n %d %lf\n", i + 1, (tri + i)->residue);
            sum += (tri + i)->residue;
        }
        if ((tri + i) != NULL && (tri + i)->residue < -thresh)
        {
            fprintf(fp, "n %d %lf\n", i + 1, (tri + i)->residue);
            sum += (tri + i)->residue;
        }
    }
    //写入大地节点
    if (!b_balanced)
    {
        fprintf(fp, "n %d %lf\n", num_triangle + 1, -sum);
    }

    //写入流费用
    fprintf(fp, "c Arc descriptor lines(from, to, minflow, maxflow, cost)\n");
    int rows, cols;
    int lower_bound = 0;
    int upper_bound = 5;
    double cost_mean;
    int nr = cost.rows;
    int nc = cost.cols;
    for (int i = 0; i < num_triangle; i++)
    {
        if ((tri + i) != NULL &&
            (tri + i)->p1 >= 1 &&
            (tri + i)->p1 <= num_nodes &&
            (tri + i)->p2 >= 1 &&
            (tri + i)->p2 <= num_nodes &&
            (tri + i)->p3 >= 1 &&
            (tri + i)->p3 <= num_nodes
            )
        {
            cost_mean = 0.0;
            nodes[(tri + i)->p1 - 1].get_pos(&rows, &cols);
            if (rows >= 0 && rows <= nr - 1) cost_mean += cost.at<double>(rows, cols);
            nodes[(tri + i)->p2 - 1].get_pos(&rows, &cols);
            if (rows >= 0 && rows <= nr - 1) cost_mean += cost.at<double>(rows, cols);
            nodes[(tri + i)->p3 - 1].get_pos(&rows, &cols);
            if (rows >= 0 && rows <= nr - 1) cost_mean += cost.at<double>(rows, cols);
            cost_mean = cost_mean / 3;
            if ((tri + i)->neigh1 > 0) fprintf(fp, "a %d %d %d %d %lf\n", i + 1, (tri + i)->neigh1, lower_bound, upper_bound, cost_mean);
            if ((tri + i)->neigh2 > 0) fprintf(fp, "a %d %d %d %d %lf\n", i + 1, (tri + i)->neigh2, lower_bound, upper_bound, cost_mean);
            if ((tri + i)->neigh3 > 0) fprintf(fp, "a %d %d %d %d %lf\n", i + 1, (tri + i)->neigh3, lower_bound, upper_bound, cost_mean);
        }
    }
    if (!b_balanced)
    {
        //写入边界流费用
        for (int i = 0; i < num_triangle; i++)
        {
            if ((tri + i) != NULL &&
                (((edges + (tri + i)->edge1 - 1)->isBoundry) ||
                    ((edges + (tri + i)->edge2 - 1)->isBoundry) ||
                    ((edges + (tri + i)->edge3 - 1)->isBoundry)
                    ))
            {
                cost_mean = 0.0;
                nodes[(tri + i)->p1 - 1].get_pos(&rows, &cols);
                if (rows >= 0 && rows <= nr - 1) cost_mean += cost.at<double>(rows, cols);
                nodes[(tri + i)->p2 - 1].get_pos(&rows, &cols);
                if (rows >= 0 && rows <= nr - 1) cost_mean += cost.at<double>(rows, cols);
                nodes[(tri + i)->p3 - 1].get_pos(&rows, &cols);
                if (rows >= 0 && rows <= nr - 1) cost_mean += cost.at<double>(rows, cols);
                cost_mean = cost_mean / 3;
                fprintf(fp, "a %d %d %d %d %lf\n", i + 1, num_triangle + 1, lower_bound, upper_bound, cost_mean);
                fprintf(fp, "a %d %d %d %d %lf\n", num_triangle + 1, i + 1, lower_bound, upper_bound, cost_mean);
            }
        }
    }
    if (fp) fclose(fp);
    fp = NULL;
    return 0;
}

int Utils::write_DIMACS(
    const char* DIMACS_file_problem,
    vector<triangle>& triangle,
    vector<tri_node>& nodes,
    vector<tri_edge>& edges,
    const Mat& cost
)
{
    if (DIMACS_file_problem == NULL ||
        triangle.size() < 1 ||
        nodes.size() < 3 ||
        edges.size() < 3 ||
        cost.rows < 2 ||
        cost.cols < 2 ||
        cost.channels() != 1 ||
        cost.type() != CV_64F
        )
    {
        fprintf(stderr, "write_DIMACS(): input check failed!\n\n");
        return -1;
    }
    FILE* fp = NULL;
    fp = fopen(DIMACS_file_problem, "wt");
    if (fp == NULL)
    {
        fprintf(stderr, "write_DIMACS(): can't open %s\n", DIMACS_file_problem);
        return -1;
    }

    int ret, num_nodes;
    int num_triangle = triangle.size();
    num_nodes = nodes.size();
    long num_arcs = 0;
    for (int i = 0; i < num_triangle; i++)
    {
        if (triangle[i].neigh1 > 0) num_arcs++;
        if (triangle[i].neigh2 > 0) num_arcs++;
        if (triangle[i].neigh3 > 0) num_arcs++;
    }

    //统计正负残差点并写入节点信息
    int positive, negative, total;
    positive = 0;
    negative = 0;
    double thresh = 0.7;
    for (int i = 0; i < num_triangle; i++)
    {
        if (triangle[i].residue > thresh)
        {
            positive++;
        }
        if (triangle[i].residue < -thresh)
        {
            negative++;
        }
    }
    bool b_balanced = (positive == negative);
    if (negative == 0 && positive == 0)
    {
        if (fp) fclose(fp);
        fprintf(stderr, "write_DIMACS(): no residue point!\n\n");
        return -1;
    }
    fprintf(fp, "c This is MCF problem file.\n");
    fprintf(fp, "c Problem line(nodes, links)\n");
    //统计边缘三角形个数
    long boundry_tri = 0;
    for (int i = 0; i < num_triangle; i++)
    {
        if (edges[triangle[i].edge1 - 1].isBoundry ||
            edges[triangle[i].edge2 - 1].isBoundry ||
            edges[triangle[i].edge3 - 1].isBoundry)
        {
            boundry_tri++;
        }
    }
    long n;
    if (!b_balanced)
    {
        n = num_triangle + 1;
        fprintf(fp, "p min %ld %ld\n", n, num_arcs + boundry_tri * 2);
    }
    else
    {
        n = num_triangle;
        fprintf(fp, "p min %ld %ld\n", n, num_arcs);
    }
    fprintf(fp, "c Node descriptor lines\n");
    positive = 0;
    negative = 0;
    int count = 0;
    bool b_positive, b_negative, is_residue;
    double sum = 0.0;
    for (int i = 0; i < num_triangle; i++)
    {
        if (triangle[i].residue > thresh)
        {
            fprintf(fp, "n %d %lf\n", i + 1, triangle[i].residue);
            sum += triangle[i].residue;
        }
        if (triangle[i].residue < -thresh)
        {
            fprintf(fp, "n %d %lf\n", i + 1, triangle[i].residue);
            sum += triangle[i].residue;
        }
    }
    //写入大地节点
    if (!b_balanced)
    {
        fprintf(fp, "n %d %lf\n", num_triangle + 1, -sum);
    }

    //写入流费用
    fprintf(fp, "c Arc descriptor lines(from, to, minflow, maxflow, cost)\n");
    int rows, cols;
    int lower_bound = 0;
    int upper_bound = 1;
    double cost_mean;
    int nr = cost.rows;
    int nc = cost.cols;
    for (int i = 0; i < num_triangle; i++)
    {
        if (triangle[i].p1 >= 1 &&
            triangle[i].p1 <= num_nodes &&
            triangle[i].p2 >= 1 &&
            triangle[i].p2 <= num_nodes &&
            triangle[i].p3 >= 1 &&
            triangle[i].p3 <= num_nodes
            )
        {
            cost_mean = 0.0;
            nodes[triangle[i].p1 - 1].get_pos(&rows, &cols);
            if (rows >= 0 && rows <= nr - 1) cost_mean += cost.at<double>(rows, cols);
            nodes[triangle[i].p2 - 1].get_pos(&rows, &cols);
            if (rows >= 0 && rows <= nr - 1) cost_mean += cost.at<double>(rows, cols);
            nodes[triangle[i].p3 - 1].get_pos(&rows, &cols);
            if (rows >= 0 && rows <= nr - 1) cost_mean += cost.at<double>(rows, cols);
            cost_mean = cost_mean / 3;
            if (triangle[i].neigh1 > 0) fprintf(fp, "a %d %d %d %d %lf\n", i + 1, triangle[i].neigh1, lower_bound, upper_bound, cost_mean);
            if (triangle[i].neigh2 > 0) fprintf(fp, "a %d %d %d %d %lf\n", i + 1, triangle[i].neigh2, lower_bound, upper_bound, cost_mean);
            if (triangle[i].neigh3 > 0) fprintf(fp, "a %d %d %d %d %lf\n", i + 1, triangle[i].neigh3, lower_bound, upper_bound, cost_mean);
        }
    }
    if (!b_balanced)
    {
        //写入边界流费用
        for (int i = 0; i < num_triangle; i++)
        {
            if (edges[triangle[i].edge1 - 1].isBoundry ||
                edges[triangle[i].edge2 - 1].isBoundry ||
                edges[triangle[i].edge3 - 1].isBoundry)
            {
                cost_mean = 0.0;
                nodes[triangle[i].p1 - 1].get_pos(&rows, &cols);
                if (rows >= 0 && rows <= nr - 1) cost_mean += cost.at<double>(rows, cols);
                nodes[triangle[i].p2 - 1].get_pos(&rows, &cols);
                if (rows >= 0 && rows <= nr - 1) cost_mean += cost.at<double>(rows, cols);
                nodes[triangle[i].p3 - 1].get_pos(&rows, &cols);
                if (rows >= 0 && rows <= nr - 1) cost_mean += cost.at<double>(rows, cols);
                cost_mean = cost_mean / 3;
                fprintf(fp, "a %d %d %d %d %lf\n", i + 1, num_triangle + 1, lower_bound, upper_bound, cost_mean);
                fprintf(fp, "a %d %d %d %d %lf\n", num_triangle + 1, i + 1, lower_bound, upper_bound, cost_mean);
            }
        }
    }
    if (fp) fclose(fp);
    fp = NULL;
    return 0;
}

int Utils::read_DIMACS(const char* DIMACS_file_solution, Mat& k1, Mat& k2, int rows, int cols)
{
    if (rows < 2 || cols < 2)
    {
        fprintf(stderr, "read_DIMACS(): input check failed!\n\n");
        return -1;
    }
    char instring[256];
    char ch;
    double obj_value = 0;
    int i, row_index, col_index, symbol;
    long from, to;
    double flow = 0;
    k1 = Mat::zeros(rows - 1, cols, CV_64F);
    k2 = Mat::zeros(rows, cols - 1, CV_64F);
    long earth_node_indx = (rows - 1) * (cols - 1) + 1;
    FILE* fp = NULL;
    fp = fopen( DIMACS_file_solution, "rt");
    if (fp == NULL)
    {
        fprintf(stderr, "read_DIMACS(): can't open file %s\n\n", DIMACS_file_solution);
        return -1;
    }
    /////////////////////读取注释///////////////////////////
    GET_NEXT_LINE;
    while (ch != 's' && ch)
    {
        if (ch != 'c')
        {
            if (fp) fclose(fp);
            fprintf(stderr, "read_DIMACS(): unknown file format!\n\n");
            return -1;
        }
        GET_NEXT_LINE;
    }
    /////////////////////读取优化目标值/////////////////////
    for (i = 1; i < 81; i++)
    {
        if (isspace((int)instring[i]) > 0)
        {
            i++;
            break;
        }
    }
    if (sscanf(&(instring[i]), "%lf", &obj_value) != 1)
    {
        if (fp) fclose(fp);
        fprintf(stderr, "read_DIMACS(): unknown file format!\n\n");
        return -1;
    }
    if (obj_value < 0.0)
    {
        if (fp) fclose(fp);
        fprintf(stderr, "read_DIMACS(): this problem can't be solved(unbounded or infeasible)!\n\n");
        return -1;
    }
    ////////////////////读取MCF结果/////////////////////////
    GET_NEXT_LINE;
    while (ch && ch == 'f')
    {
        if (sscanf(&(instring[2]), "%ld %ld %lf", &from, &to, &flow) != 3 ||
            flow < 0.0 || from < 0 || to < 0)
        {
            if (fp) fclose(fp);
            fprintf(stderr, "read_DIMACS(): unknown file format!\n\n");
            return -1;
        }
        //是否为接地弧
        if (from == earth_node_indx || to == earth_node_indx)
        {
            if (from == earth_node_indx)
            {
                //top
                if (to <= cols - 1)
                {
                    row_index = 0;
                    col_index = to - 1;
                    symbol = 1;
                    k2.at<double>(row_index, col_index) = k2.at<double>(row_index, col_index) + symbol * flow;
                }
                //bottom
                else if (to > (rows - 2) * (cols - 1))
                {
                    symbol = -1;
                    row_index = rows - 1;
                    col_index = to - (rows - 2) * (cols - 1) - 1;
                    k2.at<double>(row_index, col_index) = k2.at<double>(row_index, col_index) + symbol * flow;
                }
                //right
                else if (to % (cols - 1) == 0 && to < (rows - 2) * (cols - 1) && to >(cols - 1))
                {
                    symbol = 1;
                    row_index = to / (cols - 1) - 1;
                    col_index = cols - 1;
                    k1.at<double>(row_index, col_index) = k1.at<double>(row_index, col_index) + symbol * flow;
                }
                //left
                else
                {
                    symbol = -1;
                    row_index = to / (cols - 1);
                    col_index = 0;
                    k1.at<double>(row_index, col_index) = k1.at<double>(row_index, col_index) + symbol * flow;
                }
            }
            else
            {
                /*long tmp;
                tmp = from;
                from = to;
                to = tmp;*/
                //top
                if (from <= cols - 1)
                {
                    symbol = -1;
                    row_index = 0;
                    col_index = from - 1;
                    k2.at<double>(row_index, col_index) = k2.at<double>(row_index, col_index) + symbol * flow;
                }
                //bottom
                else if (from >= (rows - 2) * (cols - 1))
                {
                    symbol = 1;
                    row_index = rows - 1;
                    col_index = from - (rows - 2) * (cols - 1) - 1;
                    k2.at<double>(row_index, col_index) = k2.at<double>(row_index, col_index) + symbol * flow;
                }
                //right
                else if (from % (cols - 1) == 0 && from < (rows - 2) * (cols - 1) && from >(cols - 1))
                {
                    symbol = -1;
                    row_index = from / (cols - 1) - 1;
                    col_index = cols - 1;
                    k1.at<double>(row_index, col_index) = k1.at<double>(row_index, col_index) + symbol * flow;
                }
                //left
                else
                {
                    symbol = 1;
                    row_index = from / (cols - 1);
                    col_index = 0;
                    k1.at<double>(row_index, col_index) = k1.at<double>(row_index, col_index) + symbol * flow;
                }
            }
        }
        else
        {
            if (abs(from - to) > 1)
            {
                symbol = from > to ? 1 : -1;
                row_index = (from > to ? to : from) / long(cols - 1);
                col_index = (from > to ? to : from) % long(cols - 1);
                if (col_index == 0)
                {
                    col_index = cols - 1;
                    row_index--;
                }
                col_index--;
                k2.at<double>(row_index + 1, col_index) = k2.at<double>(row_index + 1, col_index) + symbol * flow;
            }
            else
            {
                symbol = from > to ? 1 : -1;
                row_index = (from > to ? to : from) / long(cols - 1);
                col_index = (from > to ? to : from) % long(cols - 1);
                k1.at<double>(row_index, col_index) = k1.at<double>(row_index, col_index) + symbol * flow;
            }
        }
        GET_NEXT_LINE;
    }
    if (ch != 'c')
    {
        if (fp) fclose(fp);
        fprintf(stderr, "read_DIMACS(): unknown file format!\n\n");
        return -1;
    }
    if (fp)
    {
        fclose(fp);
    }
    return 0;
}

int Utils::write_DIMACS(const char* DIMACS_file_problem, Mat& residue, Mat& coherence, double thresh)
{

    if (residue.cols < 2 ||
        residue.rows < 2 ||
        coherence.cols < 2 ||
        coherence.rows < 2 ||
        residue.type() != CV_64F ||
        coherence.type() != CV_64F ||
        (coherence.rows - residue.rows) != 1 ||
        (coherence.cols - residue.cols) != 1 ||
        thresh < 0.0)
    {
        fprintf(stderr, "write_DIMACS(): input check failed!\n\n");
        return -1;
    }
    long nr = residue.rows;
    long nc = residue.cols;
    long i, j;
    long node_index = 1;
    double sum = 0.0;
    //统计正负残差点数
    long positive, negative, total, Arcs_num, Nodes_num;
    positive = 0;
    negative = 0;
    for (i = 0; i < nr; i++)
    {
        for (j = 0; j < nc; j++)
        {
            if (residue.at<double>(i, j) > thresh)
            {
                positive++;
            }
            if (residue.at<double>(i, j) < -thresh)
            {
                negative++;
            }
        }
    }
    bool b_balanced = (positive == negative);
    if (!b_balanced)
    {
        Nodes_num = residue.rows * residue.cols + 1;
        Arcs_num = 2 * (residue.rows - 1) * residue.cols + 2 * residue.rows * (residue.cols - 1) +
            2 * 2 * residue.cols + 2 * 2 * (residue.rows - 2);
    }
    else
    {
        Nodes_num = residue.rows * residue.cols;
        Arcs_num = 2 * (residue.rows - 1) * residue.cols + 2 * residue.rows * (residue.cols - 1);
    }
    ofstream fout;
    FILE* fp = NULL;
    fp = fopen(DIMACS_file_problem, "wt");
    if (!fp)
    {
        fprintf(stderr, "write_DIMACS(): cant't open file %s\n\n", DIMACS_file_problem);
        return -1;
    }
    fprintf(fp, "c This is a DIMACS file, describing Minimum Cost Flow problem.\n");
    fprintf(fp, "c Problem line (nodes, links)\n");
    fprintf(fp, "p min %ld %ld\n", Nodes_num, Arcs_num);
    fprintf(fp, "c Node descriptor lines (supply+ or demand-)\n");


    /*
    * 写入节点的度(残差值1,-1)
    */

    for (i = 0; i < nr; i++)
    {
        for (j = 0; j < nc; j++)
        {
            if (residue.at<double>(i, j) > thresh)
            {
                node_index = i * nc + j + 1;
                fprintf(fp, "n %ld %lf\n", node_index, residue.at<double>(i, j));
                sum += residue.at<double>(i, j);
            }
            if (residue.at<double>(i, j) < -thresh)
            {
                node_index = i * nc + j + 1;
                fprintf(fp, "n %ld %lf\n", node_index, residue.at<double>(i, j));
                sum += residue.at<double>(i, j);
            }
        }
    }

    /*写接地节点*/

    node_index = nc * nr + 1;
    if (!b_balanced)
    {
        fprintf(fp, "n %ld %lf\n", node_index, -sum);
    }


    long earth_node_index = node_index;

    /*
    * 写入每个有向弧的费用(流费用)
    */
    long lower_bound = 0;
    long upper_bound = 5;
    double mean_coherence1, mean_coherence2, mean_coherence3, mean_coherence4;
    fprintf(fp, "c Arc descriptor lines (from, to, minflow, maxflow, cost)\n");
    /*接地节点的有向弧流费用*/
    if (!b_balanced)
    {
        //top
        for (i = 0; i < nc; i++)
        {
            node_index = i + 1;
            mean_coherence1 = coherence.at<double>(0, i);
            mean_coherence2 = mean_coherence1;
            fprintf(fp, "a %ld %ld %ld %ld %lf\na %ld %ld %ld %ld %lf\n",
                node_index, earth_node_index, lower_bound, upper_bound, mean_coherence1,
                earth_node_index, node_index, lower_bound, upper_bound, mean_coherence2);
        }
        //bottom
        for (i = 0; i < nc; i++)
        {
            node_index = nc * (nr - 1) + i + 1;
            mean_coherence1 = coherence.at<double>(nr - 1, i);
            mean_coherence2 = mean_coherence1;
            fprintf(fp, "a %ld %ld %ld %ld %lf\na %ld %ld %ld %ld %lf\n",
                node_index, earth_node_index, lower_bound, upper_bound, mean_coherence1,
                earth_node_index, node_index, lower_bound, upper_bound, mean_coherence2);
        }
        //left
        for (i = 1; i < nr - 1; i++)
        {
            node_index = nc * i + 1;
            mean_coherence1 = coherence.at<double>(i, 0);
            mean_coherence2 = mean_coherence1;
            fprintf(fp, "a %ld %ld %ld %ld %lf\na %ld %ld %ld %ld %lf\n",
                node_index, earth_node_index, lower_bound, upper_bound, mean_coherence1,
                earth_node_index, node_index, lower_bound, upper_bound, mean_coherence2);
        }
        //right
        for (i = 1; i < nr - 1; i++)
        {
            node_index = nc * (i + 1);
            mean_coherence1 = coherence.at<double>(i, nc - 1);
            mean_coherence2 = mean_coherence1;
            fprintf(fp, "a %ld %ld %ld %ld %lf\na %ld %ld %ld %ld %lf\n",
                node_index, earth_node_index, lower_bound, upper_bound, mean_coherence1,
                earth_node_index, node_index, lower_bound, upper_bound, mean_coherence2);
        }
    }

    /*非接地节点的有向弧流费用*/
    for (i = 0; i < nr - 1; i++)
    {
        for (j = 0; j < nc - 1; j++)
        {
            node_index = i * nc + j + 1;
            /*正向*/
            mean_coherence1 = mean(coherence(Range(i, i + 2), Range(j, j + 3))).val[0];
            /*逆向*/
            mean_coherence2 = mean(coherence(Range(i, i + 2), Range(j, j + 3))).val[0];


            /*正向*/
            mean_coherence3 = mean(coherence(Range(i, i + 3), Range(j, j + 2))).val[0];
            /*逆向*/
            mean_coherence4 = mean(coherence(Range(i, i + 3), Range(j, j + 2))).val[0];
            fprintf(fp, "a %ld %ld %ld %ld %lf\na %ld %ld %ld %ld %lf\na %ld %ld %ld %ld %lf\na %ld %ld %ld %ld %lf\n",
                node_index, node_index + 1, lower_bound, upper_bound, mean_coherence1,
                node_index + 1, node_index, lower_bound, upper_bound, mean_coherence2,
                node_index, node_index + nc, lower_bound, upper_bound, mean_coherence3,
                node_index + nc, node_index, lower_bound, upper_bound, mean_coherence4);

        }
    }



    for (j = 0; j < nc - 1; j++)
    {
        node_index = (nr - 1) * nc + j + 1;
        /*正向*/
        mean_coherence1 = mean(coherence(Range(nr - 1, nr + 1), Range(j, j + 3))).val[0];
        /*逆向*/
        mean_coherence2 = mean(coherence(Range(nr - 1, nr + 1), Range(j, j + 3))).val[0];
        fprintf(fp, "a %ld %ld %ld %ld %lf\na %ld %ld %ld %ld %lf\n",
            node_index, node_index + 1, lower_bound, upper_bound, mean_coherence1,
            node_index + 1, node_index, lower_bound, upper_bound, mean_coherence2);
    }

    for (i = 0; i < nr - 1; i++)
    {
        node_index = (i + 1) * nc;
        /*正向*/
        mean_coherence1 = mean(coherence(Range(i, i + 3), Range(nc - 1, nc + 1))).val[0];
        /*逆向*/
        mean_coherence2 = mean(coherence(Range(i, i + 3), Range(nc - 1, nc + 1))).val[0];
        fprintf(fp, "a %ld %ld %ld %ld %lf\na %ld %ld %ld %ld %lf\n",
            node_index, node_index + nc, lower_bound, upper_bound, mean_coherence1,
            node_index + nc, node_index, lower_bound, upper_bound, mean_coherence2);
    }
    fprintf(fp, "c ");
    fprintf(fp, "c End of file");
    if (fp) fclose(fp);
    fp = NULL;
    return 0;
}

int Utils::cumsum(Mat& phase, int dim)
{
    /*
    cumulates along the dimension specified by dim
    dim = 1,按列计算
    dim = 2,按行计算
    */
    int rows = phase.rows;
    int cols = phase.cols;
    int i, j;
    if (rows > 0 && cols > 0)
    {
        switch (dim)
        {
        case 1:
            if (phase.rows < 2 ||
                phase.cols < 1 ||
                phase.type() != CV_64F)
            {
                fprintf(stderr, "cumsum(): input check failed!\n\n");
                return -1;
            }
            for (j = 0; j < cols; j++)
            {
                for (i = 1; i < rows; i++)
                {
                    phase.at<double>(i, j) = phase.at<double>(i - 1, j) + phase.at<double>(i, j);
                }
            }
            break;
        case 2:
            if (phase.rows < 1 ||
                phase.cols < 2 ||
                phase.type() != CV_64F)
            {
                fprintf(stderr, "cumsum(): input check failed!\n\n");
                return -1;
            }
            for (i = 0; i < rows; i++)
            {
                for (j = 1; j < cols; j++)
                {
                    phase.at<double>(i, j) = phase.at<double>(i, j - 1) + phase.at<double>(i, j);
                }
            }
            break;
        default:
            break;
        }
    }
    return 0;
}

int Utils::cross(Mat& vec1, Mat& vec2, Mat& out)
{
    if (vec1.cols != 3 ||
        vec1.rows < 1 ||
        vec1.type() != CV_64F ||
        vec1.channels() != 1 ||
        vec1.cols != vec2.cols ||
        vec1.rows != vec2.rows ||
        vec2.channels() != 1 ||
        vec2.type() != CV_64F
        )
    {
        fprintf(stderr, "cross(): input check failed!\n\n");
        return -1;
    }
    Mat out_tmp = Mat::zeros(vec1.rows, vec1.cols, CV_64F);
    int rows = vec1.rows;
    for (int i = 0; i < rows; i++)
    {
        out_tmp.at<double>(i, 0) = vec1.at<double>(i, 1) * vec2.at<double>(i, 2) -
            vec1.at<double>(i, 2) * vec2.at<double>(i, 1);//a(2) * b(3) - a(3) * b(2)

        out_tmp.at<double>(i, 1) = vec1.at<double>(i, 2) * vec2.at<double>(i, 0) -
            vec1.at<double>(i, 0) * vec2.at<double>(i, 2);//a(3) * b(1) - a(1) * b(3)

        out_tmp.at<double>(i, 2) = vec1.at<double>(i, 0) * vec2.at<double>(i, 1) -
            vec1.at<double>(i, 1) * vec2.at<double>(i, 0);//a(1) * b(2) - a(2) * b(1)
    }
    out_tmp.copyTo(out);
    return 0;
}

int Utils::gen_mask(Mat& coherence, Mat& phase_derivatives, Mat& mask, int wnd_size, double coh_thresh, double phase_derivative_thresh)
{
    if (coherence.rows < 2 ||
        coherence.cols < 2 ||
        coherence.channels() != 1 ||
        coherence.type() != CV_64F ||
        coherence.rows != phase_derivatives.rows ||
        coherence.cols != phase_derivatives.cols ||
        phase_derivatives.channels() != 1 ||
        phase_derivatives.type() != CV_64F ||
        wnd_size < 0 ||
        wnd_size > coherence.rows ||
        coh_thresh < 0.0 ||
        coh_thresh > 1.0 ||
        phase_derivative_thresh < 0.0
        )
    {
        fprintf(stderr, "gen_mask(): input check failed!\n\n");
        return -1;
    }
    int nr = coherence.rows;
    int nc = coherence.cols;
    Mat temp_coh, temp_phase_derivatives;
    coherence.copyTo(temp_coh);
    phase_derivatives.copyTo(temp_phase_derivatives);
    int radius = (wnd_size - 1) / 2;
    cv::copyMakeBorder(temp_coh, temp_coh, radius, radius, radius, radius, cv::BORDER_DEFAULT);
    cv::copyMakeBorder(temp_phase_derivatives, temp_phase_derivatives, radius, radius, radius, radius, cv::BORDER_DEFAULT);
    Mat tmp = Mat::zeros(nr, nc, CV_32S);
    tmp.copyTo(mask);
#pragma omp parallel for schedule(guided)
    for (int i = 0; i < nr; i++)
    {
        double mean, mean1;
        for (int j = 0; j < nc; j++)
        {
            mean = cv::mean(temp_coh(cv::Range(i, i + 2 * radius + 1), cv::Range(j, j + 2 * radius + 1)))[0];
            mean1 = cv::mean(temp_phase_derivatives(cv::Range(i, i + 2 * radius + 1), cv::Range(j, j + 2 * radius + 1)))[0];
            if (mean > coh_thresh &&
                coherence.at<double>(i, j) > coh_thresh &&
                mean1 < phase_derivative_thresh &&
                phase_derivatives.at<double>(i, j) < phase_derivative_thresh)
            {
                mask.at<int>(i, j) = 1;
            }

        }
    }
    return 0;
}

int Utils::residue_sift(Mat& residue_src, Mat& residue_dst, double thresh, long* num_residue)
{
    int rows = residue_src.rows;
    int cols = residue_src.cols;
    if (rows < 1 ||
        cols < 1 ||
        residue_src.type() != CV_64F ||
        residue_src.channels() != 1 ||
        thresh < 0.0 ||
        num_residue == NULL
        )
    {
        fprintf(stderr, "residue_sift(): input check failed!\n\n");
        return -1;
    }
    Mat tmp;
    residue_src.copyTo(tmp);
    *num_residue = 0;
    //#pragma omp parallel for schedule(guided)
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            if (std::fabs(residue_src.at<double>(i, j)) > thresh)
            {
                tmp.at<double>(i, j) = 1.0;
                *num_residue = *num_residue + 1;
            }
            else
            {
                tmp.at<double>(i, j) = 0.0;
            }
        }
    }
    tmp.copyTo(residue_dst);
    return 0;
}

int Utils::wrap(Mat& Src, Mat& Dst)
{
    int rows = Src.rows;
    int cols = Src.cols;
    double pi = 3.1415926535;
    if (rows < 1 || cols < 1 || Src.type() != CV_64F)
    {
        fprintf(stderr, "wrap(): input check failed!\n\n");
        return -1;
    }
    Mat tmp = Mat::zeros(rows, cols, CV_64F);
#pragma omp parallel for schedule(guided)
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            tmp.at<double>(i, j) = atan2(sin(Src.at<double>(i, j)), cos(Src.at<double>(i, j)));
        }
    }
    //Dst = tmp;
    tmp.copyTo(Dst);
    return 0;
}

int Utils::residue(Mat& phase, Mat& residuemat)
{
    int rows = phase.rows;
    int cols = phase.cols;
    int ret;
    if (rows < 2 || cols < 2 || phase.type() != CV_64F)
    {
        fprintf(stderr, "residue(): input check failed!\n\n");
        return -1;
    }
    Mat Diff_1 = phase(Range(1, rows), Range(0, cols)) - phase(Range(0, rows - 1), Range(0, cols));
    Mat Diff_2 = phase(Range(0, rows), Range(1, cols)) - phase(Range(0, rows), Range(0, cols - 1));
    ret = this->wrap(Diff_1, Diff_1);
    if (return_check(ret, "wrap(*, *)", error_head)) return -1;
    ret = this->wrap(Diff_2, Diff_2);
    if (return_check(ret, "wrap(*, *)", error_head)) return -1;

    Diff_1 = Diff_1(Range(0, Diff_1.rows), Range(1, Diff_1.cols)) -
        Diff_1(Range(0, Diff_1.rows), Range(0, Diff_1.cols - 1));

    Diff_2 = Diff_2(Range(1, Diff_2.rows), Range(0, Diff_2.cols)) -
        Diff_2(Range(0, Diff_2.rows - 1), Range(0, Diff_2.cols));

    Diff_1 = Diff_2 - Diff_1;
    double pi = 3.1415926535;
    Diff_1 = Diff_1 / (2 * pi);
    //Diff_1.copyTo(residuemat);
    residuemat = Diff_1;
    return 0;
}

int Utils::residue(triangle* tri, int num_triangle, vector<tri_node>& nodes, tri_edge* edges, int num_edges)
{
    if (tri == NULL ||
        num_triangle < 1 ||
        nodes.size() < 1 ||
        edges == NULL ||
        num_edges < 3
        )
    {
        fprintf(stderr, "residue(): input check failed!\n\n");
        return -1;
    }
    double thresh = 50.0;
    int num_nodes = nodes.size();
    int end1, end2, end3, tmp;
    double x1, y1, x2, y2, x3, y3, direction, delta12, delta23, delta31, residue, phi1, phi2, phi3, distance1,
        distance2, distance3;
    int row1, col1, row2, col2, row3, col3;
    bool b_res = false;
    for (int i = 0; i < num_triangle; i++)
    {
        if ((tri + i) != NULL)
        {
            end1 = (tri + i)->p1;
            end2 = (tri + i)->p2;
            end3 = (tri + i)->p3;
        }
        if (end1 > end2)
        {
            tmp = end1;
            end1 = end2;
            end2 = tmp;
        }

        nodes[end1 - 1].get_pos(&row1, &col1);
        nodes[end2 - 1].get_pos(&row2, &col2);
        nodes[end3 - 1].get_pos(&row3, &col3);

        nodes[end1 - 1].get_distance(nodes[end2 - 1], &distance1);
        nodes[end2 - 1].get_distance(nodes[end3 - 1], &distance2);
        nodes[end3 - 1].get_distance(nodes[end1 - 1], &distance3);
        b_res = true;
        if ((distance1 > thresh) || (distance2 > thresh) || (distance3 > thresh)) b_res = false;

        nodes[end1 - 1].get_phase(&phi1);
        nodes[end2 - 1].get_phase(&phi2);
        nodes[end3 - 1].get_phase(&phi3);

        x2 = double(col2 - col1);
        y2 = double(row1 - row2);
        x1 = double(col1 - col3);
        y1 = double(row3 - row1);
        direction = x1 * y2 - x2 * y1;

        delta12 = atan2(sin(phi2 - phi1), cos(phi2 - phi1));
        delta23 = atan2(sin(phi3 - phi2), cos(phi3 - phi2));
        delta31 = atan2(sin(phi1 - phi3), cos(phi1 - phi3));

        double res = (delta12 + delta23 + delta31) / 2.0 / PI;
        if (fabs(res) > 0.7 && !b_res)//标注边长超过阈值的残差边和残差节点
        {
            (edges + (tri + i)->edge1 - 1)->isResidueEdge = true;
            (edges + (tri + i)->edge2 - 1)->isResidueEdge = true;
            (edges + (tri + i)->edge3 - 1)->isResidueEdge = true;
            nodes[(tri + i)->p1 - 1].set_residue(true);
            nodes[(tri + i)->p2 - 1].set_residue(true);
            nodes[(tri + i)->p3 - 1].set_residue(true);
        }
        res = b_res ? res : 0.0;
        if (direction > 0.0)//在目标三角形中顺残差方向(残差方向定义为逆时针方向)
        {
            (tri + i)->residue = res;
        }
        else
        {
            (tri + i)->residue = -res;
        }
    }
    return 0;
}

int Utils::residue(vector<triangle>& triangle, vector<tri_node>& nodes, vector<tri_edge>& edges, double distance_thresh)
{
    if (triangle.size() < 1 ||
        nodes.size() < 1 ||
        edges.size() < 3
        )
    {
        fprintf(stderr, "residue(): input check failed!\n\n");
        return -1;
    }
    int num_triangle = triangle.size();
    double thresh;
    thresh = distance_thresh < 2.0 ? 2.0 : distance_thresh;
    int num_nodes = nodes.size();
    int end1, end2, end3, tmp;
    double x1, y1, x2, y2, x3, y3, direction, delta12, delta23, delta31, residue, phi1, phi2, phi3, distance1,
        distance2, distance3;
    int row1, col1, row2, col2, row3, col3;
    bool b_res = false;
    for (int i = 0; i < num_triangle; i++)
    {
        end1 = triangle[i].p1;
        end2 = triangle[i].p2;
        end3 = triangle[i].p3;
        if (end1 > end2)
        {
            tmp = end1;
            end1 = end2;
            end2 = tmp;
        }

        nodes[end1 - 1].get_pos(&row1, &col1);
        nodes[end2 - 1].get_pos(&row2, &col2);
        nodes[end3 - 1].get_pos(&row3, &col3);

        nodes[end1 - 1].get_distance(nodes[end2 - 1], &distance1);
        nodes[end2 - 1].get_distance(nodes[end3 - 1], &distance2);
        nodes[end3 - 1].get_distance(nodes[end1 - 1], &distance3);
        b_res = true;
        if ((distance1 > thresh) || (distance2 > thresh) || (distance3 > thresh)) b_res = false;

        nodes[end1 - 1].get_phase(&phi1);
        nodes[end2 - 1].get_phase(&phi2);
        nodes[end3 - 1].get_phase(&phi3);

        x2 = double(col2 - col1);
        y2 = -double(row1 - row2);
        x1 = double(col1 - col3);
        y1 = -double(row3 - row1);
        direction = x1 * y2 - x2 * y1;

        delta12 = atan2(sin(phi2 - phi1), cos(phi2 - phi1));
        delta23 = atan2(sin(phi3 - phi2), cos(phi3 - phi2));
        delta31 = atan2(sin(phi1 - phi3), cos(phi1 - phi3));

        double res = (delta12 + delta23 + delta31) / 2.0 / PI;
        if (fabs(res) > 0.7 && !b_res)//标注边长超过阈值的残差边和残差节点
        {
            edges[triangle[i].edge1 - 1].isResidueEdge = true;
            edges[triangle[i].edge2 - 1].isResidueEdge = true;
            edges[triangle[i].edge3 - 1].isResidueEdge = true;

            nodes[triangle[i].p1 - 1].set_residue(true);
            nodes[triangle[i].p2 - 1].set_residue(true);
            nodes[triangle[i].p3 - 1].set_residue(true);
        }
        res = b_res ? res : 0.0;
        if (direction < 0.0)//在目标三角形中顺残差方向(残差方向定义为逆时针方向)
        {
            triangle[i].residue = res;
        }
        else
        {
            triangle[i].residue = -res;
        }
    }
    return 0;
}

int Utils::gen_mask(Mat& coherence, Mat& mask, int wnd_size, double thresh)
{
    if (coherence.rows < 2 ||
        coherence.cols < 2 ||
        coherence.channels() != 1 ||
        coherence.type() != CV_64F ||
        wnd_size < 0 ||
        wnd_size > coherence.rows ||
        thresh < 0.0 ||
        thresh > 1.0
        )
    {
        fprintf(stderr, "gen_mask(): input check failed!\n\n");
        return -1;
    }
    int nr = coherence.rows;
    int nc = coherence.cols;
    Mat temp_coh;
    coherence.copyTo(temp_coh);
    int radius = (wnd_size + 1) / 2;
    cv::copyMakeBorder(temp_coh, temp_coh, radius, radius, radius, radius, cv::BORDER_REFLECT);
    Mat tmp = Mat::zeros(nr, nc, CV_32S);
    tmp.copyTo(mask);
#pragma omp parallel for schedule(guided)
    for (int i = 0; i < nr; i++)
    {
        double mean;
        for (int j = 0; j < nc; j++)
        {
            mean = cv::mean(temp_coh(cv::Range(i, i + 2 * radius + 1), cv::Range(j, j + 2 * radius + 1)))[0];
            if (mean > thresh && coherence.at<double>(i, j) > thresh) mask.at<int>(i, j) = 1;
        }
    }
    return 0;
}

int Utils::real_coherence(ComplexMat& Mast, ComplexMat& Slave, Mat& coherence)
{
    int wa = 3;  //窗口方位向尺寸
    int wr = 3;  //窗口距离向尺寸

    int na = Mast.GetRows();
    int nr = Mast.GetCols();
    if ((na < 3) ||
        (nr < 3) ||
        Mast.re.type() != CV_64F ||
        Slave.re.type() != CV_64F ||
        Mast.GetCols() != Slave.GetCols() ||
        Mast.GetRows() != Slave.GetRows())
    {
        fprintf(stderr, "real_coherence(): input check failed!\n\n");
        return -1;
    }

    int win_a = (wa - 1) / 2; //方位窗半径
    int win_r = (wr - 1) / 2; //距离窗半径

    int na_new = na - 2 * win_a;
    int nr_new = nr - 2 * win_r;

    Mat Coherence(na_new, nr_new, CV_64F, Scalar::all(0));


#pragma omp parallel for schedule(guided)
    for (int i = win_a + 1; i <= na - win_a; i++)
    {
        for (int j = win_r + 1; j <= nr - win_r; j++)
        {
            Mat s1, s2, sum1, sum2;

            double up, down;
            magnitude(Mast.re(Range(i - 1 - win_a, i + win_a), Range(j - 1 - win_r, j + win_r)), Mast.im(Range(i - 1 - win_a, i + win_a), Range(j - 1 - win_r, j + win_r)), s1);
            magnitude(Slave.re(Range(i - 1 - win_a, i + win_a), Range(j - 1 - win_r, j + win_r)), Slave.im(Range(i - 1 - win_a, i + win_a), Range(j - 1 - win_r, j + win_r)), s2);
            up = sum((s1.mul(s1)).mul(s2.mul(s2)))[0];
            pow(s1, 4, s1);
            pow(s2, 4, s2);
            down = sqrt(sum(s1)[0] * sum(s2)[0]);
            if (up / (down + 1e-12) > 1.0)
            {
                Coherence.at<double>(i - 1 - win_a, j - 1 - win_r) = 1;
            }
            else
            {
                Coherence.at<double>(i - 1 - win_a, j - 1 - win_r) = up / (down + 1e-12);
            }

        }
    }
    copyMakeBorder(Coherence, Coherence, 1, 1, 1, 1, BORDER_REFLECT);
    coherence = Coherence;
    return 0;
}

int Utils::real_coherence(const ComplexMat& master_image, const ComplexMat& slave_image, int est_wndsize_rg, int est_wndsize_az, Mat& coherence)
{


    int na = master_image.GetRows();
    int nr = master_image.GetCols();
    if ((na < est_wndsize_az) ||
        (nr < est_wndsize_rg) ||
        master_image.type() != CV_64F ||
        slave_image.type() != CV_64F ||
        master_image.GetCols() != slave_image.GetCols() ||
        master_image.GetRows() != slave_image.GetRows() ||
        est_wndsize_rg % 2 == 0 ||
        est_wndsize_az % 2 == 0 ||
        est_wndsize_rg < 3 ||
        est_wndsize_az < 3
        )
    {
        fprintf(stderr, "real_coherence(): input check failed!\n\n");
        return -1;
    }

    int win_a = (est_wndsize_az - 1) / 2; //方位窗半径
    int win_r = (est_wndsize_rg - 1) / 2; //距离窗半径

    int na_new = na - 2 * win_a;
    int nr_new = nr - 2 * win_r;

    Mat Coherence(na_new, nr_new, CV_64F, Scalar::all(0));


#pragma omp parallel for schedule(guided)
    for (int i = win_a + 1; i <= na - win_a; i++)
    {
        for (int j = win_r + 1; j <= nr - win_r; j++)
        {
            Mat s1, s2, sum1, sum2;

            double up, down;
            magnitude(master_image.re(Range(i - 1 - win_a, i + win_a), Range(j - 1 - win_r, j + win_r)), master_image.im(Range(i - 1 - win_a, i + win_a), Range(j - 1 - win_r, j + win_r)), s1);
            magnitude(slave_image.re(Range(i - 1 - win_a, i + win_a), Range(j - 1 - win_r, j + win_r)), slave_image.im(Range(i - 1 - win_a, i + win_a), Range(j - 1 - win_r, j + win_r)), s2);
            up = sum((s1.mul(s1)).mul(s2.mul(s2)))[0];
            pow(s1, 4, s1);
            pow(s2, 4, s2);
            down = sqrt(sum(s1)[0] * sum(s2)[0]);
            if (up / (down + 1e-12) > 1.0)
            {
                Coherence.at<double>(i - 1 - win_a, j - 1 - win_r) = 1;
            }
            else
            {
                Coherence.at<double>(i - 1 - win_a, j - 1 - win_r) = up / (down + 1e-12);
            }

        }
    }
    copyMakeBorder(Coherence, Coherence, win_a, win_a, win_r, win_r, BORDER_REFLECT);
    Coherence.copyTo(coherence);
    return 0;
}

int Utils::complex_coherence(ComplexMat& Mast, ComplexMat& Slave, Mat& coherence)
{
    int wa = 3;  //窗口方位向尺寸
    int wr = 3;  //窗口距离向尺寸

    int na = Mast.GetRows();
    int nr = Mast.GetCols();

    if ((na < 3) ||
        (nr < 3) ||
        Mast.re.type() != CV_64F ||
        Slave.re.type() != CV_64F ||
        Mast.GetCols() != Slave.GetCols() ||
        Mast.GetRows() != Slave.GetRows())
    {
        fprintf(stderr, "complex_coherence(): input check failed!\n\n");
        return -1;
    }

    int win_a = (wa - 1) / 2; //方位窗半径
    int win_r = (wr - 1) / 2; //距离窗半径

    int na_new = na - 2 * win_a;
    int nr_new = nr - 2 * win_r;

    Mat Coherence(na_new, nr_new, CV_64F, Scalar::all(0));
#pragma omp parallel for schedule(guided)
    for (int i = win_a + 1; i <= na - win_a; i++)
    {
        for (int j = win_r + 1; j <= nr - win_r; j++)
        {
            Mat planes_master[] = { Mat::zeros(2 * win_a + 1, 2 * win_r + 1, CV_64F), Mat::zeros(2 * win_a + 1, 2 * win_r + 1, CV_64F) };
            Mat planes_slave[] = { Mat::zeros(2 * win_a + 1, 2 * win_r + 1, CV_64F), Mat::zeros(2 * win_a + 1, 2 * win_r + 1, CV_64F) };
            Mat planes[] = { Mat::zeros(2 * win_a + 1, 2 * win_r + 1, CV_64F), Mat::zeros(2 * win_a + 1, 2 * win_r + 1, CV_64F) };
            Mat s1, s2;
            double up, down, sum1, sum2;
            Mast.re(Range(i - 1 - win_a, i + win_a), Range(j - 1 - win_r, j + win_r)).copyTo(planes_master[0]);
            Mast.im(Range(i - 1 - win_a, i + win_a), Range(j - 1 - win_r, j + win_r)).copyTo(planes_master[1]);

            Slave.re(Range(i - 1 - win_a, i + win_a), Range(j - 1 - win_r, j + win_r)).copyTo(planes_slave[0]);
            Slave.im(Range(i - 1 - win_a, i + win_a), Range(j - 1 - win_r, j + win_r)).copyTo(planes_slave[1]);

            merge(planes_master, 2, s1);
            merge(planes_slave, 2, s2);
            mulSpectrums(s1, s2, s1, 0, true);
            split(s1, planes);
            sum1 = sum(planes[0])[0];
            sum2 = sum(planes[1])[0];
            up = sqrt(sum1 * sum1 + sum2 * sum2);
            magnitude(planes_master[0], planes_master[1], planes_master[0]);
            magnitude(planes_slave[0], planes_slave[1], planes_slave[0]);
            sum1 = sum(planes_master[0].mul(planes_master[0]))[0];
            sum2 = sum(planes_slave[0].mul(planes_slave[0]))[0];
            down = sqrt(sum1 * sum2);
            Coherence.at<double>(i - 1 - win_a, j - 1 - win_r) = up / (down + 0.0000001);
        }
    }
    copyMakeBorder(Coherence, Coherence, 1, 1, 1, 1, BORDER_REFLECT);
    coherence = Coherence;
    return 0;
}

int Utils::complex_coherence(
    const ComplexMat& master_image,
    const ComplexMat& slave_image,
    int est_wndsize_rg,
    int est_wndsize_az,
    Mat& coherence
)
{
    int na = master_image.GetRows();
    int nr = master_image.GetCols();

    if ((na < est_wndsize_az) ||
        (nr < est_wndsize_rg) ||
        master_image.type() != CV_64F ||
        slave_image.type() != CV_64F ||
        master_image.GetCols() != slave_image.GetCols() ||
        master_image.GetRows() != slave_image.GetRows() ||
        est_wndsize_az % 2 == 0 ||
        est_wndsize_rg % 2 == 0 ||
        est_wndsize_rg < 3 ||
        est_wndsize_az < 3
        )
    {
        fprintf(stderr, "complex_coherence(): input check failed!\n\n");
        return -1;
    }

    int win_a = (est_wndsize_az - 1) / 2; //方位窗半径
    int win_r = (est_wndsize_rg - 1) / 2; //距离窗半径

    int na_new = na - 2 * win_a;
    int nr_new = nr - 2 * win_r;

    Mat Coherence(na_new, nr_new, CV_64F, Scalar::all(0));
#pragma omp parallel for schedule(guided)
    for (int i = win_a + 1; i <= na - win_a; i++)
    {
        for (int j = win_r + 1; j <= nr - win_r; j++)
        {
            Mat planes_master[] = { Mat::zeros(2 * win_a + 1, 2 * win_r + 1, CV_64F), Mat::zeros(2 * win_a + 1, 2 * win_r + 1, CV_64F) };
            Mat planes_slave[] = { Mat::zeros(2 * win_a + 1, 2 * win_r + 1, CV_64F), Mat::zeros(2 * win_a + 1, 2 * win_r + 1, CV_64F) };
            Mat planes[] = { Mat::zeros(2 * win_a + 1, 2 * win_r + 1, CV_64F), Mat::zeros(2 * win_a + 1, 2 * win_r + 1, CV_64F) };
            Mat s1, s2;
            double up, down, sum1, sum2;
            master_image.re(Range(i - 1 - win_a, i + win_a), Range(j - 1 - win_r, j + win_r)).copyTo(planes_master[0]);
            master_image.im(Range(i - 1 - win_a, i + win_a), Range(j - 1 - win_r, j + win_r)).copyTo(planes_master[1]);

            slave_image.re(Range(i - 1 - win_a, i + win_a), Range(j - 1 - win_r, j + win_r)).copyTo(planes_slave[0]);
            slave_image.im(Range(i - 1 - win_a, i + win_a), Range(j - 1 - win_r, j + win_r)).copyTo(planes_slave[1]);

            merge(planes_master, 2, s1);
            merge(planes_slave, 2, s2);
            mulSpectrums(s1, s2, s1, 0, true);
            split(s1, planes);
            sum1 = sum(planes[0])[0];
            sum2 = sum(planes[1])[0];
            up = sqrt(sum1 * sum1 + sum2 * sum2);
            magnitude(planes_master[0], planes_master[1], planes_master[0]);
            magnitude(planes_slave[0], planes_slave[1], planes_slave[0]);
            sum1 = sum(planes_master[0].mul(planes_master[0]))[0];
            sum2 = sum(planes_slave[0].mul(planes_slave[0]))[0];
            down = sqrt(sum1 * sum2);
            Coherence.at<double>(i - 1 - win_a, j - 1 - win_r) = up / (down + 0.0000001);
        }
    }
    copyMakeBorder(Coherence, Coherence, win_a, win_a, win_r, win_r, BORDER_REFLECT);
    Coherence.copyTo(coherence);
    return 0;
}

int Utils::phase_coherence(Mat& phase, Mat& coherence)
{
    if (phase.rows < 3 ||
        phase.cols < 3 ||
        phase.type() != CV_64F ||
        phase.channels() != 1)
    {
        fprintf(stderr, "phase_coherence(): input check failed!\n\n");
        return -1;
    }
    ComplexMat master, slave;
    Mat cos, sin;
    int ret;
    ret = this->phase2cos(phase, cos, sin);
    if (return_check(ret, "phase2cos(*, *, *)", error_head)) return -1;
    master.SetRe(cos);
    slave.SetRe(cos);
    master.SetIm(sin);
    sin = -sin;
    slave.SetIm(sin);
    ret = this->complex_coherence(master, slave, coherence);
    if (return_check(ret, "complex_coherence(*, *, *)", error_head)) return -1;
    return 0;
}

int Utils::phase_coherence(const Mat& phase, int est_wndsize_rg, int est_wndsize_az, Mat& coherence)
{
    if (phase.rows < 3 ||
        phase.cols < 3 ||
        phase.type() != CV_64F ||
        phase.channels() != 1 ||
        est_wndsize_rg % 2 == 0 ||
        est_wndsize_az % 2 == 0
        )
    {
        fprintf(stderr, "phase_coherence(): input check failed!\n\n");
        return -1;
    }
    ComplexMat master, slave;
    Mat cos, sin;
    int ret;
    ret = phase2cos(phase, cos, sin);
    if (return_check(ret, "phase2cos(*, *, *)", error_head)) return -1;
    master.SetRe(cos);
    slave.SetRe(cos);
    master.SetIm(sin);
    sin = -sin;
    slave.SetIm(sin);
    ret = complex_coherence(master, slave, est_wndsize_rg, est_wndsize_az, coherence);
    if (return_check(ret, "complex_coherence(*, *, *)", error_head)) return -1;
    return 0;
}

int Utils::phase_derivatives_variance(Mat& phase, Mat& phase_derivatives_variance, int wndsize)
{
    if (phase.cols < 2 ||
        phase.rows < 2 ||
        phase.type() != CV_64F ||
        phase.channels() != 1 ||
        wndsize < 3 ||
        wndsize % 2 == 0
        )
    {
        fprintf(stderr, "phase_derivatives_variance(): input check failed!\n\n");
        return -1;
    }

    int nr = phase.rows;
    int nc = phase.cols;
    if (wndsize > int(nr / 10) || wndsize > int(nc / 10)) wndsize = 3;
    wndsize = (wndsize - 1) / 2;
    int ret;
    phase.copyTo(phase_derivatives_variance);
    Mat derivative_row, derivative_col;
    derivative_row = phase(Range(1, nr), Range(0, nc)) - phase(Range(0, nr - 1), Range(0, nc));
    derivative_col = phase(Range(0, nr), Range(1, nc)) - phase(Range(0, nr), Range(0, nc - 1));
    ret = wrap(derivative_row, derivative_row);
    if (return_check(ret, "wrap(*, *)", error_head)) return -1;
    ret = wrap(derivative_col, derivative_col);
    if (return_check(ret, "wrap(*, *)", error_head)) return -1;
    copyMakeBorder(derivative_col, derivative_col, 0, 1, 0, 1, BORDER_DEFAULT);
    copyMakeBorder(derivative_row, derivative_row, 0, 1, 0, 1, BORDER_DEFAULT);
    copyMakeBorder(derivative_col, derivative_col, wndsize, wndsize, wndsize, wndsize, BORDER_DEFAULT);
    copyMakeBorder(derivative_row, derivative_row, wndsize, wndsize, wndsize, wndsize, BORDER_DEFAULT);
    //cv::meanStdDev()
#pragma omp parallel for schedule(guided)
    for (int i = 0; i < nr; i++)
    {
        double std1, std2, mean1, mean2;
        Mat tmp1, tmp2;
        for (int j = 0; j < nc; j++)
        {
            mean1 = cv::mean(derivative_row(Range(i, i + 2 * wndsize + 1), Range(j, j + 2 * wndsize + 1)))[0];
            mean2 = cv::mean(derivative_col(Range(i, i + 2 * wndsize + 1), Range(j, j + 2 * wndsize + 1)))[0];
            tmp1 = derivative_row(Range(i, i + 2 * wndsize + 1), Range(j, j + 2 * wndsize + 1)) - mean1;
            tmp2 = derivative_col(Range(i, i + 2 * wndsize + 1), Range(j, j + 2 * wndsize + 1)) - mean2;
            tmp1 = tmp1.mul(tmp1);
            tmp2 = tmp2.mul(tmp2);
            std1 = sum(tmp1)[0];
            std2 = sum(tmp2)[0];
            phase_derivatives_variance.at<double>(i, j) = (sqrt(std1) + sqrt(std2)) / ((2 * wndsize + 1) * (2 * wndsize + 1));
        }
    }
    return 0;
}

int Utils::max_integrable_distance(Mat& phase, Mat& max_integrable_distance, double conservative_thresh)
{
    if (phase.rows < 5 ||
        phase.cols < 5 ||
        phase.channels() != 1 ||
        phase.type() != CV_64F ||
        conservative_thresh < 1.41
        )
    {
        fprintf(stderr, "max_integrable_distance(): input check failed!\n\n");
        return -1;
    }
    int nr = phase.rows;
    int nc = phase.cols;
    max_integrable_distance = Mat::ones(nr, nc, CV_64F);
    max_integrable_distance = max_integrable_distance * 1.5;
#pragma omp parallel for schedule(guided)
    for (int i = 1; i < nr - 1; i++)
    {
        for (int j = 1; j < nc - 1; j++)
        {
            double max = -1.0;
            double delta, ph0;
            ph0 = phase.at<double>(i, j);

            delta = phase.at<double>(i - 1, j - 1) - ph0;
            delta = fabs(atan2(sin(delta), cos(delta))) / 1.414;
            max = max > delta ? max : delta;

            delta = phase.at<double>(i - 1, j) - ph0;
            delta = fabs(atan2(sin(delta), cos(delta))) / 1.0;
            max = max > delta ? max : delta;

            delta = phase.at<double>(i - 1, j + 1) - ph0;
            delta = fabs(atan2(sin(delta), cos(delta))) / 1.414;
            max = max > delta ? max : delta;

            delta = phase.at<double>(i, j - 1) - ph0;
            delta = fabs(atan2(sin(delta), cos(delta))) / 1.0;
            max = max > delta ? max : delta;

            delta = phase.at<double>(i, j + 1) - ph0;
            delta = fabs(atan2(sin(delta), cos(delta))) / 1.0;
            max = max > delta ? max : delta;

            delta = phase.at<double>(i + 1, j - 1) - ph0;
            delta = fabs(atan2(sin(delta), cos(delta))) / 1.414;
            max = max > delta ? max : delta;

            delta = phase.at<double>(i + 1, j) - ph0;
            delta = fabs(atan2(sin(delta), cos(delta))) / 1.0;
            max = max > delta ? max : delta;

            delta = phase.at<double>(i + 1, j + 1) - ph0;
            delta = fabs(atan2(sin(delta), cos(delta))) / 1.414;
            max = max > delta ? max : delta;

            if (max < 1.0 / conservative_thresh * PI)
            {
                max = 1.0 / conservative_thresh * PI;
            }
            max_integrable_distance.at<double>(i, j) = PI / max < 1.5 ? 1.5 : PI / max;
        }
    }

    return 0;
}

int Utils::fftshift(Mat& mag)
{
    // rearrange the quadrants of Fourier image
    // so that the origin is at the image center
    if (mag.rows < 2 ||
        mag.cols < 2 ||
        mag.channels() != 1)
    {
        fprintf(stderr, "fftshift(): input check failed!\n\n");
        return -1;
    }
    mag = mag(Rect(0, 0, mag.cols & -2, mag.rows & -2));
    int cx = mag.cols / 2;
    int cy = mag.rows / 2;
    Mat tmp;
    Mat q0(mag, Rect(0, 0, cx, cy));
    Mat q1(mag, Rect(cx, 0, cx, cy));
    Mat q2(mag, Rect(0, cy, cx, cy));
    Mat q3(mag, Rect(cx, cy, cx, cy));

    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);

    q1.copyTo(tmp);
    q2.copyTo(q1);
    tmp.copyTo(q2);
    return 0;
}

int Utils::read_DIMACS(const char* DIMACS_file_solution, tri_edge* edges, int num_edges, vector<tri_node>& nodes, triangle* tri, int num_triangle)
{
    if (DIMACS_file_solution == NULL ||
        edges == NULL ||
        num_edges < 3 ||
        nodes.size() < 3 ||
        tri == NULL ||
        num_triangle < 1
        )
    {
        fprintf(stderr, "read_DIMACS(): input check failed!\n\n");
        return -1;
    }
    FILE* fp = NULL;
    fp = fopen(DIMACS_file_solution, "rt");
    if (fp == NULL)
    {
        fprintf(stderr, "read_DIMACS(): can't open %s \n", DIMACS_file_solution);
        return -1;
    }

    char instring[256];
    char ch;
    double obj_value = 0;
    int i, tmp, end1, end2, end3, row1, col1, row2, col2, row3, col3;
    int end[3];
    double x1, y1, x2, y2, direction;
    long from, to;
    double flow = 0;
    bool flag;
    int x[3];
    int y[3];
    long* ptr_neigh = NULL;
    int num_neigh, target_edges;
    int num_nodes = nodes.size();
    /////////////////////读取注释///////////////////////////
    GET_NEXT_LINE;
    while (ch != 's' && ch)
    {
        if (ch != 'c')
        {
            if (fp) fclose(fp);
            fprintf(stderr, "read_DIMACS(): unknown file format!\n\n");
            return -1;
        }
        GET_NEXT_LINE;
    }
    /////////////////////读取优化目标值/////////////////////
    for (i = 1; i < 81; i++)
    {
        if (isspace((int)instring[i]) > 0)
        {
            i++;
            break;
        }
    }
    if (sscanf(&(instring[i]), "%lf", &obj_value) != 1)
    {
        if (fp) fclose(fp);
        fprintf(stderr, "read_DIMACS(): unknown file format!\n\n");
        return -1;
    }
    if (obj_value < 0.0)
    {
        if (fp) fclose(fp);
        fprintf(stderr, "read_DIMACS(): this problem can't be solved(unbounded or infeasible)!\n\n");
        return -1;
    }
    ////////////////////////读取MCF结果////////////////////////
    GET_NEXT_LINE;
    while (ch && ch == 'f')
    {
        if (sscanf(&(instring[2]), "%ld %ld %lf", &from, &to, &flow) != 3 ||
            flow < 0.0 || from < 0 || to < 0)
        {
            if (fp) fclose(fp);
            fprintf(stderr, "read_DIMACS(): unknown file format!\n\n");
            return -1;
        }

        if (from > 0 &&
            from <= num_triangle &&
            to > 0 &&
            to <= num_triangle)
        {
            if (/*from > 0 &&
                from <= num_triangle &&
                to > 0 &&
                to <= num_triangle &&*/
                (tri + from - 1) != NULL &&
                (tri + to - 1) != NULL
                )
            {
                end[0] = -1;
                end[1] = -1;
                x[0] = (tri + from - 1)->p1;
                x[1] = (tri + from - 1)->p2;
                x[2] = (tri + from - 1)->p3;
                y[0] = (tri + to - 1)->p1;
                y[1] = (tri + to - 1)->p2;
                y[2] = (tri + to - 1)->p3;
                i = 0; tmp = 0; flag = false;
                while (end[0] == -1 || end[1] == -1)
                {
                    if (i > 2)
                    {
                        if (fp) fclose(fp);
                        fprintf(stderr, "read_DIMACS(): illegal Delaunay triangle!\n\n");
                        return -1;
                    }
                    for (int j = 0; j < 3; j++)
                    {
                        if (x[i] == y[j])
                        {
                            end[tmp] = x[i];
                            tmp++;
                            if (i == 0) flag = true;
                            break;
                        }
                    }
                    i++;

                }
            }
            if (i == 3)
            {
                if (flag) end[2] = x[1];
                else
                {
                    end[2] = x[0];
                }
            }
            else
            {
                end[2] = x[2];
            }
            if (end[0] > end[1])
            {
                end1 = end[1];
                end2 = end[0];
            }
            else
            {
                end1 = end[0];
                end2 = end[1];
            }
            end3 = end[2];




            if (end1 > 0 && end1 <= num_nodes && end2 > 0 && end2 <= num_nodes && end3 > 0 && end3 <= num_nodes)
            {
                //找到边序号target_edges
                nodes[end1 - 1].get_neigh_ptr(&ptr_neigh, &num_neigh);
                for (i = 0; i < num_neigh; i++)
                {
                    if ((ptr_neigh + i) != NULL && *(ptr_neigh + i) > 0 && *(ptr_neigh + i) <= num_edges)
                    {
                        if ((edges + *(ptr_neigh + i) - 1)->end1 == end2 || (edges + *(ptr_neigh + i) - 1)->end2 == end2)
                        {
                            target_edges = *(ptr_neigh + i);
                        }
                    }
                }

                nodes[end1 - 1].get_pos(&row1, &col1);
                nodes[end2 - 1].get_pos(&row2, &col2);
                nodes[end3 - 1].get_pos(&row3, &col3);
                x1 = double(col1 - col3);
                y1 = double(row3 - row1);
                x2 = double(col2 - col1);
                y2 = double(row1 - row2);
                direction = x1 * y2 - x2 * y1;
                if (direction > 0.0)//在目标三角形中顺残差方向
                {
                    (edges + target_edges - 1)->gain = flow;
                }
                else
                {
                    (edges + target_edges - 1)->gain = -flow;
                }
            }
        }
        GET_NEXT_LINE;
    }

    if (fp)
    {
        fclose(fp);
        fp = NULL;
    }
    return 0;
}

int Utils::read_DIMACS(
    const char* DIMACS_file_solution,
    vector<tri_edge>& edges,
    vector<tri_node>& nodes,
    vector<triangle>& triangle
)
{
    if (DIMACS_file_solution == NULL ||
        edges.size() < 3 ||
        nodes.size() < 3 ||
        triangle.size() < 1
        )
    {
        fprintf(stderr, "read_DIMACS(): input check failed!\n\n");
        return -1;
    }
    FILE* fp = NULL;
    fp = fopen(DIMACS_file_solution, "rt");
    if (fp == NULL)
    {
        fprintf(stderr, "read_DIMACS(): can't open %s \n", DIMACS_file_solution);
        return -1;
    }

    char instring[256];
    char ch;
    double obj_value = 0;
    int i, tmp, end1, end2, end3, row1, col1, row2, col2, row3, col3;
    int end[3];
    double x1, y1, x2, y2, direction;
    long from, to;
    double flow = 0;
    bool flag;
    int x[3];
    int y[3];
    long* ptr_neigh = NULL;
    int num_neigh, target_edges;
    int num_nodes = nodes.size();
    int num_triangle = triangle.size(); int num_edges = edges.size();
    /////////////////////读取注释///////////////////////////
    GET_NEXT_LINE;
    while (ch != 's' && ch)
    {
        if (ch != 'c')
        {
            if (fp) fclose(fp);
            fprintf(stderr, "read_DIMACS(): unknown file format!\n\n");
            return -1;
        }
        GET_NEXT_LINE;
    }
    /////////////////////读取优化目标值/////////////////////
    for (i = 1; i < 81; i++)
    {
        if (isspace((int)instring[i]) > 0)
        {
            i++;
            break;
        }
    }
    if (sscanf(&(instring[i]), "%lf", &obj_value) != 1)
    {
        if (fp) fclose(fp);
        fprintf(stderr, "read_DIMACS(): unknown file format!\n\n");
        return -1;
    }
    if (obj_value < 0.0)
    {
        if (fp) fclose(fp);
        fprintf(stderr, "read_DIMACS(): this problem can't be solved(unbounded or infeasible)!\n\n");
        return -1;
    }
    ////////////////////////读取MCF结果////////////////////////
    GET_NEXT_LINE;
    while (ch && ch == 'f')
    {
        if (sscanf(&(instring[2]), "%ld %ld %lf", &from, &to, &flow) != 3 ||
            flow < 0.0 || from < 0 || to < 0)
        {
            if (fp) fclose(fp);
            fprintf(stderr, "read_DIMACS(): unknown file format!\n\n");
            return -1;
        }
        //非接地边
        if (from > 0 &&
            from <= num_triangle &&
            to > 0 &&
            to <= num_triangle)
        {
            /////////寻找两个三角形的公共边//////////////
            {
                end[0] = -1;
                end[1] = -1;
                x[0] = triangle[from - 1].p1;
                x[1] = triangle[from - 1].p2;
                x[2] = triangle[from - 1].p3;
                y[0] = triangle[to - 1].p1;
                y[1] = triangle[to - 1].p2;
                y[2] = triangle[to - 1].p3;
                i = 0; tmp = 0; flag = false;
                while (end[0] == -1 || end[1] == -1)
                {
                    if (i > 2)
                    {
                        if (fp) fclose(fp);
                        fprintf(stderr, "read_DIMACS(): illegal Delaunay triangle!\n\n");
                        return -1;
                    }
                    for (int j = 0; j < 3; j++)
                    {
                        if (x[i] == y[j])
                        {
                            end[tmp] = x[i];
                            tmp++;
                            if (i == 0) flag = true;
                            break;
                        }
                    }
                    i++;

                }
            }
            if (i == 3)
            {
                if (flag) end[2] = x[1];
                else
                {
                    end[2] = x[0];
                }
            }
            else
            {
                end[2] = x[2];
            }
            if (end[0] > end[1])
            {
                end1 = end[1];
                end2 = end[0];
            }
            else
            {
                end1 = end[0];
                end2 = end[1];
            }
            end3 = end[2];
            /////////寻找两个三角形的公共边//////////////



            if (end1 > 0 && end1 <= num_nodes && end2 > 0 && end2 <= num_nodes && end3 > 0 && end3 <= num_nodes)
            {
                //找到边序号target_edges
                nodes[end1 - 1].get_neigh_ptr(&ptr_neigh, &num_neigh);
                for (i = 0; i < num_neigh; i++)
                {
                    if ((ptr_neigh + i) != NULL && *(ptr_neigh + i) > 0 && *(ptr_neigh + i) <= num_edges)
                    {
                        if (edges[*(ptr_neigh + i) - 1].end1 == end2 || edges[*(ptr_neigh + i) - 1].end2 == end2)
                        {
                            target_edges = *(ptr_neigh + i);
                        }
                    }
                }

                nodes[end1 - 1].get_pos(&row1, &col1);
                nodes[end2 - 1].get_pos(&row2, &col2);
                nodes[end3 - 1].get_pos(&row3, &col3);
                x1 = double(col1 - col3);
                y1 = -double(row3 - row1);
                x2 = double(col2 - col1);
                y2 = -double(row1 - row2);
                direction = x1 * y2 - x2 * y1;
                if (direction < 0.0)//在目标三角形中顺残差方向
                {
                    edges[target_edges - 1].gain = -flow;
                }
                else
                {
                    edges[target_edges - 1].gain = flow;
                }
            }
        }
        //接地边
        if (from == num_triangle + 1 || to == num_triangle + 1)
        {
            if (from == num_triangle + 1)
            {
                if (edges[triangle[to - 1].edge1 - 1].isBoundry)target_edges = triangle[to - 1].edge1;
                else if (edges[triangle[to - 1].edge2 - 1].isBoundry) target_edges = triangle[to - 1].edge2;
                else target_edges = triangle[to - 1].edge3;


                if (edges[target_edges - 1].end1 > edges[target_edges - 1].end2)
                {
                    end1 = edges[target_edges - 1].end2;
                    end2 = edges[target_edges - 1].end1;
                }
                else
                {
                    end1 = edges[target_edges - 1].end1;
                    end2 = edges[target_edges - 1].end2;
                }

                if (triangle[to - 1].p1 != end1 && triangle[to - 1].p1 != end2) end3 = triangle[to - 1].p1;
                else if (triangle[to - 1].p2 != end1 && triangle[to - 1].p2 != end2) end3 = triangle[to - 1].p2;
                else end3 = triangle[to - 1].p3;

                nodes[end1 - 1].get_pos(&row1, &col1);
                nodes[end2 - 1].get_pos(&row2, &col2);
                nodes[end3 - 1].get_pos(&row3, &col3);
                x1 = double(col1 - col3);
                y1 = -double(row3 - row1);
                x2 = double(col2 - col1);
                y2 = -double(row1 - row2);
                direction = x1 * y2 - x2 * y1;
                if (direction < 0.0)//在目标三角形中顺残差方向
                {
                    edges[target_edges - 1].gain = flow;
                }
                else
                {
                    edges[target_edges - 1].gain = -flow;
                }
            }
            else
            {
                if (edges[triangle[from - 1].edge1 - 1].isBoundry)target_edges = triangle[from - 1].edge1;
                else if (edges[triangle[from - 1].edge2 - 1].isBoundry) target_edges = triangle[from - 1].edge2;
                else target_edges = triangle[from - 1].edge3;


                if (edges[target_edges - 1].end1 > edges[target_edges - 1].end2)
                {
                    end1 = edges[target_edges - 1].end2;
                    end2 = edges[target_edges - 1].end1;
                }
                else
                {
                    end1 = edges[target_edges - 1].end1;
                    end2 = edges[target_edges - 1].end2;
                }

                if (triangle[from - 1].p1 != end1 && triangle[from - 1].p1 != end2) end3 = triangle[from - 1].p1;
                else if (triangle[from - 1].p2 != end1 && triangle[from - 1].p2 != end2) end3 = triangle[from - 1].p2;
                else end3 = triangle[from - 1].p3;

                nodes[end1 - 1].get_pos(&row1, &col1);
                nodes[end2 - 1].get_pos(&row2, &col2);
                nodes[end3 - 1].get_pos(&row3, &col3);
                x1 = double(col1 - col3);
                y1 = -double(row3 - row1);
                x2 = double(col2 - col1);
                y2 = -double(row1 - row2);
                direction = x1 * y2 - x2 * y1;
                if (direction < 0.0)//在目标三角形中顺残差方向
                {
                    edges[target_edges - 1].gain = -flow;
                }
                else
                {
                    edges[target_edges - 1].gain = flow;
                }
            }
        }
        GET_NEXT_LINE;
    }

    if (fp)
    {
        fclose(fp);
        fp = NULL;
    }
    return 0;
}

int Utils::cvmat2bin(const char* filename, Mat& mat)
{
    int nr = mat.rows;
    int nc = mat.cols;
    if (nr < 1 || nc < 1 || mat.type() != CV_64F || mat.channels() != 1)
    {
        fprintf(stderr, "cvmat2bin(): input check failed!\n\n");
        return -1;
    }
    FILE* fp = NULL;
    fp = fopen(filename, "wb");
    if (fp == NULL)
    {
        fprintf(stderr, "can't open file: %s\n", filename);
        return -1;
    }
    fwrite(&nr, sizeof(int), 1, fp);
    fwrite(&nc, sizeof(int), 1, fp);
    fwrite((double*)mat.data, sizeof(double), nr * nc, fp);
    if (fp != NULL) fclose(fp);
    return 0;
}

int Utils::bin2cvmat(const char* filename, Mat& dst)
{

    FILE* fp = NULL;
    fp = fopen(filename, "rb");
    if (fp == NULL)
    {
        fprintf(stderr, "can't open file: %s\n", filename);
        return -1;
    }
    int rows, cols;
    fread(&rows, sizeof(int), 1, fp);
    fread(&cols, sizeof(int), 1, fp);
    if (rows < 1 || cols < 1)
    {
        fprintf(stderr, "unknown file format!\n");
        if (fp) fclose(fp);
        return -1;
    }
    Mat matrix(rows, cols, CV_64F, cv::Scalar::all(0));
    double* p = (double*)malloc(sizeof(double) * rows * cols);
    if (p == NULL)
    {
        fprintf(stderr, "failed to allocate memory for reading data from %s!\n", filename);
        if (fp) fclose(fp);
        return -1;
    }
    fread(p, sizeof(double), rows * cols, fp);
    std::memcpy(matrix.data, p, sizeof(double) * rows * cols);
    if (p != NULL) free(p);
    if (fp != NULL) fclose(fp);
    dst = matrix;
    return 0;
}

int Utils::multilook(ComplexMat& Master, ComplexMat& Slave, Mat& phase, int multilook_times)
{
    if (Master.GetRows() != Slave.GetRows() ||
        Master.GetCols() != Slave.GetCols() ||
        Master.type() != CV_64F ||
        Slave.type() != CV_64F ||
        Master.GetRows() < 1 ||
        Master.GetCols() < 1 ||
        multilook_times < 1 ||
        Master.GetRows() < multilook_times ||
        Master.GetCols() < multilook_times)
    {
        fprintf(stderr, "multilook(): input check failed!\n\n");
        return -1;
    }
    int ret;
    if (multilook_times == 1)
    {
        ret = generate_phase(Master, Slave, phase);
        if (return_check(ret, "generate_phase(*, *, *)", error_head)) return -1;
        return 0;
    }
    ComplexMat tmp;
    ret = Master.Mul(Slave, tmp, true);
    if (return_check(ret, "Master.Mul(*, *, *)", error_head)) return -1;
    int nr = tmp.GetRows();
    int nc = tmp.GetCols();
    nr = (nr - (nr % multilook_times)) / multilook_times;
    nc = (nc - (nc % multilook_times)) / multilook_times;
    Mat real = Mat::zeros(nr, nc, CV_64F);
    Mat imag = Mat::zeros(nr, nc, CV_64F);
#pragma omp parallel for schedule(guided)
    for (int i = 0; i < nr; i++)
    {
        for (int j = 0; j < nc; j++)
        {
            real.at<double>(i, j) = cv::mean(tmp.re(Range(i * multilook_times, (i + 1) * multilook_times),
                Range(j * multilook_times, (j + 1) * multilook_times)))[0];
            imag.at<double>(i, j) = cv::mean(tmp.im(Range(i * multilook_times, (i + 1) * multilook_times),
                Range(j * multilook_times, (j + 1) * multilook_times)))[0];
        }
    }
    tmp.SetRe(real);
    tmp.SetIm(imag);
    phase = tmp.GetPhase();
    return 0;
}

int Utils::multilook(const ComplexMat& master, const ComplexMat& slave, int multilook_rg, int multilook_az, Mat& phase)
{
    if (master.GetRows() != slave.GetRows() ||
        master.GetCols() != slave.GetCols() ||
        master.type() != CV_64F ||
        slave.type() != CV_64F ||
        master.GetRows() < 1 ||
        master.GetCols() < 1 ||
        multilook_rg < 1 ||
        multilook_az < 1 ||
        master.GetRows() < multilook_az ||
        master.GetCols() < multilook_rg)
    {
        fprintf(stderr, "multilook(): input check failed!\n\n");
        return -1;
    }
    int ret;
    if (multilook_rg == 1 && multilook_az == 1)
    {
        ret = generate_phase(master, slave, phase);
        if (return_check(ret, "generate_phase(*, *, *)", error_head)) return -1;
        return 0;
    }
    ComplexMat tmp;
    ret = master.Mul(slave, tmp, true);
    if (return_check(ret, "Master.Mul(*, *, *)", error_head)) return -1;
    int nr = tmp.GetRows();
    int nc = tmp.GetCols();
    int radius_rg = multilook_rg / 2;
    int radius_az = multilook_az / 2;
    Mat real = Mat::zeros(nr, nc, CV_64F);
    Mat imag = Mat::zeros(nr, nc, CV_64F);
#pragma omp parallel for schedule(guided)
    for (int i = 0; i < nr; i++)
    {
        int left, right, bottom, top;
        for (int j = 0; j < nc; j++)
        {
            left = j - radius_rg; left = left < 0 ? 0 : left;
            right = left + multilook_rg; right = right > nc - 1 ? nc - 1 : right;
            top = i - radius_az; top = top < 0 ? 0 : top;
            bottom = top + multilook_az; bottom = bottom > nr - 1 ? nr - 1 : bottom;
            real.at<double>(i, j) = cv::mean(tmp.re(Range(top, bottom + 1), Range(left, right + 1)))[0];
            imag.at<double>(i, j) = cv::mean(tmp.im(Range(top, bottom + 1), Range(left, right + 1)))[0];
        }
    }
    tmp.SetRe(real);
    tmp.SetIm(imag);
    tmp.GetPhase().copyTo(phase);
    return 0;
}

int Utils::phase2cos(const Mat& phase, Mat& cos, Mat& sin)
{
    if (phase.rows < 1 ||
        phase.cols < 1 ||
        phase.type() != CV_64F ||
        phase.channels() != 1)
    {
        fprintf(stderr, "phase2cos(): input check failed!\n\n");
        return -1;
    }
    int nr = phase.rows;
    int nc = phase.cols;
    Mat Cos(nr, nc, CV_64F);
    Mat Sin(nr, nc, CV_64F);
#pragma omp parallel for schedule(guided)
    for (int i = 0; i < nr; i++)
    {
        for (int j = 0; j < nc; j++)
        {
            Cos.at<double>(i, j) = std::cos(phase.at<double>(i, j));
            Sin.at<double>(i, j) = std::sin(phase.at<double>(i, j));
        }
    }
    Cos.copyTo(cos);
    Sin.copyTo(sin);
    return 0;
}

int Utils::xyz2ell(Mat xyz, Mat& llh)
{
    if (xyz.rows != 1 ||
        xyz.cols != 3 ||
        xyz.type() != CV_64F ||
        xyz.channels() != 1)
    {
        fprintf(stderr, "xyz2ell(): input check failed!\n\n");
        return -1;
    }
    double x = xyz.at<double>(0, 0);
    double y = xyz.at<double>(0, 1);
    double z = xyz.at<double>(0, 2);
    double Rad_earth_e = 6378136.49;
    double f = 1 / 298.257223563;
    double t = Rad_earth_e * (1 - f);
    double e = sqrt((Rad_earth_e * Rad_earth_e - t * t) / (Rad_earth_e * Rad_earth_e));
    double r = x * x + y * y;
    if (fabs(r) < 1e-15)
    {
        r = 1e-14;
    }
    if (fabs(x) < 1e-15)
    {
        x = 1e-14;
    }
    double lat = atan(z / r);
    double lon = atan(y / x);
    double N, height;
    double tmp = 0.0;
    double pi = 3.1415926535;
    for (int k = 0; k < 10; k++)
    {
        tmp = (sqrt(1 - e * e * sin(lat) * sin(lat)));
        if (fabs(tmp) < 1e-15)
        {
            tmp = 1e-14;
        }
        N = Rad_earth_e / tmp;
        tmp = sin(lat);
        if (fabs(tmp) < 1e-15)
        {
            tmp = 1e-14;
        }
        height = z / tmp - N * (1 - e * e);
        tmp = (sqrt(x * x + y * y) * (N * (1 - e * e) + height));
        if (fabs(tmp) < 1e-15)
        {
            tmp = 1e-14;
        }
        lat = atan(z * (N + height) / tmp);
    }
    if (x > 0 && y < 0)
    {
        lon = -lon;
    }
    else if (x < 0 && y > 0)
    {
        lon = pi + lon;
    }
    else if (x < 0 && y < 0)
    {
        lon = -pi + lon;
    }
    lat = lat * 180 / pi;
    lon = lon * 180 / pi;
    llh = Mat::zeros(1, 3, CV_64F);
    llh.at<double>(0, 0) = lat;
    llh.at<double>(0, 1) = lon;
    llh.at<double>(0, 2) = height;
    return 0;
}

int Utils::ell2xyz(Mat llh, Mat& xyz)
{
    if (llh.cols != 3 ||
        llh.rows != 1 ||
        llh.type() != CV_64F ||
        llh.channels() != 1
        )
    {
        fprintf(stderr, "ell2xyz(): input check failed!\n\n");
        return -1;
    }
    double e2 = 0.00669438003551279091;
    double lat = llh.at<double>(0, 0);
    double lon = llh.at<double>(0, 1);
    double height = llh.at<double>(0, 2);
    lat = lat / 180.0 * PI;
    lon = lon / 180.0 * PI;
    double Ea = 6378136.49;
    double N = Ea / sqrt(1 - e2 * (sin(lat) * sin(lat)));
    double Nph = N + height;
    double x = Nph * cos(lat) * cos(lon);
    double y = Nph * cos(lat) * sin(lon);
    double z = (Nph - e2 * N) * sin(lat);
    Mat tmp = Mat::zeros(1, 3, CV_64F);
    tmp.at<double>(0, 0) = x;
    tmp.at<double>(0, 1) = y;
    tmp.at<double>(0, 2) = z;
    tmp.copyTo(xyz);
    return 0;
}

int Utils::saveSLC(const char* filename, double db, ComplexMat& SLC)
{
    if (filename == NULL ||
        db < 0 ||
        SLC.GetRows() < 1 ||
        SLC.GetCols() < 1 /*||
        SLC.type() != CV_64F*/)
    {
        fprintf(stderr, "saveSLC(): input check failed!\n\n");
        return -1;
    }
    ComplexMat tmp;
    Mat mod;
    if (SLC.type() != CV_64F && SLC.type() != CV_32F)
    {
        SLC.re.convertTo(tmp.re, CV_32F);
        SLC.im.convertTo(tmp.im, CV_32F);
        mod = tmp.GetMod();
    }
    else
    {
        mod = SLC.GetMod();
    }
    int nr = mod.rows;
    int nc = mod.cols;
    double max, min;
    if (SLC.type() == CV_64F)
    {
#pragma omp parallel for schedule(guided)
        for (int i = 0; i < nr; i++)
        {
            for (int j = 0; j < nc; j++)
            {
                mod.at<double>(i, j) = 20 * log10(mod.at<double>(i, j) + 0.000001);
            }
        }

        minMaxLoc(mod, &min, &max);
        if (fabs(max - min) < 0.00000001)
        {
            fprintf(stderr, "SLC image intensity is the same for every pixel\n\n");
            return -1;
        }
        min = max - db;
#pragma omp parallel for schedule(guided)
        for (int i = 0; i < nr; i++)
        {
            for (int j = 0; j < nc; j++)
            {
                if (mod.at<double>(i, j) <= min) mod.at<double>(i, j) = min;
            }
        }
    }
    else
    {
#pragma omp parallel for schedule(guided)
        for (int i = 0; i < nr; i++)
        {
            for (int j = 0; j < nc; j++)
            {
                mod.at<float>(i, j) = 20 * log10(mod.at<float>(i, j) + 0.000001);
            }
        }

        minMaxLoc(mod, &min, &max);
        if (fabs(max - min) < 0.00000001)
        {
            fprintf(stderr, "SLC image intensity is the same for every pixel\n\n");
            return -1;
        }
        min = max - db;
#pragma omp parallel for schedule(guided)
        for (int i = 0; i < nr; i++)
        {
            for (int j = 0; j < nc; j++)
            {
                if (mod.at<float>(i, j) <= min) mod.at<float>(i, j) = min;
            }
        }
    }
    mod = (mod - min) / (max - min) * 255.0;
    mod.convertTo(mod, CV_8U);
    bool ret = cv::imwrite(filename, mod);
    if (!ret)
    {
        fprintf(stderr, "cv::imwrite(): can't write to %s\n\n", filename);
        return -1;
    }
    return 0;
}

int Utils::savephase(const char* filename, const char* colormap, Mat phase)
{
    if (filename == NULL ||
        colormap == NULL ||
        phase.rows < 1 ||
        phase.cols < 1 ||
        phase.type() != CV_64F ||
        phase.channels() != 1)
    {
        fprintf(stderr, "savephase(): input check failed!\n\n");
        return -1;
    }
    bool gray = false;
    cv::ColormapTypes type = cv::COLORMAP_PARULA;
    if (strcmp(colormap, "jet") == 0) type = cv::COLORMAP_JET;
    if (strcmp(colormap, "hsv") == 0) type = cv::COLORMAP_HSV;
    if (strcmp(colormap, "cool") == 0) type = cv::COLORMAP_COOL;
    if (strcmp(colormap, "rainbow") == 0) type = cv::COLORMAP_RAINBOW;
    if (strcmp(colormap, "spring") == 0) type = cv::COLORMAP_SPRING;
    if (strcmp(colormap, "summer") == 0) type = cv::COLORMAP_SUMMER;
    if (strcmp(colormap, "winter") == 0) type = cv::COLORMAP_WINTER;
    if (strcmp(colormap, "autumn") == 0) type = cv::COLORMAP_AUTUMN;
    if (strcmp(colormap, "gray") == 0) gray = true;

    double min, max;
    Mat tmp;
    phase.copyTo(tmp);
    cv::minMaxLoc(tmp, &min, &max);
    if (fabs(max - min) < 0.000001)
    {
        fprintf(stderr, "phase value is the same for every pixel\n\n");
        return -1;
    }
    tmp = (tmp - min) / (max - min) * 255.0;
    tmp.convertTo(tmp, CV_8U);
    if (!gray)
    {
        cv::applyColorMap(tmp, tmp, type);
    }
    bool ret = cv::imwrite(filename, tmp);
    if (!ret)
    {
        fprintf(stderr, "cv::imwrite(): can't write to %s\n\n", filename);
        return -1;
    }
    return 0;
}

int Utils::resampling(const char* Src_file, const char* Dst_file, int dst_rows, int dst_cols)
{
    if (Src_file == NULL ||
        Dst_file == NULL ||
        dst_rows < 1 ||
        dst_cols < 1)
    {
        fprintf(stderr, "down_sampling(): input check failed!\n\n");
        return -1;
    }
    Mat img = cv::imread(Src_file);
    if (img.rows < 1 || img.cols < 1)
    {
        fprintf(stderr, "can't read from %s!\n\n", Dst_file);
        return -1;
    }
    cv::resize(img, img, cv::Size(dst_cols, dst_rows));
    if (cv::imwrite(Dst_file, img) == false)
    {
        fprintf(stderr, "failed to write %s\n\n", Dst_file);
        return -1;
    }
    return 0;
}

int Utils::amplitude_phase_blend(const char* amplitude_file, const char* phase_file, const char* blended_file, double SAR_ratio)
{
    if (amplitude_file == NULL ||
        phase_file == NULL ||
        blended_file == NULL
        )
    {
        fprintf(stderr, "amplitude_phase_blend(): input check failed!\n\n");
        return -1;
    }
    Mat amplitude = imread(amplitude_file);
    Mat phase = imread(phase_file);
    if (amplitude.rows < 1 ||
        amplitude.cols < 1
        )
    {
        fprintf(stderr, "amplitude_phase_blend(): can't open %s!\n\n", amplitude_file);
        return -1;
    }
    if (phase.rows < 1 ||
        phase.cols < 1
        )
    {
        fprintf(stderr, "amplitude_phase_blend(): can't open %s!\n", phase_file);
        return -1;
    }
    SAR_ratio = SAR_ratio < 0.5 ? 0.5 : SAR_ratio;
    SAR_ratio = SAR_ratio > 0.99 ? 0.99 : SAR_ratio;
    Mat blend;
    addWeighted(amplitude, SAR_ratio, phase, 1.0 - SAR_ratio, 0, blend);
    if (blend.rows < 1 || blend.cols < 1)
    {
        fprintf(stderr, "amplitude_phase_blend(): failed to blend !\n");
        return -1;
    }
    if (!imwrite(blended_file, blend))
    {
        fprintf(stderr, "amplitude_phase_blend(): failed to write to %s !\n", blended_file);
        return -1;
    }
    return 0;
}



int Utils::read_edges(const char* filename, tri_edge** edges, long* num_edges, int** neighbours, long num_nodes)
{
    if (filename == NULL ||
        num_edges == NULL ||
        num_nodes < 3 ||
        edges == NULL ||
        neighbours == NULL)
    {
        fprintf(stderr, "read_edges(): input check failed!\n\n");
        return -1;
    }
    FILE* fp = NULL;
    fp = fopen(filename, "rt");
    if (fp == NULL)
    {
        fprintf(stderr, "read_edges(): can't open %s\n", filename);
        return -1;
    }
    char str[1024];
    char* ptr;
    fgets(str, 1024, fp);
    *num_edges = strtol(str, &ptr, 0);
    if (*num_edges <= 0)
    {
        fprintf(stderr, "read_edges(): %s is unknown format!\n", filename);
        if (fp)
        {
            fclose(fp);
            fp = NULL;
        }
        return -1;
    }
    *edges = (tri_edge*)malloc(*num_edges * sizeof(tri_edge));
    if (*edges == NULL)
    {
        fprintf(stderr, "read_edges(): unreasonable number of edges, out of memory!\n");
        if (fp)
        {
            fclose(fp);
            fp = NULL;
        }
        return -1;
    }
    memset(*edges, 0, *num_edges * sizeof(tri_edge));
    *neighbours = (int*)malloc(sizeof(int) * num_nodes);
    if (*neighbours == NULL)
    {
        fprintf(stderr, "read_edges(): unreasonable number of nodes, out of memory!\n");
        if (fp)
        {
            fclose(fp);
            fp = NULL;
        }
        if (*edges)
        {
            free(*edges);
            *edges = NULL;
        }
        return -1;
    }
    memset(*neighbours, 0, sizeof(int) * num_nodes);
    long end1, end2, edges_number, boundry_marker;
    for (int i = 0; i < *num_edges; i++)
    {
        fgets(str, 1024, fp);
        edges_number = strtol(str, &ptr, 0);
        end1 = strtol(ptr, &ptr, 0);
        end2 = strtol(ptr, &ptr, 0);
        boundry_marker = strtol(ptr, &ptr, 0);
        (*edges + i)->end1 = end1;
        (*edges + i)->end2 = end2;
        (*edges + i)->num = i + 1;
        (*edges + i)->gain = 0;
        (*edges + i)->isResidueEdge = false;
        (*edges + i)->isBoundry = (boundry_marker == 1);
        if (end1 < 1 ||
            end1 > num_nodes ||
            end2 < 1 ||
            end2 > num_nodes)
        {
            fprintf(stderr, "read_edges(): endpoints exceed 1~num_nodes!\n");
            if (fp)
            {
                fclose(fp);
                fp = NULL;
            }
            if (*edges)
            {
                free(*edges);
                *edges = NULL;
            }
            if (*neighbours)
            {
                free(*neighbours);
                *neighbours = NULL;
            }
            return -1;
        }
        *(*neighbours + end1 - 1) = *(*neighbours + end1 - 1) + 1;//统计每个节点有多少邻接边
        *(*neighbours + end2 - 1) = *(*neighbours + end2 - 1) + 1;
    }
    if (fp)
    {
        fclose(fp);
        fp = NULL;
    }
    return 0;
}

int Utils::read_edges(const char* edge_file, vector<tri_edge>& edges, std::vector<int>& node_neighbours, long num_nodes)
{
    if (edge_file == NULL ||
        num_nodes < 3)
    {
        fprintf(stderr, "read_edges(): input check failed!\n\n");
        return -1;
    }
    FILE* fp = NULL;
    fp = fopen(edge_file, "rt");
    if (fp == NULL)
    {
        fprintf(stderr, "read_edges(): can't open %s\n", edge_file);
        return -1;
    }
    char str[1024];
    char* ptr;
    fgets(str, 1024, fp);
    long long num_edges = 0;
    num_edges = strtol(str, &ptr, 0);
    if (num_edges <= 0)
    {
        fprintf(stderr, "read_edges(): %s is unknown format!\n", edge_file);
        if (fp)
        {
            fclose(fp);
            fp = NULL;
        }
        return -1;
    }
    edges.clear(); node_neighbours.clear();
    edges.resize(num_edges);
    node_neighbours.resize(num_nodes);
    long end1, end2, edges_number, boundry_marker;
    for (int i = 0; i < num_edges; i++)
    {
        fgets(str, 1024, fp);
        edges_number = strtol(str, &ptr, 0);
        end1 = strtol(ptr, &ptr, 0);
        end2 = strtol(ptr, &ptr, 0);
        boundry_marker = strtol(ptr, &ptr, 0);
        edges[i].end1 = end1;
        edges[i].end2 = end2;
        edges[i].num = i + 1;
        edges[i].gain = 0;
        edges[i].isResidueEdge = false;
        edges[i].phase_diff = 0.0;
        edges[i].isBoundry = (boundry_marker == 1);
        if (end1 < 1 ||
            end1 > num_nodes ||
            end2 < 1 ||
            end2 > num_nodes)
        {
            fprintf(stderr, "read_edges(): endpoints exceed 1~num_nodes!\n");
            if (fp)
            {
                fclose(fp);
                fp = NULL;
            }
            return -1;
        }
        node_neighbours[end1 - 1] += 1;//统计每个节点有多少邻接边
        node_neighbours[end2 - 1] += 1;//统计每个节点有多少邻接边
    }
    if (fp)
    {
        fclose(fp);
        fp = NULL;
    }
    return 0;
}

int Utils::init_tri_node(vector<tri_node>& node_array, Mat& phase, Mat& mask, tri_edge* edges, long num_edges, int* num_neighbour, int num_nodes)
{
    if (phase.rows < 2 ||
        phase.cols < 2 ||
        phase.channels() != 1 ||
        phase.type() != CV_64F ||
        mask.rows != phase.rows ||
        mask.cols != phase.cols ||
        mask.channels() != 1 ||
        mask.type() != CV_32S ||
        edges == NULL ||
        num_edges < 3 ||
        num_neighbour == NULL ||
        num_nodes < 3)
    {
        fprintf(stderr, "init_tri_node(): input check failed!\n\n");
        return -1;
    }
    int sum = cv::countNonZero(mask);;
    if (sum != num_nodes)
    {
        fprintf(stderr, "init_tri_node(): mask and num_nodes mismatch!\n\n");
        return -1;
    }
    int rows = phase.rows;
    int cols = phase.cols;
    int count = 0;
    tri_node* ptr = NULL;
    double Phase;
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            if (mask.at<int>(i, j) > 0)
            {
                Phase = phase.at<double>(i, j);
                ptr = new tri_node(i, j, *(num_neighbour + count), Phase);
                if (mask.at<int>(i, j) > 1)//邻接已解缠节点标记
                {
                    ptr->set_status(true);
                }
                node_array.push_back(*ptr);
                delete ptr;
                ptr = NULL;
                count++;

            }
        }
    }

    long* neighbour_ptr = NULL;
    int dummy, ret;
    tri_edge tmp;
    for (int i = 0; i < num_edges; i++)
    {
        tmp = *(edges + i);
        if (tmp.end1 < 0 ||
            tmp.end2 < 0 ||
            tmp.end1 > node_array.size() ||
            tmp.end2 > node_array.size())
        {
            fprintf(stderr, "init_tri_node(): edges' endpoint exceed legal value!\n\n");
            return -1;
        }
        ret = node_array[tmp.end1 - 1].get_neigh_ptr(&neighbour_ptr, &dummy);
        if (return_check(ret, "tri_node::get_neigh_ptr(*, *)", error_head)) return -1;
        while (neighbour_ptr != NULL && *neighbour_ptr != -1)
        {
            neighbour_ptr = neighbour_ptr + 1;
        }
        *neighbour_ptr = i + 1;

        ret = node_array[tmp.end2 - 1].get_neigh_ptr(&neighbour_ptr, &dummy);
        if (return_check(ret, "tri_node::get_neigh_ptr(*, *)", error_head)) return -1;
        while (neighbour_ptr != NULL && *neighbour_ptr != -1)
        {
            neighbour_ptr = neighbour_ptr + 1;
        }
        *neighbour_ptr = i + 1;
    }
    return 0;
}

int Utils::init_tri_node(
    vector<tri_node>& node_array,
    const Mat& phase,
    const Mat& mask,
    const vector<tri_edge>& edges,
    const vector<int>& node_neighbours,
    int num_nodes
)
{
    if (phase.rows < 2 ||
        phase.cols < 2 ||
        phase.channels() != 1 ||
        phase.type() != CV_64F ||
        mask.rows != phase.rows ||
        mask.cols != phase.cols ||
        mask.channels() != 1 ||
        mask.type() != CV_32S ||
        edges.size() < 3 ||
        (node_neighbours.size() - num_nodes) != 0 ||
        num_nodes < 3)
    {
        fprintf(stderr, "init_tri_node(): input check failed!\n\n");
        return -1;
    }
    int sum = cv::countNonZero(mask);;
    if (sum != num_nodes)
    {
        fprintf(stderr, "init_tri_node(): mask and num_nodes mismatch!\n\n");
        return -1;
    }
    long long num_edges = edges.size();
    node_array.clear();
    node_array.resize(num_nodes);
    int rows = phase.rows;
    int cols = phase.cols;
    int count = 0;
    tri_node* ptr = NULL;
    double Phase;
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            if (mask.at<int>(i, j) > 0)
            {
                Phase = phase.at<double>(i, j);
                ptr = new tri_node(i, j, node_neighbours[count], Phase);
                if (mask.at<int>(i, j) > 1)//邻接已解缠节点标记
                {
                    ptr->set_status(true);
                }
                node_array[count] = *ptr;
                delete ptr;
                ptr = NULL;
                count++;

            }
        }
    }

    long* neighbour_ptr = NULL;
    int dummy, ret;
    tri_edge tmp;
    for (int i = 0; i < num_edges; i++)
    {
        tmp = edges[i];
        if (tmp.end1 < 0 ||
            tmp.end2 < 0 ||
            tmp.end1 > node_array.size() ||
            tmp.end2 > node_array.size())
        {
            fprintf(stderr, "init_tri_node(): edges' endpoint exceed legal value!\n\n");
            return -1;
        }
        ret = node_array[tmp.end1 - 1].get_neigh_ptr(&neighbour_ptr, &dummy);
        if (return_check(ret, "tri_node::get_neigh_ptr(*, *)", error_head)) return -1;
        while (neighbour_ptr != NULL && *neighbour_ptr != -1)
        {
            neighbour_ptr = neighbour_ptr + 1;
        }
        *neighbour_ptr = i + 1;

        ret = node_array[tmp.end2 - 1].get_neigh_ptr(&neighbour_ptr, &dummy);
        if (return_check(ret, "tri_node::get_neigh_ptr(*, *)", error_head)) return -1;
        while (neighbour_ptr != NULL && *neighbour_ptr != -1)
        {
            neighbour_ptr = neighbour_ptr + 1;
        }
        *neighbour_ptr = i + 1;
    }
    return 0;
}

int Utils::init_edge_phase_diff(vector<tri_edge>& edges, const vector<tri_node>& node_array)
{
    if (edges.size() < 3 || node_array.size() < 3)
    {
        fprintf(stderr, "init_edge_phase_diff(): input check failed!\n");
        return -1;
    }
    size_t num_nodes = node_array.size();
    size_t num_edges = edges.size();
    size_t end1, end2;
    double phi1, phi2;
    for (size_t i = 0; i < num_edges; i++)
    {
        end1 = edges[i].end1 < edges[i].end2 ? edges[i].end1 : edges[i].end2;
        end2 = edges[i].end1 < edges[i].end2 ? edges[i].end2 : edges[i].end1;
        node_array[end1 - 1].get_phase(&phi1);
        node_array[end2 - 1].get_phase(&phi2);
        phi1 = phi2 - phi1;
        phi1 = atan2(sin(phi1), cos(phi1));
        edges[i].phase_diff = phi1;
    }
    return 0;
}

int Utils::init_edges_quality(Mat& quality, tri_edge* edges, int num_edges, vector<tri_node>& nodes)
{
    if (quality.rows < 2 ||
        quality.cols < 2 ||
        quality.type() != CV_64F ||
        quality.channels() != 1 ||
        edges == NULL ||
        num_edges < 3 ||
        nodes.size() < 3
        )
    {
        fprintf(stderr, "init_edges_quality(): input check failed!\n\n");
        return -1;
    }
    int rows, cols;
    double qual = 0.0;
    for (int i = 0; i < num_edges; i++)
    {
        qual = 0.0;
        nodes[(edges + i)->end1 - 1].get_pos(&rows, &cols);
        qual += quality.at<double>(rows, cols);
        nodes[(edges + i)->end2 - 1].get_pos(&rows, &cols);
        qual += quality.at<double>(rows, cols);
        (edges + i)->quality = qual / 2.0;
    }
    return 0;
}

int Utils::init_edges_quality(const Mat& quality_map, vector<tri_edge>& edges, const vector<tri_node>& nodes)
{
    if (quality_map.rows < 2 ||
        quality_map.cols < 2 ||
        quality_map.type() != CV_64F ||
        quality_map.channels() != 1 ||
        edges.size() < 3 ||
        nodes.size() < 3
        )
    {
        fprintf(stderr, "init_edges_quality(): input check failed!\n\n");
        return -1;
    }
    int rows, cols;
    double qual = 0.0;
    size_t num_edges = edges.size();
    for (int i = 0; i < num_edges; i++)
    {
        qual = 0.0;
        nodes[edges[i].end1 - 1].get_pos(&rows, &cols);
        qual += quality_map.at<double>(rows, cols);
        nodes[edges[i].end2 - 1].get_pos(&rows, &cols);
        qual += quality_map.at<double>(rows, cols);
        edges[i].quality = qual / 2.0;
    }
    return 0;
}

int Utils::read_triangle(
    const char* ele_file,
    const char* neigh_file,
    triangle** tri,
    int* num_triangle,
    vector<tri_node>& nodes,
    tri_edge* edges,
    int num_edges
)
{
    if (ele_file == NULL ||
        neigh_file == NULL ||
        tri == NULL ||
        num_triangle == NULL ||
        nodes.size() < 3 ||
        edges == NULL ||
        num_edges < 3
        )
    {
        fprintf(stderr, "read_triangle(): input check failed!\n\n");
        return -1;
    }
    FILE* fp_ele, * fp_neigh;
    fp_ele = NULL;
    fp_neigh = NULL;
    fp_ele = fopen(ele_file, "rt");
    if (fp_ele == NULL)
    {
        fprintf(stderr, "read_triangle(): can't open %s\n", ele_file);
        return -1;
    }
    fp_neigh = fopen(neigh_file, "rt");
    if (fp_neigh == NULL)
    {
        fprintf(stderr, "read_triangle(): can't open %s\n", neigh_file);
        if (fp_ele)
        {
            fclose(fp_ele);
            fp_ele = NULL;
        }
        return -1;
    }

    char str[INPUTMAXSIZE];
    char* ptr;
    fgets(str, INPUTMAXSIZE, fp_ele);
    *num_triangle = strtol(str, &ptr, 0);
    if (*num_triangle < 1)
    {
        fprintf(stderr, "read_triangle(): number of triangles exceed legal range!\n");
        if (fp_ele)
        {
            fclose(fp_ele);
            fp_ele = NULL;
        }
        if (fp_neigh)
        {
            fclose(fp_neigh);
            fp_neigh = NULL;
        }
        return -1;
    }
    fgets(str, INPUTMAXSIZE, fp_neigh);

    *tri = (triangle*)malloc(*num_triangle * sizeof(triangle));
    if (*tri == NULL)
    {
        fprintf(stderr, "read_triangle(): out of memory!\n");
        if (fp_ele)
        {
            fclose(fp_ele);
            fp_ele = NULL;
        }
        if (fp_neigh)
        {
            fclose(fp_neigh);
            fp_neigh = NULL;
        }
        return -1;
    }
    memset(*tri, 0, sizeof(triangle) * (*num_triangle));

    int p1, p2, p3, neigh1, neigh2, neigh3, num1, num2;
    for (int i = 0; i < *num_triangle; i++)
    {
        fgets(str, INPUTMAXSIZE, fp_ele);
        num1 = strtol(str, &ptr, 0);
        p1 = strtol(ptr, &ptr, 0);
        p2 = strtol(ptr, &ptr, 0);
        p3 = strtol(ptr, &ptr, 0);

        fgets(str, INPUTMAXSIZE, fp_neigh);
        num2 = strtol(str, &ptr, 0);
        neigh1 = strtol(ptr, &ptr, 0);
        neigh2 = strtol(ptr, &ptr, 0);
        neigh3 = strtol(ptr, &ptr, 0);
        (*tri + i)->p1 = p1;
        (*tri + i)->p2 = p2;
        (*tri + i)->p3 = p3;
        (*tri + i)->neigh1 = neigh1;
        (*tri + i)->neigh2 = neigh2;
        (*tri + i)->neigh3 = neigh3;
        (*tri + i)->num = num1;
    }
    if (fp_ele)
    {
        fclose(fp_ele);
        fp_ele = NULL;
    }
    if (fp_neigh)
    {
        fclose(fp_neigh);
        fp_neigh = NULL;
    }
    //获取三角形的边序号
    long* ptr_neigh = NULL;
    int num_neigh, count;
    int edge[3];
    memset(edge, 0, sizeof(int) * 3);
    for (int j = 0; j < *num_triangle; j++)
    {
        count = 0;
        nodes[(*tri + j)->p1 - 1].get_neigh_ptr(&ptr_neigh, &num_neigh);
        for (int i = 0; i < num_neigh; i++)
        {
            if ((edges + *(ptr_neigh + i) - 1)->end1 == (*tri + j)->p2 ||
                (edges + *(ptr_neigh + i) - 1)->end1 == (*tri + j)->p3 ||
                (edges + *(ptr_neigh + i) - 1)->end2 == (*tri + j)->p2 ||
                (edges + *(ptr_neigh + i) - 1)->end2 == (*tri + j)->p3
                )
            {
                edge[count] = *(ptr_neigh + i);
                count++;
            }
        }
        nodes[(*tri + j)->p2 - 1].get_neigh_ptr(&ptr_neigh, &num_neigh);
        for (int i = 0; i < num_neigh; i++)
        {
            if ((edges + *(ptr_neigh + i) - 1)->end1 == (*tri + j)->p3 ||
                (edges + *(ptr_neigh + i) - 1)->end2 == (*tri + j)->p3)
            {
                edge[count] = *(ptr_neigh + i);
                //count++;
            }
        }
        (*tri + j)->edge1 = edge[0];
        (*tri + j)->edge2 = edge[1];
        (*tri + j)->edge3 = edge[2];
    }



    return 0;
}

int Utils::read_triangle(
    const char* ele_file,
    const char* neigh_file,
    vector<triangle>& triangle,
    vector<tri_node>& nodes,
    vector<tri_edge>& edges
)
{
    if (ele_file == NULL ||
        neigh_file == NULL ||
        nodes.size() < 3 ||
        edges.size() < 3
        )
    {
        fprintf(stderr, "read_triangle(): input check failed!\n\n");
        return -1;
    }
    FILE* fp_ele, * fp_neigh;
    fp_ele = NULL;
    fp_neigh = NULL;
    fp_ele = fopen(ele_file, "rt");
    if (fp_ele == NULL)
    {
        fprintf(stderr, "read_triangle(): can't open %s\n", ele_file);
        return -1;
    }
    fp_neigh = fopen(neigh_file, "rt");
    if (fp_neigh == NULL)
    {
        fprintf(stderr, "read_triangle(): can't open %s\n", neigh_file);
        if (fp_ele)
        {
            fclose(fp_ele);
            fp_ele = NULL;
        }
        return -1;
    }

    char str[INPUTMAXSIZE];
    char* ptr;
    fgets(str, INPUTMAXSIZE, fp_ele);
    long num_triangle = strtol(str, &ptr, 0);
    if (num_triangle < 1)
    {
        fprintf(stderr, "read_triangle(): number of triangles exceed legal range!\n");
        if (fp_ele)
        {
            fclose(fp_ele);
            fp_ele = NULL;
        }
        if (fp_neigh)
        {
            fclose(fp_neigh);
            fp_neigh = NULL;
        }
        return -1;
    }
    fgets(str, INPUTMAXSIZE, fp_neigh);
    triangle.clear();
    triangle.resize(num_triangle);

    int p1, p2, p3, neigh1, neigh2, neigh3, num1, num2;
    for (int i = 0; i < num_triangle; i++)
    {
        fgets(str, INPUTMAXSIZE, fp_ele);
        num1 = strtol(str, &ptr, 0);
        p1 = strtol(ptr, &ptr, 0);
        p2 = strtol(ptr, &ptr, 0);
        p3 = strtol(ptr, &ptr, 0);

        fgets(str, INPUTMAXSIZE, fp_neigh);
        num2 = strtol(str, &ptr, 0);
        neigh1 = strtol(ptr, &ptr, 0);
        neigh2 = strtol(ptr, &ptr, 0);
        neigh3 = strtol(ptr, &ptr, 0);
        triangle[i].p1 = p1;
        triangle[i].p2 = p2;
        triangle[i].p3 = p3;
        triangle[i].neigh1 = neigh1;
        triangle[i].neigh2 = neigh2;
        triangle[i].neigh3 = neigh3;
        triangle[i].num = num1;
    }
    if (fp_ele)
    {
        fclose(fp_ele);
        fp_ele = NULL;
    }
    if (fp_neigh)
    {
        fclose(fp_neigh);
        fp_neigh = NULL;
    }
    //获取三角形的边序号
    long* ptr_neigh = NULL;
    int num_neigh, count;
    int edge[3];
    memset(edge, 0, sizeof(int) * 3);
    for (int j = 0; j < num_triangle; j++)
    {
        count = 0;
        nodes[triangle[j].p1 - 1].get_neigh_ptr(&ptr_neigh, &num_neigh);
        for (int i = 0; i < num_neigh; i++)
        {
            if ((edges[*(ptr_neigh + i) - 1].end1 == triangle[j].p2) ||
                (edges[*(ptr_neigh + i) - 1].end1 == triangle[j].p3) ||
                (edges[*(ptr_neigh + i) - 1].end2 == triangle[j].p2) ||
                (edges[*(ptr_neigh + i) - 1].end2 == triangle[j].p3)
                )
            {
                edge[count] = *(ptr_neigh + i);
                count++;
            }
        }
        nodes[triangle[j].p2 - 1].get_neigh_ptr(&ptr_neigh, &num_neigh);
        for (int i = 0; i < num_neigh; i++)
        {
            if ((edges[*(ptr_neigh + i) - 1].end1 == triangle[j].p3) ||
                (edges[*(ptr_neigh + i) - 1].end2 == triangle[j].p3))
            {
                edge[count] = *(ptr_neigh + i);
                //count++;
            }
        }
        triangle[j].edge1 = edge[0];
        triangle[j].edge2 = edge[1];
        triangle[j].edge3 = edge[2];
    }

    return 0;
}

//int Utils::gen_delaunay(const char* filename, const char* exe_path)
//{
//    if (filename == NULL ||
//        exe_path == NULL
//        )
//    {
//        fprintf(stderr, "gen_delaunay(): input check failed!\n");
//        return -1;
//    }
//    FILE* fp = NULL;
//    fp = fopen(filename, "rt");
//    if (!fp)
//    {
//        fprintf(stderr, "gen_delaunay(): can't open %s!\n", filename);
//        return -1;
//    }
//    else
//    {
//        fclose(fp);
//        fp = NULL;
//    }
//    USES_CONVERSION;
//    LPWSTR szCommandLine = new TCHAR[256];
//    wcscpy(szCommandLine, A2W(exe_path));
//    wcscat(szCommandLine, L"\\delaunay.exe -en ");
//    wcscat(szCommandLine, A2W(filename));
//
//    STARTUPINFO si;
//    PROCESS_INFORMATION p_i;
//    ZeroMemory(&si, sizeof(si));
//    si.cb = sizeof(si);
//    ZeroMemory(&p_i, sizeof(p_i));
//    si.dwFlags = STARTF_USESHOWWINDOW;
//    si.wShowWindow = FALSE;
//    BOOL bRet = ::CreateProcess(
//        NULL,           // 不在此指定可执行文件的文件名
//        szCommandLine,      // 命令行参数
//        NULL,           // 默认进程安全性
//        NULL,           // 默认线程安全性
//        FALSE,          // 指定当前进程内的句柄不可以被子进程继承
//        CREATE_NEW_CONSOLE, // 为新进程创建一个新的控制台窗口
//        NULL,           // 使用本进程的环境变量
//        NULL,           // 使用本进程的驱动器和目录
//        &si,
//        &p_i);
//    if (bRet)
//    {
//        HANDLE hd = CreateJobObjectA(NULL, "delaunay");
//        if (hd)
//        {
//            JOBOBJECT_EXTENDED_LIMIT_INFORMATION extLimitInfo;
//            extLimitInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
//            BOOL retval = SetInformationJobObject(hd, JobObjectExtendedLimitInformation, &extLimitInfo, sizeof(extLimitInfo));
//            if (retval)
//            {
//                if (p_i.hProcess)
//                {
//                    retval = AssignProcessToJobObject(hd, p_i.hProcess);
//                }
//            }
//        }
//        WaitForSingleObject(p_i.hProcess, INFINITE);
//        if (szCommandLine != NULL) delete[] szCommandLine;
//        ::CloseHandle(p_i.hThread);
//        ::CloseHandle(p_i.hProcess);
//    }
//    else
//    {
//        fprintf(stderr, "gen_triangle(): create triangle.exe process failed!\n\n");
//        if (szCommandLine != NULL) delete[] szCommandLine;
//        return -1;
//    }
//
//
//    return 0;
//}


int Utils::write_node_file(const char* filename, const Mat& mask)
{
    if (filename == NULL ||
        mask.rows < 2 ||
        mask.cols < 2 ||
        mask.channels() != 1 ||
        mask.type() != CV_32S
        )
    {
        fprintf(stderr, "write_node_file(): input check failed!\n\n");
        return -1;
    }
    FILE* fp = NULL;
    fp = fopen(filename, "wt");
    if (fp == NULL)
    {
        fprintf(stderr, "write_node_file(): can't open %s\n", filename);
        return -1;
    }
    int nonzero = cv::countNonZero(mask);
    if (nonzero <= 0)
    {
        fprintf(stderr, "write_node_file(): no node exist!\n");
        if (fp)
        {
            fclose(fp);
            fp = NULL;
        }
        return -1;
    }
    int dim, attr1, attr2, count;
    dim = 2;
    attr1 = 0;
    attr2 = 0;
    fprintf(fp, "%d %d %d %d\n", nonzero, dim, attr1, attr2);
    int rows = mask.rows;
    int cols = mask.cols;
    count = 1;
    double x, y;
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            if (mask.at<int>(i, j) > 0)
            {
                x = double(i + 1);
                y = double(j + 1);
                fprintf(fp, "%d %lf %lf\n", count, x, y);
                count++;
            }
        }
    }
    if (fp)
    {
        fclose(fp);
        fp = NULL;
    }
    return 0;
}

int Utils::PS_amp_dispersion(const vector<Mat>& amplitude, double thresh, Mat& mask)
{
    if (
        amplitude.size() < 3 ||
        thresh < 0.0
        )
    {
        fprintf(stderr, "PS_amp_dispersion(): input check failed!\n\n");
        return -1;
    }
    if (amplitude[0].rows < 1 || amplitude[0].cols < 1 || amplitude[0].channels() != 1 || amplitude[0].type() != CV_64F)
    {
        fprintf(stderr, "PS_amp_dispersion(): input check failed!\n\n");
        return -1;
    }
    int nr = amplitude[0].rows;
    int nc = amplitude[0].cols;
    int num_images = amplitude.size();
    Mat tmp1 = Mat::zeros(nr, nc, CV_64F);
    tmp1.copyTo(mask);
#pragma omp parallel for schedule(guided)
    for (int i = 0; i < nr; i++)
    {
        double mean, tmp;
        for (int j = 0; j < nc; j++)
        {
            mean = 0;
            tmp = 0;
            for (int k = 0; k < num_images; k++)
            {
                mean += amplitude[k].at<double>(i, j);
                //mean += (amplitude + k)->at<double>(i, j);
            }
            mean = mean / num_images;
            for (int m = 0; m < num_images; m++)
            {
                tmp += (amplitude[m].at<double>(i, j) - mean) * (amplitude[m].at<double>(i, j) - mean);
                //tmp += ((amplitude + m)->at<double>(i, j) - mean) * ((amplitude + m)->at<double>(i, j) - mean);
            }
            tmp = sqrt(tmp / num_images);
            if (mean < 1e-8) mean = 1e-8;
            tmp = tmp / mean;
            if (tmp <= thresh)
            {
                mask.at<double>(i, j) = 1.0;
            }

        }
    }
    return 0;
}

int Utils::butter_lowpass(int grid_size, int n_win, double low_pass_wavelength, Mat& lowpass)
{
    if (grid_size < 3 ||
        n_win < 3 ||
        low_pass_wavelength < 1
        )
    {
        fprintf(stderr, "butter_lowpass(): input check failed!\n\n");
        return -1;
    }
    double freq0 = 1 / low_pass_wavelength;
    double start = -(n_win) / grid_size / n_win / 2;
    double interval = 1 / grid_size / n_win;
    double end = (n_win - 2) / grid_size / n_win / 2;
    Mat freq_i = Mat::zeros(1, n_win, CV_64F);
    for (int i = 0; i < n_win; i++)
    {
        freq_i.at<double>(0, i) = start + i * interval;
    }
    freq_i = freq_i / freq0;
    cv::pow(freq_i, 10, freq_i);
    freq_i = freq_i + 1.0;
    freq_i = 1 / freq_i;
    Mat tmp;
    cv::transpose(freq_i, tmp);
    tmp = tmp * freq_i;
    int ret = fftshift2(tmp);
    if (return_check(ret, "fftshift2(*)", error_head)) return -1;
    tmp.copyTo(lowpass);
    //freq_i = -(n_win) / grid_size / n_win / 2:1 / grid_size / n_win : (n_win - 2) / grid_size / n_win / 2;
    //butter_i = 1. / (1 + (freq_i / freq0). ^ (2 * 5));
    //low_pass = butter_i'*butter_i;
    //    low_pass = fftshift(low_pass);
    return 0;
}

int Utils::circshift(Mat& out, const cv::Point& delta)
{
    Size sz = out.size();
    if (sz.height <= 0 ||
        sz.width <= 0
        )
    {
        fprintf(stderr, "circshift(): input check failed!\n\n");
        return -1;
    }
    if ((sz.height == 1 && sz.width == 1) ||
        (delta.x == 0 && delta.y == 0)
        )
    {
        return 0;
    }
    int x = delta.x;
    int y = delta.y;
    if (x > 0) x = x % sz.width;
    if (y > 0) y = y % sz.height;
    if (x < 0) x = x % sz.width + sz.width;
    if (y < 0) y = y % sz.height + sz.height;
    vector<Mat> planes;
    split(out, planes);
    for (int i = 0; i < planes.size(); i++)
    {
        Mat tmp0, tmp1, tmp2, tmp3;
        Mat q0(planes[i], Rect(0, 0, sz.width, sz.height - y));
        Mat q1(planes[i], Rect(0, sz.height - y, sz.width, y));
        q0.copyTo(tmp0);
        q1.copyTo(tmp1);
        tmp0.copyTo(planes[i](Rect(0, y, sz.width, sz.height - y)));
        tmp1.copyTo(planes[i](Rect(0, 0, sz.width, y)));


        Mat q2(planes[i], Rect(0, 0, sz.width - x, sz.height));
        Mat q3(planes[i], Rect(sz.width - x, 0, x, sz.height));
        q2.copyTo(tmp2);
        q3.copyTo(tmp3);
        tmp2.copyTo(planes[i](Rect(x, 0, sz.width - x, sz.height)));
        tmp3.copyTo(planes[i](Rect(0, 0, x, sz.height)));
    }
    merge(planes, out);
    return 0;
}

int Utils::fftshift2(Mat& out)
{
    Size sz = out.size();
    Point pt(0, 0);
    pt.x = (int)floor(sz.width / 2.0);
    pt.y = (int)floor(sz.height / 2.0);
    int ret = circshift(out, pt);
    if (return_check(ret, "circshift(*)", error_head)) return -1;
    return 0;
}

int Utils::ifftshift(Mat& out)
{
    Size sz = out.size();
    Point pt(0, 0);
    pt.x = (int)ceil(sz.width / 2.0);
    pt.y = (int)ceil(sz.height / 2.0);
    int ret = circshift(out, pt);
    if (return_check(ret, "circshift(*)", error_head)) return -1;
    return 0;
}

int Utils::fft2(Mat& Src, Mat& Dst)
{
    if (Src.rows < 1 ||
        Src.cols < 1 ||
        Src.channels() != 1 ||
        Src.type() != CV_64F)
    {
        fprintf(stderr, "fft2(): input check failed!\n\n");
        return -1;
    }
    Mat planes[] = { Mat_<double>(Src), Mat::zeros(Src.size(), CV_64F) };
    Mat complexImg;
    merge(planes, 2, complexImg);
    dft(complexImg, Dst, DFT_COMPLEX_OUTPUT);
    return 0;
}

int Utils::fft2(ComplexMat& src, ComplexMat& Dst)
{
    if (src.GetCols() < 1 ||
        src.GetRows() < 1 ||
        src.type() != CV_64F
        )
    {
        fprintf(stderr, "fft2(): input check failed!\n\n");
        return -1;
    }
    Mat re, im;
    src.GetIm().copyTo(im);
    src.GetRe().copyTo(re);
    Mat planes[] = { re, im };
    Mat complexImg;
    merge(planes, 2, complexImg);
    dft(complexImg, complexImg, DFT_COMPLEX_INPUT);
    split(complexImg, planes);
    Dst.SetRe(planes[0]);
    Dst.SetIm(planes[1]);
    return 0;
}

int Utils::ifft2(ComplexMat& src, ComplexMat& dst)
{
    if (src.GetCols() < 1 ||
        src.GetRows() < 1 ||
        src.type() != CV_64F
        )
    {
        fprintf(stderr, "ifft2(): input check failed!\n\n");
        return -1;
    }
    Mat re, im;
    im = src.GetIm();
    re = src.GetRe();
    Mat planes[] = { re, im };
    Mat complexImg;
    merge(planes, 2, complexImg);
    idft(complexImg, complexImg, DFT_COMPLEX_OUTPUT);
    split(complexImg, planes);
    dst.SetRe(planes[0]);
    dst.SetIm(planes[1]);
    return 0;
}

int Utils::std(const Mat& input, double* std)
{
    if (input.rows < 1 ||
        input.cols < 1 ||
        input.channels() != 1 ||
        input.type() != CV_64F ||
        std == NULL
        )
    {
        fprintf(stderr, "std(): input check failed!\n\n");
        return -1;
    }
    Mat data;
    double mean_v = cv::mean(input)[0];
    data = input - mean_v;
    data = data.mul(data);
    double sum_v = cv::sum(data)[0];
    int x = data.rows * data.cols - 1;
    if (x > 0)
    {
        *std = sqrt(sum_v / double(x));
    }
    else
    {
        *std = 0.0;
    }

    return 0;
}


int Utils::PS_deflat(
    vector<Mat>& interf_phase,
    Mat& interf_combination,
    vector<Mat>& pos,
    vector<Mat>& gcps,
    Mat& start_row,
    Mat& start_col,
    int mode,
    double lambda
)
{
    if (interf_phase.size() < 5 ||
        gcps.size() != pos.size() ||
        gcps.size() < 5 ||
        interf_combination.channels() != 1 ||
        interf_combination.type() != CV_32S ||
        interf_combination.cols != 2 ||
        start_row.rows != start_col.rows ||
        start_row.cols != start_col.cols ||
        //start_row.rows != gcps.size() ||
        start_col.cols != 1 ||
        start_col.channels() != 1 ||
        start_row.channels() != 1 ||
        start_col.type() != CV_32S ||
        start_row.type() != CV_32S ||
        mode < 1 ||
        mode > 2 ||
        lambda <= 0.0
        )
    {
        fprintf(stderr, "PS_deflat(): input check failed!\n\n");
        return -1;
    }
    int n_images = pos.size();
    int n_interf = interf_phase.size();
    if (n_interf != interf_combination.rows ||
        start_row.rows != n_images
        )
    {
        fprintf(stderr, "PS_deflat(): parameter check failed!\n\n");
        return -1;
    }
    volatile bool parallel_flag = true;
#pragma omp parallel for schedule(guided)
    for (int i = 0; i < n_interf; i++)
    {
        if (!parallel_flag) continue;
        Mat GCPS, POS1, POS2, phase;
        int row_start, col_start, master_ix, slave_ix, ret;
        master_ix = interf_combination.at<int>(i, 0);
        slave_ix = interf_combination.at<int>(i, 1);
        if (master_ix < 1 ||
            master_ix > n_images ||
            slave_ix < 1 ||
            slave_ix > n_images
            )
        {
            fprintf(stderr, "PS_deflat(): image index out of range!\n");
            parallel_flag = false;
            continue;
        }
        GCPS = gcps[master_ix - 1];
        POS1 = pos[master_ix - 1];
        POS2 = pos[slave_ix - 1];
        phase = interf_phase[i];
        row_start = start_row.at<int>(master_ix - 1, 0);
        col_start = start_col.at<int>(slave_ix - 1, 0);
        ret = _PS_deflat(phase, POS1, POS2, GCPS, 1, 1, mode, lambda);
        if (ret < 0)
        {
            parallel_flag = false;
            continue;
        }
        interf_phase[i] = phase;
    }
    if (parallel_check(parallel_flag, "PS_deflat()", parallel_error_head)) return -1;
    return 0;
}

int Utils::_PS_deflat(
    Mat& phase,
    Mat& pos1,
    Mat& pos2,
    Mat& gcps,
    int start_row,
    int start_col,
    int mode,
    double lambda
)
{
    if (
        phase.rows < 1 ||
        phase.cols < 1 ||
        phase.channels() != 1 ||
        phase.type() != CV_64F ||
        pos1.rows < phase.rows ||
        pos1.cols != 6 ||
        pos1.channels() != 1 ||
        pos1.type() != CV_64F ||
        pos2.rows < phase.rows ||
        pos2.cols != 6 ||
        pos2.channels() != 1 ||
        pos2.type() != CV_64F ||
        gcps.channels() != 1 ||
        gcps.type() != CV_64F ||
        gcps.cols != 5 ||
        gcps.rows < 4 ||
        start_col < 1 ||
        start_row < 1 ||
        start_row > pos1.rows ||
        mode > 2 ||
        mode < 1 ||
        lambda <= 0.0
        )
    {
        fprintf(stderr, "_PS_deflat(): input check failed!\n\n");
        return -1;
    }
    double C = 4.0 * PI;
    if (mode == 1)
    {
        C = 4.0 * PI;
    }
    else
    {
        C = 2.0 * PI;
    }

    int ret;
    int num_gcps = gcps.rows;
    Mat R_M = Mat::zeros(num_gcps, 1, CV_64F);
    Mat R_S = Mat::zeros(num_gcps, 1, CV_64F);

    /*计算每个地面控制点的斜距*/
#pragma omp parallel for schedule(guided)
    for (int j = 0; j < num_gcps; j++)
    {
        Mat llh = Mat::zeros(1, 3, CV_64F);
        Mat xyz0, range_sat_tar1, range_sat_tar2;
        Mat fdcs1 = Mat::zeros(1, pos1.rows, CV_64F);
        Mat fdcs2 = Mat::zeros(1, pos2.rows, CV_64F);
        double range_sat_tar_norm1, range_sat_tar_norm2;

        llh.at<double>(0, 0) = gcps.at<double>(j, 0);
        llh.at<double>(0, 1) = gcps.at<double>(j, 1);
        llh.at<double>(0, 2) = gcps.at<double>(j, 2);
        ell2xyz(llh, xyz0);
        Mat xyz1 = Mat::zeros(pos1.rows, 3, CV_64F);
        Mat xyz2 = Mat::zeros(pos2.rows, 3, CV_64F);
        for (int k = 0; k < xyz1.rows; k++)
        {
            xyz0.copyTo(xyz1(Range(k, k + 1), Range(0, 3)));
        }
        for (int k = 0; k < xyz2.rows; k++)
        {
            xyz0.copyTo(xyz2(Range(k, k + 1), Range(0, 3)));
        }
        range_sat_tar1 = xyz1 - pos1(Range(0, pos1.rows), Range(0, 3));
        range_sat_tar2 = xyz2 - pos2(Range(0, pos2.rows), Range(0, 3));
        for (int k = 0; k < range_sat_tar1.rows; k++)
        {
            range_sat_tar1(Range(k, k + 1), Range(0, 3)).copyTo(xyz1);
            range_sat_tar_norm1 = sqrt(sum(xyz1.mul(xyz1))[0]);
            transpose(xyz1, xyz1);
            xyz1 = 2.0 * pos1(Range(k, k + 1), Range(3, 6)) * xyz1 / lambda / (range_sat_tar_norm1 + 1e-12);
            fdcs1.at<double>(0, k) = xyz1.at<double>(0, 0);
        }
        for (int k = 0; k < range_sat_tar2.rows; k++)
        {
            range_sat_tar2(Range(k, k + 1), Range(0, 3)).copyTo(xyz1);
            range_sat_tar_norm2 = sqrt(sum(xyz1.mul(xyz1))[0]);
            transpose(xyz1, xyz1);
            xyz1 = 2.0 * pos2(Range(k, k + 1), Range(3, 6)) * xyz1 / lambda / (range_sat_tar_norm2 + 1e-12);
            fdcs2.at<double>(0, k) = xyz1.at<double>(0, 0);
        }
        Point p1, p2;
        fdcs1 = abs(fdcs1);
        fdcs2 = abs(fdcs2);
        minMaxLoc(fdcs1, NULL, NULL, &p1, NULL);
        minMaxLoc(fdcs2, NULL, NULL, &p2, NULL);
        Mat Sat_pos_M, Sat_pos_S;
        pos1(Range(p1.x, p1.x + 1), Range(0, 3)).copyTo(Sat_pos_M);
        pos2(Range(p2.x, p2.x + 1), Range(0, 3)).copyTo(Sat_pos_S);
        Mat Slantrange_M = xyz0 - Sat_pos_M;
        Mat Slantrange_S = xyz0 - Sat_pos_S;
        R_M.at<double>(j, 0) = sqrt(sum(Slantrange_M.mul(Slantrange_M))[0]);
        R_S.at<double>(j, 0) = sqrt(sum(Slantrange_S.mul(Slantrange_S))[0]);
    }
    Mat A = Mat::zeros(num_gcps, 3, CV_64F);
    for (int j = 0; j < num_gcps; j++)
    {
        A.at<double>(j, 0) = 1.0;
        A.at<double>(j, 1) = gcps.at<double>(j, 3);
        A.at<double>(j, 2) = gcps.at<double>(j, 4);
    }
    Mat A_t;
    transpose(A, A_t);
    A = A_t * A;
    Mat b = A_t * (R_M - R_S);
    Mat x;
    if (!solve(A, b, x, DECOMP_LU))
    {
        fprintf(stderr, "_PS_deflat(): can't solve least square problem!\n");
        return -1;
    }

    Mat flat_phase = Mat::zeros(phase.rows, phase.cols, CV_64F);
    int nr = phase.rows;
    int nc = phase.cols;
#pragma omp parallel for schedule(guided)
    for (int j = 0; j < nr; j++)
    {
        for (int k = 0; k < nc; k++)
        {
            double xx = C * (x.at<double>(0, 0) + (double(j) + double(start_row)) * x.at<double>(1, 0) +
                (double(k) + double(start_col)) * x.at<double>(2, 0)) / lambda;
            phase.at<double>(j, k) = phase.at<double>(j, k) + xx;
        }
    }
    wrap(phase, phase);
    return 0;
}







int Utils::stateVec_interp(Mat& stateVec, double time_interval, Mat& stateVec_interp)
{
    if (stateVec.empty() ||
        stateVec.cols != 7 ||
        stateVec.rows < 7 ||
        time_interval < 0.0)
    {
        fprintf(stderr, "stateVec_interp(): input check failed!\n");
        return -1;
    }
    Mat statevec;
    if (stateVec.type() != CV_64F) stateVec.convertTo(statevec, CV_64F);
    else stateVec.copyTo(statevec);

    int rows = statevec.rows; int cols = statevec.cols;
    Mat time; statevec(cv::Range(0, rows), cv::Range(0, 1)).copyTo(time);
    time = time - time.at<double>(0, 0);
    Mat A = Mat::ones(rows, 6, CV_64F);
    Mat temp, b;
    //拟合x
    Mat x; statevec(cv::Range(0, rows), cv::Range(1, 2)).copyTo(x);
    time.copyTo(A(cv::Range(0, rows), cv::Range(1, 2)));
    temp = time.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(2, 3)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(3, 4)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(4, 5)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(5, 6)));
    transpose(A, temp);
    b = temp * x;
    A = temp * A;
    if (!cv::solve(A, b, x, cv::DECOMP_NORMAL))
    {
        fprintf(stderr, "stateVec_interp(): matrix defficiency!\n");
        return -1;
    }

    //拟合y
    A = Mat::ones(rows, 6, CV_64F);
    Mat y; statevec(cv::Range(0, rows), cv::Range(2, 3)).copyTo(y);
    time.copyTo(A(cv::Range(0, rows), cv::Range(1, 2)));
    temp = time.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(2, 3)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(3, 4)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(4, 5)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(5, 6)));
    transpose(A, temp);
    b = temp * y;
    A = temp * A;
    if (!cv::solve(A, b, y, cv::DECOMP_NORMAL))
    {
        fprintf(stderr, "stateVec_interp(): matrix defficiency!\n");
        return -1;
    }

    //拟合z

    A = Mat::ones(rows, 6, CV_64F);
    Mat z; statevec(cv::Range(0, rows), cv::Range(3, 4)).copyTo(z);
    time.copyTo(A(cv::Range(0, rows), cv::Range(1, 2)));
    temp = time.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(2, 3)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(3, 4)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(4, 5)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(5, 6)));
    transpose(A, temp);
    b = temp * z;
    A = temp * A;
    if (!cv::solve(A, b, z, cv::DECOMP_NORMAL))
    {
        fprintf(stderr, "stateVec_interp(): matrix defficiency!\n");
        return -1;
    }

    //拟合vx

    A = Mat::ones(rows, 6, CV_64F);
    Mat vx; statevec(cv::Range(0, rows), cv::Range(4, 5)).copyTo(vx);
    time.copyTo(A(cv::Range(0, rows), cv::Range(1, 2)));
    temp = time.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(2, 3)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(3, 4)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(4, 5)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(5, 6)));
    transpose(A, temp);
    b = temp * vx;
    A = temp * A;
    if (!cv::solve(A, b, vx, cv::DECOMP_NORMAL))
    {
        fprintf(stderr, "stateVec_interp(): matrix defficiency!\n");
        return -1;
    }

    //拟合vy

    A = Mat::ones(rows, 6, CV_64F);
    Mat vy; statevec(cv::Range(0, rows), cv::Range(5, 6)).copyTo(vy);
    time.copyTo(A(cv::Range(0, rows), cv::Range(1, 2)));
    temp = time.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(2, 3)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(3, 4)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(4, 5)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(5, 6)));
    transpose(A, temp);
    b = temp * vy;
    A = temp * A;
    if (!cv::solve(A, b, vy, cv::DECOMP_NORMAL))
    {
        fprintf(stderr, "stateVec_interp(): matrix defficiency!\n");
        return -1;
    }

    //拟合vz

    A = Mat::ones(rows, 6, CV_64F);
    Mat vz; statevec(cv::Range(0, rows), cv::Range(6, 7)).copyTo(vz);
    time.copyTo(A(cv::Range(0, rows), cv::Range(1, 2)));
    temp = time.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(2, 3)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(3, 4)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(4, 5)));
    temp = temp.mul(time);
    temp.copyTo(A(cv::Range(0, rows), cv::Range(5, 6)));
    transpose(A, temp);
    b = temp * vz;
    A = temp * A;
    if (!cv::solve(A, b, vz, cv::DECOMP_NORMAL))
    {
        fprintf(stderr, "stateVec_interp(): matrix defficiency!\n");
        return -1;
    }

    //插值

    int count = 1;
    double t = 0;
    while (t <= time.at<double>(rows - 1, 0))
    {
        count++;
        t += time_interval;
    }
    stateVec_interp.create(count, 7, CV_64F);
    Mat tt = Mat::ones(count, 6, CV_64F);
    tt(cv::Range(0, count), cv::Range(0, 1)).copyTo(stateVec_interp(cv::Range(0, count), cv::Range(0, 1)));
    t = 0.0;
    for (int i = 0; i < count; i++)
    {
        tt.at<double>(i, 1) = t;
        tt.at<double>(i, 2) = t * t;
        tt.at<double>(i, 3) = t * t * t;
        tt.at<double>(i, 4) = t * t * t * t;
        tt.at<double>(i, 5) = t * t * t * t * t;
        t += time_interval;
    }
    x = tt * x;
    y = tt * y;
    z = tt * z;
    vx = tt * vx;
    vy = tt * vy;
    vz = tt * vz;
    x.copyTo(stateVec_interp(cv::Range(0, count), cv::Range(1, 2)));
    y.copyTo(stateVec_interp(cv::Range(0, count), cv::Range(2, 3)));
    z.copyTo(stateVec_interp(cv::Range(0, count), cv::Range(3, 4)));
    vx.copyTo(stateVec_interp(cv::Range(0, count), cv::Range(4, 5)));
    vy.copyTo(stateVec_interp(cv::Range(0, count), cv::Range(5, 6)));
    vz.copyTo(stateVec_interp(cv::Range(0, count), cv::Range(6, 7)));
    return 0;
}




int Utils::coord_conversion(Mat& coefficient, Mat& coord_in_1, Mat& coord_in_2, Mat& coord_out)
{
    if (coefficient.cols != 32 ||
        coefficient.rows != 1 ||
        coefficient.type() != CV_64F ||
        coord_in_1.empty() ||
        coord_in_2.empty() ||
        coord_in_1.type() != CV_64F ||
        coord_in_2.type() != CV_64F ||
        coord_in_1.rows != coord_in_2.rows ||
        coord_in_1.cols != coord_in_2.cols)
    {
        fprintf(stderr, "coord_conversion(): input check failed!\n");
        return -1;
    }
    double offset_out, scale_out, offset_in_1, offset_in_2, scale_in_1, scale_in_2;
    double a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
        a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24;
    offset_out = coefficient.at<double>(0, 0);
    scale_out = coefficient.at<double>(0, 1);
    offset_in_1 = coefficient.at<double>(0, 2);
    scale_in_1 = coefficient.at<double>(0, 3);
    offset_in_2 = coefficient.at<double>(0, 4);
    scale_in_2 = coefficient.at<double>(0, 5);

    a0 = coefficient.at<double>(0, 6);
    a1 = coefficient.at<double>(0, 7);
    a2 = coefficient.at<double>(0, 8);
    a3 = coefficient.at<double>(0, 9);
    a4 = coefficient.at<double>(0, 10);
    a5 = coefficient.at<double>(0, 11);
    a6 = coefficient.at<double>(0, 12);
    a7 = coefficient.at<double>(0, 13);
    a8 = coefficient.at<double>(0, 14);
    a9 = coefficient.at<double>(0, 15);
    a10 = coefficient.at<double>(0, 16);
    a11 = coefficient.at<double>(0, 17);
    a12 = coefficient.at<double>(0, 18);
    a13 = coefficient.at<double>(0, 19);
    a14 = coefficient.at<double>(0, 20);
    a15 = coefficient.at<double>(0, 21);
    a16 = coefficient.at<double>(0, 22);
    a17 = coefficient.at<double>(0, 23);
    a18 = coefficient.at<double>(0, 24);
    a19 = coefficient.at<double>(0, 25);
    a20 = coefficient.at<double>(0, 26);
    a21 = coefficient.at<double>(0, 27);
    a22 = coefficient.at<double>(0, 28);
    a23 = coefficient.at<double>(0, 29);
    a24 = coefficient.at<double>(0, 30);
    int rows = coord_in_1.rows; int cols = coord_in_1.cols;
    Mat coord_out1(rows, cols, CV_64F);
#pragma omp parallel for schedule(guided)
    for (int i = 0; i < rows; i++)
    {
        double in1, in2, out;
        for (int j = 0; j < cols; j++)
        {
            in1 = (coord_in_1.at<double>(i, j) - offset_in_1) / scale_in_1;
            in2 = (coord_in_2.at<double>(i, j) - offset_in_2) / scale_in_2;
            out = a0 + a1 * in1 + a2 * in1 * in1 + a3 * in1 * in1 * in1 + a4 * in1 * in1 * in1 * in1 +
                a5 * in2 + a6 * in2 * in1 + a7 * in2 * in1 * in1 + a8 * in2 * in1 * in1 * in1 + a9 * in2 * in1 * in1 * in1 * in1 +
                a10 * in2 * in2 + a11 * in2 * in2 * in1 + a12 * in2 * in2 * in1 * in1 + a13 * in2 * in2 * in1 * in1 * in1 + a14 * in2 * in2 * in1 * in1 * in1 * in1 +
                a15 * in2 * in2 * in2 + a16 * in2 * in2 * in2 * in1 + a17 * in2 * in2 * in2 * in1 * in1 + a18 * in2 * in2 * in2 * in1 * in1 * in1 + a19 * in2 * in2 * in2 * in1 * in1 * in1 * in1 +
                a20 * in2 * in2 * in2 * in2 + a21 * in2 * in2 * in2 * in2 * in1 + a22 * in2 * in2 * in2 * in2 * in1 * in1 + a23 * in2 * in2 * in2 * in2 * in1 * in1 * in1 + a24 * in2 * in2 * in2 * in2 * in1 * in1 * in1 * in1;
            out = out * scale_out + offset_out;
            coord_out1.at<double>(i, j) = out;
        }
    }
    coord_out1.copyTo(coord_out);
    return 0;
}


int Utils::homogeneous_test(const Mat& pixel1, const Mat& pixel2, int* homo_flag, double alpha, const char* method)
{
    if (pixel1.cols != 1 ||
        pixel1.rows < 5 ||
        pixel1.type() != CV_64F ||
        pixel1.rows != pixel2.rows ||
        pixel1.cols != pixel2.cols ||
        pixel2.type() != CV_64F ||
        homo_flag == NULL ||
        method == NULL
        )
    {
        fprintf(stderr, "homogeneous_test(): input check failed!\n");
        return -1;
    }

    //Kolmogorov-Smirnov检验
    /*
     alpha      0.20    0.15    0.10    0.05    0.025    0.01    0.005    0.001
     c(alpha)   1.073    1.138    1.224    1.358    1.48    1.628    1.731    1.949
    */
    if (strcmp(method, "KS") == 0)
    {
        Mat p1, p2, cdf1, cdf2;
        double thresh;
        pixel1.copyTo(p1); pixel2.copyTo(p2);
        int N = p1.rows;
        cv::sort(p1, p1, cv::SORT_EVERY_COLUMN + cv::SORT_ASCENDING);
        cv::sort(p2, p2, cv::SORT_EVERY_COLUMN + cv::SORT_ASCENDING);
        if (p1.at<double>(N - 2, 0) <= p2.at<double>(0, 0) || p2.at<double>(N - 2, 0) <= p1.at<double>(0, 0))
        {
            *homo_flag = -1;
            return 0;
        }
        //确定threshold
        if (fabs(alpha - 0.2) < 0.01)
        {
            thresh = sqrt(2 / (double)N) * 1.073;
        }
        else if (fabs(alpha - 0.15) < 0.0001)
        {
            thresh = sqrt(2 / (double)N) * 1.138;
        }
        else if (fabs(alpha - 0.1) < 0.0001)
        {
            thresh = sqrt(2 / (double)N) * 1.224;
        }
        else if (fabs(alpha - 0.05) < 0.0001)
        {
            thresh = sqrt(2 / (double)N) * 1.358;
        }
        else if (fabs(alpha - 0.025) < 0.0001)
        {
            thresh = sqrt(2 / (double)N) * 1.48;
        }
        else if (fabs(alpha - 0.01) < 0.0001)
        {
            thresh = sqrt(2 / (double)N) * 1.628;
        }
        else if (fabs(alpha - 0.005) < 0.0001)
        {
            thresh = sqrt(2 / (double)N) * 1.731;
        }
        else if (fabs(alpha - 0.001) < 0.0001)
        {
            thresh = sqrt(2 / (double)N) * 1.949;
        }
        else
        {
            thresh = sqrt(2 / (double)N) * 1.358;
        }
        //计算C.D.F最大间距
        double Dmax = 0.0, tmp;
        int front_1 = 0, front_2 = 0;
        if (p1.at<double>(0, 0) > p2.at<double>(0, 0))
        {
            for (int i = 0; i < N - 1; i++)
            {
                if (p2.at<double>(i, 0) <= p1.at<double>(0, 0) && p2.at<double>(i + 1, 0) >= p1.at<double>(0, 0))
                {
                    front_2 = i;
                    break;
                }
            }
        }
        else
        {
            for (int i = 0; i < N - 1; i++)
            {
                if (p1.at<double>(i, 0) <= p2.at<double>(0, 0) && p1.at<double>(i + 1, 0) >= p2.at<double>(0, 0))
                {
                    front_1 = i;
                    break;
                }
            }
        }
        while (front_1 < N && front_2 < N)
        {
            tmp = fabs((double)front_1 / (double)N - (double)front_2 / (double)N);
            Dmax = Dmax > tmp ? Dmax : tmp;
            if (front_1 >= N - 1 || front_2 >= N - 1)
            {
                break;
            }
            if (p1.at<double>(front_1 + 1, 0) < p2.at<double>(front_2 + 1, 0)) front_1++;
            else if (p1.at<double>(front_1 + 1, 0) > p2.at<double>(front_2 + 1, 0)) front_2++;
            else
            {
                front_1++; front_2++;
            }
        }
        if (Dmax > thresh) *homo_flag = -1;
        else *homo_flag = 0;
    }

    //Anderson-Darling 检验
    else if (strcmp(method, "AD") == 0)
    {
        /*
        alpha = 0.01, AD_inf = 3.857;
        alpha = 0.05, AD_inf = 2.492;
        alpha = 0.1,  AD_inf = 1.933;
        */
        Mat p1, p2, p;
        double thresh;
        pixel1.copyTo(p1); pixel2.copyTo(p2);
        cv::vconcat(p1, p2, p);
        int n = p1.rows; int N = 2 * n;
        cv::sort(p1, p1, cv::SORT_EVERY_COLUMN + cv::SORT_ASCENDING);
        cv::sort(p2, p2, cv::SORT_EVERY_COLUMN + cv::SORT_ASCENDING);
        cv::sort(p, p, cv::SORT_EVERY_COLUMN + cv::SORT_ASCENDING);
        if (fabs(alpha - 0.05) < 0.0001)
        {
            thresh = (2.492 - 1) * (1 - 1.55 / (double)N) + 1;
        }
        else if (fabs(alpha - 0.01) < 0.0001)
        {
            thresh = (3.857 - 1) * (1 - 1.55 / (double)N) + 1;
        }
        else
        {
            thresh = (1.933 - 1) * (1 - 1.55 / (double)N) + 1;
        }
        int c = 0; double sum = 0.0, sentinel = -1.0;
        for (int i = 1; i < N; i++)
        {
            while (c <= n - 1)
            {
                if (p.at<double>(i - 1, 0) <= p1.at<double>(c, 0)) break;
                c++;
            }
            sum += double((N * c - n * i) * (N * c - n * i)) / double(i * (N - i));
        }
        sum /= (double)(n * n);
        if (sum > thresh) *homo_flag = -1;
        else *homo_flag = 0;

    }

    return 0;
}


int Utils::coherence_matrix_estimation(const vector<ComplexMat>& slc_series, ComplexMat& coherence_matrix, int est_window_width, int est_window_height, int ref_row, int ref_col, bool b_homogeneous_test, bool b_normalize)
{
    if (slc_series.size() < 5 ||
        est_window_width < 3 ||
        est_window_height < 3 ||
        est_window_height % 2 != 1 ||
        est_window_width % 2 != 1 ||
        ref_col < 0 ||
        ref_row < 0
        )
    {
        fprintf(stderr, "coherence_estimation(): input check failed!\n");
        return -1;
    }
    if (slc_series[0].type() != CV_64F || slc_series[0].isempty() || ref_row > slc_series[0].GetRows() - 1 || ref_col > slc_series[0].GetCols() - 1
        )
    {
        fprintf(stderr, "coherence_estimation(): input check failed!\n");
        return -1;
    }
    int n_images = slc_series.size(), ret;
    int rows = slc_series[0].GetRows(); int cols = slc_series[0].GetCols();
    int radius_width = (est_window_width - 1) / 2;
    int radius_height = (est_window_height - 1) / 2;
    int left, right, bottom, top;
    left = (ref_col - radius_width) < 0 ? 0 : (ref_col - radius_width);
    right = (ref_col + radius_width) > cols - 1 ? cols - 1 : (ref_col + radius_width);
    bottom = (ref_row + radius_height) > rows - 1 ? rows - 1 : (ref_row + radius_height);
    top = (ref_row - radius_height) < 0 ? 0 : (ref_row - radius_height);
    rows = bottom - top + 1;
    cols = right - left + 1;
    if (b_homogeneous_test)
    {
        //统计同质检验
        ComplexMat pix1(n_images, 1); ComplexMat pix2(n_images, 1);
        Mat pix1_amp, pix2_amp;
        Mat mask = Mat::zeros(rows, cols, CV_32S);
        mask.at<int>(ref_row - top, ref_col - left) = 1;
        int b_homo, count = 1;
        for (int i = 0; i < n_images; i++)
        {
            pix1.re.at<double>(i, 0) = slc_series[i].re.at<double>(ref_row, ref_col);
            pix1.im.at<double>(i, 0) = slc_series[i].im.at<double>(ref_row, ref_col);
        }
        pix1_amp = pix1.GetMod();
        for (int i = 0; i < rows; i++)
        {

            for (int j = 0; j < cols; j++)
            {
                if (i == (ref_row - top) && j == (ref_col - left)) continue;
                for (int k = 0; k < n_images; k++)
                {
                    pix2.re.at<double>(k, 0) = slc_series[k].re.at<double>(i + top, j + left);
                    pix2.im.at<double>(k, 0) = slc_series[k].im.at<double>(i + top, j + left);
                }
                pix2_amp = pix2.GetMod();
                ret = homogeneous_test(pix1_amp, pix2_amp, &b_homo, 0.1);
                //ret = homogeneous_test(pix1_amp, pix2_amp, &b_homo, 0.1, "AD");
                if (return_check(ret, "homogeneous_test()", error_head)) return -1;
                if (b_homo == 0) { mask.at<int>(i, j) = 1; count++; }
            }
        }
        //Mat c; mask.convertTo(c, CV_64F);
        //cvmat2bin("E:\\working_dir\\projects\\software\\InSAR\\bin\\mask.bin", c);
        if (count < 2)
        {
            //fprintf(stderr, "coherence_matrix_estimation(): no homogenous pixels inside estimation window!\n");
            return -1;
        }
        //估计相关矩阵
        int count2 = count; count = 0;
        ComplexMat Covariance;
        Mat sum(n_images, 1, CV_64F), A(n_images, count2, CV_64F), B(n_images, count2, CV_64F), C, A_t, B_t;
        double s;
        for (int k = 0; k < n_images; k++)
        {
            s = 0.0;
            count = 0;
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < cols; j++)
                {
                    if (mask.at<int>(i, j) > 0)
                    {
                        A.at<double>(k, count) = slc_series[k].re.at<double>(i + top, j + left);
                        B.at<double>(k, count) = slc_series[k].im.at<double>(i + top, j + left);
                        count++;
                        s += A.at<double>(k, count - 1) * A.at<double>(k, count - 1)
                            + B.at<double>(k, count - 1) * B.at<double>(k, count - 1);
                    }
                }
            }
            sum.at<double>(k, 0) = s;
        }
        cv::transpose(A, A_t); cv::transpose(B, B_t);
        C = A * A_t + B * B_t;
        C.copyTo(Covariance.re);
        C = B * A_t - A * B_t;
        C.copyTo(Covariance.im);
        double denum;
        if (b_normalize)
        {

            for (int i = 0; i < n_images; i++)
            {
                for (int j = 0; j < n_images; j++)
                {
                    denum = sqrt(sum.at<double>(i, 0) * sum.at<double>(j, 0));
                    Covariance.re.at<double>(i, j) = Covariance.re.at<double>(i, j) / (denum + 1e-10);
                    Covariance.im.at<double>(i, j) = Covariance.im.at<double>(i, j) / (denum + 1e-10);
                }
            }


        }
        else
        {
            Covariance = Covariance * (1 / (double)count2);
        }
        coherence_matrix = Covariance;
    }
    else
    {

    }
    return 0;
}



int Utils::unwrap_region_growing(
    vector<tri_node>& nodes,
    const vector<tri_edge>& edges,
    size_t start_edge,
    double distance_thresh,
    double quality_thresh
)
{
    if (nodes.size() < 3 ||
        edges.size() < 3 ||
        start_edge < 1 ||
        start_edge > edges.size()
        )
    {
        fprintf(stderr, "unwrap_region_growing(): input check failed!\n\n");
        return -1;
    }
    if (distance_thresh < 1.0) distance_thresh = 1.0;
    if (quality_thresh > 0.9) quality_thresh = 0.9;
    //找到增量积分起始点
    int ix = 0;
    double MC = -1.0;
    size_t num_edges = edges.size();
    //for (int i = 0; i < num_edges; i++)
    //{
    //    if (edges[i].MC > MC)
    //    {
    //        ix = i;
    //        MC = edges[i].MC;
    //    }
    //}
    size_t start = edges[start_edge - 1].end1;

    //采用类似质量图法解缠的算法进行增量积分集成
    edge_index tmp;
    priority_queue<edge_index> que;
    //nodes[start - 1].set_vel(0.0);//起始点形变速率和高程误差设置为0,后续可根据参考点进行校正
    //nodes[start - 1].set_height(0.0);
    nodes[start - 1].set_status(true);
    long* ptr_neigh = NULL;
    int num_neigh, end2, number, row1, col1, row2, col2;
    double distance, phase, MC_total, phase_total, delta_phase;

    nodes[start - 1].get_neigh_ptr(&ptr_neigh, &num_neigh);
    for (int i = 0; i < num_neigh; i++)
    {
        end2 = edges[*(ptr_neigh + i) - 1].end1 == start ? edges[*(ptr_neigh + i) - 1].end2 : edges[*(ptr_neigh + i) - 1].end1;
        nodes[start - 1].get_distance(nodes[end2 - 1], &distance);
        if (!nodes[end2 - 1].get_status() &&
            distance <= distance_thresh &&
            edges[*(ptr_neigh + i) - 1].quality > quality_thresh
            )
        {
            tmp.num = *(ptr_neigh + i);
            tmp.quality = -edges[*(ptr_neigh + i) - 1].quality;
            que.push(tmp);
        }
    }

    while (que.size() != 0)
    {
        tmp = que.top();
        que.pop();
        if (nodes[edges[tmp.num - 1].end1 - 1].get_status())
        {
            number = edges[tmp.num - 1].end1;
            end2 = edges[tmp.num - 1].end2;
        }
        else
        {
            number = edges[tmp.num - 1].end2;
            end2 = edges[tmp.num - 1].end1;
        }
        MC_total = 1e-10;
        phase_total = 0.0;
        if (!nodes[end2 - 1].get_status())
        {
            nodes[end2 - 1].get_pos(&row2, &col2);
            nodes[end2 - 1].get_neigh_ptr(&ptr_neigh, &num_neigh);
            for (int i = 0; i < num_neigh; i++)
            {
                number = edges[*(ptr_neigh + i) - 1].end1 == end2 ? edges[*(ptr_neigh + i) - 1].end2 : edges[*(ptr_neigh + i) - 1].end1;
                if (nodes[number - 1].get_status())
                {
                    nodes[number - 1].get_phase(&phase);
                    nodes[number - 1].get_pos(&row1, &col1);
                    if (row1 > row2)
                    {
                        delta_phase = -edges[*(ptr_neigh + i) - 1].phase_diff;
                    }
                    if (row1 < row2)
                    {
                        delta_phase = edges[*(ptr_neigh + i) - 1].phase_diff;
                    }
                    if (row1 == row2)
                    {
                        if (col1 > col2)
                        {
                            delta_phase = -edges[*(ptr_neigh + i) - 1].phase_diff;
                        }
                        else
                        {
                            delta_phase = edges[*(ptr_neigh + i) - 1].phase_diff;
                        }
                    }
                    phase_total += (delta_phase + phase) * edges[*(ptr_neigh + i) - 1].quality;
                    MC_total += edges[*(ptr_neigh + i) - 1].quality;
                }
            }
            nodes[end2 - 1].set_phase(phase_total / MC_total);
            nodes[end2 - 1].set_status(true);
            nodes[end2 - 1].get_neigh_ptr(&ptr_neigh, &num_neigh);
            number = end2;
            for (int i = 0; i < num_neigh; i++)
            {

                end2 = edges[*(ptr_neigh + i) - 1].end1 == number ? edges[*(ptr_neigh + i) - 1].end2 : edges[*(ptr_neigh + i) - 1].end1;
                nodes[number - 1].get_distance(nodes[end2 - 1], &distance);
                if (!nodes[end2 - 1].get_status() &&
                    distance <= distance_thresh &&
                    edges[*(ptr_neigh + i) - 1].quality > quality_thresh
                    )
                {
                    tmp.num = *(ptr_neigh + i);
                    tmp.quality = -edges[*(ptr_neigh + i) - 1].quality;
                    que.push(tmp);
                }
            }
        }
    }
    return 0;
}

//int Utils::unwrap_3D(
//    const Mat& mask,
//    const vector<Mat>& quality_map,
//    vector<Mat>& wrapped_phase_series,
//    vector<Mat>& unwrapped_phase_series,
//    const char* delaunay_exe_path,
//    const char* tmp_file_path,
//    double distance_thresh,
//    double quality_thresh
//)
//{
//    if (mask.rows < 2 ||
//        mask.cols < 2 ||
//        mask.type() != CV_32S ||
//        wrapped_phase_series.size() < 2 ||
//        delaunay_exe_path == NULL ||
//        tmp_file_path == NULL ||
//        quality_map.size() != wrapped_phase_series.size()
//        )
//    {
//        fprintf(stderr, "unwrap_3D(): input check failed!\n");
//        return -1;
//    }
//
//    quality_thresh = quality_thresh > 1.0 ? 0.9 : quality_thresh;
//    distance_thresh = distance_thresh < 1.0 ? 1.0 : distance_thresh;
//
//    /*----------------------------------*/
//    /*            时间维解缠            */
//    /*----------------------------------*/
//
//    /*
//    * 利用mask生成delaunay三角网络
//    */
//    size_t nr = mask.rows;
//    size_t nc = mask.cols;
//    long num_nodes = cv::countNonZero(mask);
//    if (num_nodes < 3)
//    {
//        fprintf(stderr, "unwrap_3D(): at least 3 nodes are needed!\n");
//        return -1;
//    }
//    int ret;
//    int n_images = wrapped_phase_series.size();
//    for (size_t i = 0; i < n_images; i++)
//    {
//        if (quality_map[i].rows != nr || quality_map[i].cols != nc)
//        {
//            fprintf(stderr, "unwrap_3D(): quality map size mismatch!\n");
//            return -1;
//        }
//        if (wrapped_phase_series[i].rows != nr || wrapped_phase_series[i].cols != nc)
//        {
//            fprintf(stderr, "unwrap_3D(): wrapped phase size mismatch!\n");
//            return -1;
//        }
//    }
//    unwrapped_phase_series.resize(n_images);
//    vector<tri_node> nodes; vector<vector<tri_node>> nodes_vec; nodes_vec.resize(n_images);
//    vector<tri_edge> edges; vector<vector<tri_edge>> edges_vec; edges_vec.resize(n_images);
//    vector<int> node_neighbour;
//    string tmp_folder(tmp_file_path);
//    string node_file = tmp_folder + "\\triangle.node";
//    ret = write_node_file(node_file.c_str(), mask);
//    if (return_check(ret, "write_node_file()", error_head)) return -1;
//    ret = gen_delaunay(node_file.c_str(), delaunay_exe_path);
//    if (return_check(ret, "gen_delaunay()", error_head)) return -1;
//    string edge_file = tmp_folder + "\\triangle.1.edge";
//    ret = read_edges(edge_file.c_str(), edges, node_neighbour, num_nodes);
//    if (return_check(ret, "read_edges()", error_head)) return -1;
//    for (int i = 0; i < n_images; i++)
//    {
//        ret = init_tri_node(nodes, wrapped_phase_series[i], mask, edges, node_neighbour, num_nodes);
//        if (return_check(ret, "init_tri_node()", error_head)) return -1;
//        ret = init_edge_phase_diff(edges, nodes);
//        if (return_check(ret, "init_edge_phase_diff()", error_head)) return -1;
//        ret = init_edges_quality(quality_map[i], edges, nodes);
//        if (return_check(ret, "init_edges_quality()", error_head)) return -1;
//        nodes_vec[i] = nodes;
//        edges_vec[i] = edges;
//    }
//
//    /*
//    * 一维高斯滤波
//    */
//
//    int Gaussian_radius = 2;
//    double sigma = 1.0;
//    Mat Gaussian_template = Mat::zeros(2 * Gaussian_radius + 1, 1, CV_64F);
//    for (int i = 0; i < 2 * Gaussian_radius + 1; i++)
//    {
//        Gaussian_template.at<double>(i, 0) = exp(-(double(i - Gaussian_radius)) * (double(i - Gaussian_radius)) / (2.0 * sigma * sigma))
//            / (sigma * sqrt(PI * 2.0));
//    }
//    size_t num_edges = edges.size();
//#pragma omp parallel for schedule(guided)
//    for (long long i = 0; i < num_edges; i++)
//    {
//        Mat time_series = Mat::zeros(n_images, 1, CV_64F); Mat temp;
//        for (size_t j = 0; j < n_images; j++)
//        {
//            time_series.at<double>(j, 0) = edges_vec[j][i].phase_diff;
//        }
//        cv::copyMakeBorder(time_series, time_series, Gaussian_radius, Gaussian_radius, 0, 0, cv::BORDER_REPLICATE);
//        for (size_t j = Gaussian_radius; j < n_images + Gaussian_radius; j++)
//        {
//            time_series.at<double>(j, 0) = cv::sum(time_series(cv::Range(j - Gaussian_radius, j + Gaussian_radius + 1),
//                cv::Range(0, 1)).mul(Gaussian_template))[0];
//        }
//        time_series(cv::Range(Gaussian_radius, n_images + Gaussian_radius), cv::Range(0, 1)).copyTo(temp);
//
//        /*
//        * 时间维解缠
//        */
//        Mat temp2; temp.copyTo(temp2);
//        for (size_t j = 1; j < n_images; j++)
//        {
//            double tmp = temp.at<double>(j, 0) - temp.at<double>(j - 1, 0);
//            tmp = atan2(sin(tmp), cos(tmp));
//            temp2.at<double>(j, 0) = temp2.at<double>(j - 1, 0) + tmp;
//        }
//
//        for (size_t j = 0; j < n_images; j++)
//        {
//            edges_vec[j][i].phase_diff = temp2.at<double>(j, 0);
//        }
//    }
//
//    /*----------------------------------*/
//    /*            空间维解缠            */
//    /*----------------------------------*/
//
//    /*
//    * 确定解缠起始点
//    */
//    double mean_quality, max_quality = -1.0;
//    size_t ix_max;
//    for (size_t i = 0; i < num_edges; i++)
//    {
//        mean_quality = 0.0;
//        for (size_t j = 0; j < n_images; j++)
//        {
//            mean_quality += edges_vec[j][i].quality;
//        }
//        mean_quality /= (double)n_images;
//        if (mean_quality > max_quality)
//        {
//            max_quality = mean_quality;
//            ix_max = i;
//        }
//    }
//    //#pragma omp parallel for schedule(guided)
//    for (size_t i = 0; i < n_images; i++)
//    {
//        //vector<tri_node> nodes; vector<tri_edge>edges;
//        int row, col; double phase;
//        Mat unwrapped_phase;
//        wrapped_phase_series[i].copyTo(unwrapped_phase);
//        nodes = nodes_vec[i];
//        edges = edges_vec[i];
//        ret = unwrap_region_growing(nodes, edges, ix_max, distance_thresh, quality_thresh);
//        if (return_check(ret, "unwrap_region_growing()", error_head)) return -1;
//
//
//
//        /*
//        * 从节点中取出解缠相位
//        */
//
//        for (size_t j = 0; j < num_nodes; j++)
//        {
//            if (nodes[j].get_status())
//            {
//                nodes[j].get_pos(&row, &col);
//                nodes[j].get_phase(&phase);
//                unwrapped_phase.at<double>(row, col) = phase;
//            }
//        }
//        unwrapped_phase.copyTo(unwrapped_phase_series[i]);
//    }
//
//    /*----------------------------------*/
//    /*          时间维再解缠            */
//    /*----------------------------------*/
//
//
////#pragma omp parallel for schedule(guided)
////    for (int i = 0; i < nr; i++)
////    {
////        double tmp, tmp_old;
////        for (size_t j = 0; j < nc; j++)
////        {
////            if (mask.at<int>(i, j) > 0)
////            {
////                for (size_t k = 1; k < n_images; k++)
////                {
////                    if (k == 1) tmp_old = unwrapped_phase_series[0].at<double>(i, j);
////                    tmp = unwrapped_phase_series[k].at<double>(i, j) - tmp_old;
////                    tmp = atan2(sin(tmp), cos(tmp));
////                    tmp_old = unwrapped_phase_series[k].at<double>(i, j);
////                    unwrapped_phase_series[k].at<double>(i, j) = unwrapped_phase_series[k - 1].at<double>(i, j) + tmp;
////                }
////            }
////            
////        }
////    }
//
//    return 0;
//}

//int Utils::unwrap_3D_mcf(
//    const Mat& mask,
//    const vector<Mat>& quality_map,
//    vector<Mat>& wrapped_phase_series,
//    vector<Mat>& unwrapped_phase_series,
//    const char* delaunay_exe_path,
//    const char* mcf_exe_path,
//    const char* tmp_file_path,
//    double distance_thresh
//)
//{
//    if (mask.rows < 2 ||
//        mask.cols < 2 ||
//        mask.type() != CV_32S ||
//        wrapped_phase_series.size() < 2 ||
//        delaunay_exe_path == NULL ||
//        tmp_file_path == NULL ||
//        mcf_exe_path == NULL ||
//        quality_map.size() != wrapped_phase_series.size()
//        )
//    {
//        fprintf(stderr, "unwrap_3D_mcf(): input check failed!\n");
//        return -1;
//    }
//
//    distance_thresh = distance_thresh < 1.0 ? 1.0 : distance_thresh;
//
//    /*----------------------------------*/
//    /*            时间维解缠            */
//    /*----------------------------------*/
//
//    /*
//    * 利用mask生成delaunay三角网络
//    */
//    size_t nr = mask.rows;
//    size_t nc = mask.cols;
//    long num_nodes = cv::countNonZero(mask);
//    if (num_nodes < 3)
//    {
//        fprintf(stderr, "unwrap_3D_mcf(): at least 3 nodes are needed!\n");
//        return -1;
//    }
//    int ret;
//    int n_images = wrapped_phase_series.size();
//    for (size_t i = 0; i < n_images; i++)
//    {
//        if (quality_map[i].rows != nr || quality_map[i].cols != nc)
//        {
//            fprintf(stderr, "unwrap_3D_mcf(): quality map size mismatch!\n");
//            return -1;
//        }
//        if (wrapped_phase_series[i].rows != nr || wrapped_phase_series[i].cols != nc)
//        {
//            fprintf(stderr, "unwrap_3D_mcf(): wrapped phase size mismatch!\n");
//            return -1;
//        }
//    }
//    Unwrap unwrap;
//    unwrapped_phase_series.resize(n_images);
//    vector<tri_node> nodes;
//    vector<tri_edge> edges;
//    vector<triangle> tri;
//    vector<int> node_neighbour;
//    Mat out_mask;
//    string tmp_folder(tmp_file_path);
//    string node_file = tmp_folder + "\\triangle.node";
//    string ele_file = tmp_folder + "\\triangle.1.ele";
//    string neigh_file = tmp_folder + "\\triangle.1.neigh";
//    string mcf_problem = tmp_folder + "\\mcf_delaunay.net";
//    string mcf_solution = tmp_folder + "\\mcf_delaunay.net.sol";
//    string edge_file = tmp_folder + "\\triangle.1.edge";
//    ret = write_node_file(node_file.c_str(), mask);
//    if (return_check(ret, "write_node_file()", error_head)) return -1;
//    ret = gen_delaunay(node_file.c_str(), delaunay_exe_path);
//    if (return_check(ret, "gen_delaunay()", error_head)) return -1;
//    ret = read_edges(edge_file.c_str(), edges, node_neighbour, num_nodes);
//    if (return_check(ret, "read_edges()", error_head)) return -1;
//    int num_triangle, positive = 0, negative = 0;
//    size_t num_edges = edges.size();
//    //先将起始点做时间维解缠
//    int pos_row, pos_col; double tmp;
//    ret = init_tri_node(nodes, wrapped_phase_series[0], mask, edges, node_neighbour, num_nodes);
//    if (return_check(ret, "init_tri_node()", error_head)) return -1;
//    nodes[199].get_pos(&pos_row, &pos_col);
//    Mat time_series(n_images, 1, CV_64F);
//    for (int i = 0; i < n_images; i++)
//    {
//        time_series.at<double>(i, 0) = wrapped_phase_series[i].at<double>(pos_row, pos_col);
//    }
//    for (int i = 1; i < n_images; i++)
//    {
//        tmp = time_series.at<double>(i, 0) - time_series.at<double>(i - 1, 0);
//        wrapped_phase_series[i].at<double>(pos_row, pos_col) = time_series.at<double>(i - 1, 0) +
//            atan2(sin(tmp), cos(tmp));
//    }
//
//    for (int i = 0; i < n_images; i++)
//    {
//        //将edges的gain清零
//        for (size_t ii = 0; ii < num_edges; ii++)
//        {
//            edges[ii].gain = 0.0;
//        }
//        positive = 0, negative = 0;
//        ret = init_tri_node(nodes, wrapped_phase_series[i], mask, edges, node_neighbour, num_nodes);
//        if (return_check(ret, "init_tri_node()", error_head)) return -1;
//        if (i == 0)
//        {
//            ret = read_triangle(ele_file.c_str(), neigh_file.c_str(), tri, nodes, edges);
//            if (return_check(ret, "read_triangle()", error_head)) return -1;
//            num_triangle = tri.size();
//        }
//        ret = residue(tri, nodes, edges, 10.0);
//        if (return_check(ret, "residue()", error_head)) return -1;
//        /*
//        * 检查残差点数,若无残差点则不使用mcf.exe求解
//        */
//        for (int ii = 0; ii < num_triangle; ii++)
//        {
//            if (tri[ii].residue > 0.7)
//            {
//                positive++;
//            }
//            if (tri[ii].residue < -0.7)
//            {
//                negative++;
//            }
//        }
//        if (positive == 0 && negative == 0)
//        {
//
//            ret = unwrap.MCF(wrapped_phase_series[i], unwrapped_phase_series[i], out_mask, mask, nodes, edges, 200, false, distance_thresh);
//            if (return_check(ret, "unwrap.MCF()", error_head)) return -1;
//        }
//        else
//        {
//            ret = write_DIMACS(mcf_problem.c_str(), tri, nodes, edges, quality_map[i]);
//            if (return_check(ret, "write_DIMACS()", error_head)) return -1;
//            ret = unwrap.mcf_delaunay(mcf_problem.c_str(), mcf_exe_path);
//            if (return_check(ret, "unwrap.mcf_delaunay()", error_head)) return -1;
//            ret = read_DIMACS(mcf_solution.c_str(), edges, nodes, tri);
//            if (return_check(ret, "read_DIMACS()", error_head)) return -1;
//            ret = unwrap.MCF(wrapped_phase_series[i], unwrapped_phase_series[i], out_mask, mask, nodes, edges, 200, false, distance_thresh);
//            if (return_check(ret, "unwrap.MCF()", error_head)) return -1;
//        }
//
//    }
//    return 0;
//}

//int Utils::unwrap_3D_adaptive_tiling(
//    const Mat& mask,
//    const vector<Mat>& quality_map,
//    vector<Mat>& wrapped_phase_series,
//    vector<Mat>& unwrapped_phase_series,
//    const char* delaunay_exe_path,
//    const char* mcf_exe_path,
//    const char* tmp_file_path,
//    double distance_thresh,
//    double quality_thresh
//)
//{
//    if (mask.rows < 2 ||
//        mask.cols < 2 ||
//        mask.type() != CV_32S ||
//        wrapped_phase_series.size() < 2 ||
//        delaunay_exe_path == NULL ||
//        tmp_file_path == NULL ||
//        mcf_exe_path == NULL ||
//        quality_map.size() != wrapped_phase_series.size() ||
//        distance_thresh < 1.0 ||
//        quality_thresh > 1.0
//        )
//    {
//        fprintf(stderr, "unwrap_3D_adaptive_tiling(): input check failed!\n");
//        return -1;
//    }
//
//    /*----------------------------------*/
//    /*           自适应分块             */
//    /*----------------------------------*/
//
//    /*
//    * 确定搜索范围半径
//    */
//    int r = (int)floor(distance_thresh), c, ret;
//    Mat c_mat = Mat::zeros(2 * r + 1, 1, CV_32S);
//    for (int i = 0; i < 2 * r + 1; i++)
//    {
//        c_mat.at<int>(i, 0) = (int)floor(sqrt(distance_thresh * distance_thresh - double(r - i) * double(r - i)));
//    }
//
//    Mat mask1, mask2; mask.copyTo(mask1);
//    int nr = mask.rows; int nc = mask.cols;
//    int n_images = wrapped_phase_series.size();
//    size_t num_nodes = cv::countNonZero(mask);
//    if (num_nodes < 3)
//    {
//        fprintf(stderr, "unwrap_3D_adaptive_tiling(): at least 3 nodes are needed!\n");
//        return -1;
//    }
//    size_t node_count = 0;
//    int block_count = 1, row, col, mask2_start_row = nr, mask2_end_row = -1, mask2_start_col = nc, mask2_end_col = -1;
//    bool b_break;
//    char str[4096];
//    node_index node_ix, node_ix2;
//    queue<node_index> que;
//    vector<Mat> wrapped_phase, unwrapped_phase, qualitymap;
//    wrapped_phase.resize(n_images);
//    qualitymap.resize(n_images);
//    unwrapped_phase_series.resize(n_images);
//    for (int i = 0; i < n_images; i++)
//    {
//        wrapped_phase_series[i].copyTo(unwrapped_phase_series[i]);
//    }
//    while (node_count != num_nodes)
//    {
//        mask2_start_row = nr, mask2_end_row = -1, mask2_start_col = nc, mask2_end_col = -1;
//        b_break = false;
//        for (int i = 0; i < nr; i++)
//        {
//            for (int j = 0; j < nc; j++)
//            {
//                if (mask1.at<int>(i, j) == 1)
//                {
//                    mask2_start_row = mask2_start_row > i ? i : mask2_start_row;
//                    mask2_end_row = mask2_end_row < i ? i : mask2_end_row;
//                    mask2_start_col = mask2_start_col > j ? j : mask2_start_col;
//                    mask2_end_col = mask2_end_col < j ? j : mask2_end_col;
//
//                    mask1.at<int>(i, j) += block_count; //标注已选点并将其加入到待处理队列中
//                    node_ix.row = i; node_ix.col = j;
//                    que.push(node_ix);
//                    b_break = true;
//                    break;
//                }
//            }
//            if (b_break) break;
//        }
//
//        /*
//        * 搜索满足条件的点,并加入队列
//        */
//
//        while (!que.empty())
//        {
//            node_ix = que.front();
//            que.pop();
//            node_count++;
//
//            for (int i = 0; i <= 2 * r; i++)
//            {
//                c = c_mat.at<int>(i, 0);
//                for (int j = 0; j <= 2 * c; j++)
//                {
//                    row = node_ix.row + (i - r);
//                    row = row < 0 ? 0 : row; row = row > nr - 1 ? nr - 1 : row;
//                    col = node_ix.col + (j - c);
//                    col = col < 0 ? 0 : col; col = col > nc - 1 ? nc - 1 : col;
//                    if (mask1.at<int>(row, col) == 1)
//                    {
//                        mask2_start_row = mask2_start_row > row ? row : mask2_start_row;
//                        mask2_end_row = mask2_end_row < row ? row : mask2_end_row;
//                        mask2_start_col = mask2_start_col > col ? col : mask2_start_col;
//                        mask2_end_col = mask2_end_col < col ? col : mask2_end_col;
//
//                        mask1.at<int>(row, col) += block_count; //标注已选点
//                        node_ix2.row = row; node_ix2.col = col;
//                        que.push(node_ix2);
//                    }
//                }
//            }
//        }
//
//        /*
//        * 确定新的掩膜矩阵mask2
//        */
//
//        mask2 = Mat::zeros(mask2_end_row - mask2_start_row + 1, mask2_end_col - mask2_start_col + 1, CV_32S);
//
//        for (int i = mask2_start_row; i <= mask2_end_row; i++)
//        {
//            for (int j = mask2_start_col; j <= mask2_end_col; j++)
//            {
//                if (mask1.at<int>(i, j) == block_count + 1)
//                {
//                    mask2.at<int>(i - mask2_start_row, j - mask2_start_col) = 1;
//                }
//            }
//        }
//        mask2.convertTo(mask2, CV_64F);
//        cvmat2bin("E:\\working_dir\\projects\\software\\InSAR\\bin\\mask2.bin", mask2);
//        mask2.convertTo(mask2, CV_32S);
//        if (3 > cv::countNonZero(mask2))
//        {
//            block_count++;
//            continue;
//        }
//
//        /*
//        * 3D相位解缠
//        */
//
//        for (int i = 0; i < n_images; i++)
//        {
//            wrapped_phase_series[i](cv::Range(mask2_start_row, mask2_end_row + 1), cv::Range(mask2_start_col, mask2_end_col + 1)).copyTo
//            (wrapped_phase[i]);
//            quality_map[i](cv::Range(mask2_start_row, mask2_end_row + 1), cv::Range(mask2_start_col, mask2_end_col + 1)).copyTo
//            (qualitymap[i]);
//        }
//
//        //ret = unwrap_3D(mask2, qualitymap, wrapped_phase, unwrapped_phase, delaunay_exe_path, tmp_file_path,
//        //    distance_thresh + 1.0, quality_thresh);
//        ret = unwrap_3D_mcf(mask2, qualitymap, wrapped_phase, unwrapped_phase, delaunay_exe_path,
//            mcf_exe_path, tmp_file_path, distance_thresh);
//        if (return_check(ret, "unwrap_3D_mcf()", error_head)) return -1;
//
//        for (int i = 0; i < n_images; i++)
//        {
//            unwrapped_phase[i].copyTo(unwrapped_phase_series[i](cv::Range(mask2_start_row, mask2_end_row + 1),
//                cv::Range(mask2_start_col, mask2_end_col + 1)));
//
//            memset(str, 0, 4096);
//            sprintf(str, "H:\\data\\experiment\\test\\Regis2\\unwrapped_phase_%d.jpg", i + 1);
//            //cvmat2bin(str, unwrapped_phase[i]);
//            savephase(str, "jet", unwrapped_phase[i]);
//        }
//        block_count++;
//
//    }
//    mask1.convertTo(mask1, CV_64F);
//    cvmat2bin("E:\\working_dir\\projects\\software\\InSAR\\bin\\mask1.bin", mask1);
//    return 0;
//}

















tri_node::tri_node()
{
    this->rows = 0;
    this->cols = 0;
    this->num_neigh_edges = 0;
    this->phase = 0;
    this->b_unwrapped = false;
    this->b_balanced = true;
    this->neigh_edges = NULL;
    this->b_residue = false;
    this->epsilon_height = 0.0;
    this->vel = 0.0;
    //std::cout << "constructor1" << "\n";
}

tri_node::tri_node(const tri_node& node)
{
    this->b_unwrapped = node.b_unwrapped;
    this->b_balanced = node.b_balanced;
    this->cols = node.cols;
    this->b_residue = node.b_residue;
    int num_node = node.num_neigh_edges <= 0 ? 1 : node.num_neigh_edges;
    if (node.neigh_edges == NULL)
    {
        this->neigh_edges = NULL;
    }
    else
    {
        this->neigh_edges = (long*)malloc(sizeof(long) * num_node);
        long* ptr = NULL;
        int num;
        node.get_neigh_ptr(&ptr, &num);
        if (this->neigh_edges != NULL || num > 0 || ptr != NULL)
        {
            std::memcpy(this->neigh_edges, ptr, sizeof(long) * num_node);
        }
    }

    this->num_neigh_edges = node.num_neigh_edges;
    this->phase = node.phase;
    this->rows = node.rows;
    this->epsilon_height = node.epsilon_height;
    this->vel = node.vel;
    //std::cout << "constructor2" << "\n";
}

tri_node::tri_node(int row, int col, int num_neigh_edge, double phi)
{
    this->rows = row;
    this->cols = col;
    this->num_neigh_edges = num_neigh_edge;
    this->phase = phi;
    this->b_unwrapped = false;
    this->b_residue = false;
    this->b_balanced = true;
    this->epsilon_height = 0.0;
    this->vel = 0.0;
    if (num_neigh_edge > 0)
    {
        this->neigh_edges = (long*)malloc(sizeof(long) * num_neigh_edge);
    }
    else
    {
        this->neigh_edges = NULL;
    }
    if (this->neigh_edges != NULL)
    {
        for (int i = 0; i < num_neigh_edge; i++)
        {
            *(this->neigh_edges + i) = -1;//初始化邻接边序号都为-1
        }
    }

    //std::cout << "constructor3" << "\n";
}

tri_node::~tri_node()
{
    if (this->neigh_edges != NULL)
    {
        free(this->neigh_edges);
        this->neigh_edges = NULL;
    }
    //std::cout << "destructor" << "\n";
}

tri_node tri_node::operator=(const tri_node& src)
{
    if (src.neigh_edges == this->neigh_edges && this->neigh_edges != NULL)//两者相等
    {
        return *this;
    }
    else
    {
        if (this->neigh_edges)
        {
            free(this->neigh_edges);
            this->neigh_edges = NULL;
        }
        if (src.num_neigh_edges > 0)
        {
            this->neigh_edges = (long*)malloc(src.num_neigh_edges * sizeof(long));
            if (this->neigh_edges != NULL && src.neigh_edges != NULL)
            {
                memcpy(this->neigh_edges, src.neigh_edges, src.num_neigh_edges * sizeof(long));
            }
        }
        this->b_balanced = src.b_balanced;
        this->b_residue = src.b_residue;
        this->b_unwrapped = src.b_unwrapped;
        this->cols = src.cols;
        this->rows = src.rows;
        this->num_neigh_edges = src.num_neigh_edges;
        this->phase = src.phase;
        this->epsilon_height = src.epsilon_height;
        this->vel = src.vel;
        return *this;
    }
}

int tri_node::get_phase(double* phi) const
{
    if (phi == NULL)
    {
        fprintf(stderr, "get_phase(): input check failed!\n\n");
        return -1;
    }
    *phi = this->phase;
    return 0;
}

int tri_node::get_pos(int* rows, int* cols) const
{
    if (rows == NULL ||
        cols == NULL)
    {
        fprintf(stderr, "tri_node::get_pos(): input check failed!\n\n");
        return -1;
    }
    *rows = this->rows;
    *cols = this->cols;
    return 0;
}

int tri_node::set_phase(double phi)
{
    this->phase = phi;
    return 0;
}

int tri_node::get_neigh_ptr(long** ptr2ptr, int* num) const
{
    if (ptr2ptr == NULL || num == NULL)
    {
        fprintf(stderr, "get_neigh_ptr(): input check failed!\n\n");
        return -1;
    }
    *ptr2ptr = this->neigh_edges;
    *num = this->num_neigh_edges;
    return 0;
}

int tri_node::set_status(bool b_unwrapped)
{
    this->b_unwrapped = b_unwrapped;
    return 0;
}

int tri_node::set_balance(bool b_balanced)
{
    this->b_balanced = b_balanced;
    return 0;
}

int tri_node::print_neighbour() const
{
    if (this->num_neigh_edges <= 0)
    {
        fprintf(stdout, "no neighbour edges!\n");
        return 0;
    }
    for (int i = 0; i < this->num_neigh_edges; i++)
    {
        fprintf(stdout, "%ld ", *(this->neigh_edges + i));
    }
    fprintf(stdout, "\n");
    return 0;
}

int tri_node::get_num_neigh(int* num_neigh) const
{
    *num_neigh = this->num_neigh_edges;
    return 0;
}

int tri_node::get_distance(tri_node node, double* distance) const
{
    *distance = sqrt(((double)node.rows - (double)this->rows) * ((double)node.rows - (double)this->rows) +
        ((double)node.cols - (double)this->cols) * ((double)node.cols - (double)this->cols));
    return 0;
}

bool tri_node::get_status() const
{
    return this->b_unwrapped;
}

bool tri_node::get_balance() const
{
    return this->b_balanced;
}

bool tri_node::is_residue_node() const
{
    return this->b_residue;
}

int tri_node::set_residue(bool b_res)
{
    this->b_residue = b_res;
    return 0;
}

double tri_node::get_vel() const
{
    return this->vel;
}

double tri_node::get_height() const
{
    return this->epsilon_height;
}

int tri_node::set_vel(double vel)
{
    this->vel = vel;
    return 0;
}

int tri_node::set_height(double height)
{
    this->epsilon_height = height;
    return 0;
}
View Code

6、需要使用的头文件

 6.1:com_htzs_insar_jni_Registration.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_htzs_insar_jni_Registration */

#ifndef _Included_com_htzs_insar_jni_Registration
#define _Included_com_htzs_insar_jni_Registration
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_htzs_insar_jni_Registration
 * Method:    calcOffset
 * Signature: (I[S[S[S[S)Lcom/htzs/insar/jni/Point;
 */
JNIEXPORT jobject JNICALL Java_com_htzs_insar_jni_Registration_calcOffset
  (JNIEnv *, jclass, jint, jshortArray, jshortArray, jshortArray, jshortArray);

/*
 * Class:     com_htzs_insar_jni_Registration
 * Method:    fitting
 * Signature: ([Lcom/htzs/insar/jni/Point;[Lcom/htzs/insar/jni/Point;D)Lcom/htzs/insar/jni/Fit;
 */
JNIEXPORT jobject JNICALL Java_com_htzs_insar_jni_Registration_fitting
  (JNIEnv *, jclass, jobjectArray, jobjectArray, jdouble);

/*
 * Class:     com_htzs_insar_jni_Registration
 * Method:    registration
 * Signature: (I[S[SLcom/htzs/insar/jni/Point;Lcom/htzs/insar/jni/Fit;I)[S
 */
JNIEXPORT jshortArray JNICALL Java_com_htzs_insar_jni_Registration_registration
  (JNIEnv *, jclass, jint, jshortArray, jshortArray, jobject, jobject, jint);

#ifdef __cplusplus
}
#endif
#endif
View Code

 6.2:ComplexMat.h

#pragma once
#ifndef __COMPLEXMAT__H__
#define __COMPLEXMAT__H__
#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include"opencv2/imgproc/imgproc.hpp"
#include"opencv2/opencv.hpp"
#include <omp.h>  /*多线程计算库*/

using cv::Mat;
namespace InSAR {
    class  ComplexMat
    {
    public:
        ComplexMat();
        ComplexMat(Mat& real, Mat& imagine);
        ComplexMat(int rows, int cols);
        /*拷贝构造函数*/
        ComplexMat(const ComplexMat& b);
        ~ComplexMat();
        void SetRe(Mat& re);
        void SetIm(Mat& im);
        Mat GetRe() const;
        Mat GetIm() const;
        Mat GetMod() const;
        /*计算复矩阵的相位*/
        Mat GetPhase();
        int type() const;
        int GetRows() const;
        int GetCols() const;
        /*计算复数(共轭)点乘*/
        int Mul(const ComplexMat& Src, ComplexMat& Dst, bool bConj) const;
        /*计算复数乘积(点乘,elementwise)*/
        ComplexMat operator*(const ComplexMat& b) const;
        /*复数矩阵与实数矩阵对应相乘*/
        ComplexMat operator*(const Mat& a) const;
        /*复数矩阵乘以常数*/
        ComplexMat operator*(const double& a) const;
        /*取出部分复数矩阵*/
        ComplexMat operator()(cv::Range _rowRange, cv::Range _colRange) const;
        /*将复数矩阵部分进行赋值*/
        int SetValue(cv::Range _rowRange, cv::Range _colRange, ComplexMat& src);
        /*复数矩阵加法*/
        ComplexMat operator+(const ComplexMat& b) const;
        /*深拷贝赋值*/
        ComplexMat operator=(const ComplexMat&);
        /*复数矩阵内求和
        * 参数1 求和方向(0为沿着每列求和,1为沿着每行求和)
        */
        ComplexMat sum(int dim = 0) const;
        /*求取复共轭*/
        ComplexMat conj() const;
        /*计算非零元素个数*/
        int countNonzero() const;
        /*数组是否为空*/
        bool isempty()const;
        /*转换类型*/
        void convertTo(ComplexMat& out, int type) const;
        Mat re;
        Mat im;
    private:

        Mat mod;
        Mat Phase;
    };
}


#endif // !__COMPLEXMAT__H__
View Code

 6.3:Registration.h

#pragma once
#ifndef __REGISTRATION__H__
#define __REGISTRATION__H__

#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include"opencv2/imgproc/imgproc.hpp"
#include"opencv2/opencv.hpp"
#include <omp.h>  /*多线程计算库*/
#include<vector>

#include"ComplexMat.h"
#include"Utils.h"


using cv::Mat;
using InSAR::ComplexMat;
namespace InSAR
{
    
    class Registration
    {
    public:
        Registration();
        ~Registration();
        /*求取两幅辅图像的实相关函数
         参数1 主图像(复)
         参数2 辅图像(复)
         参数3 行偏移量(返回值)
         参数4 列偏移量(返回值)
        */
        int real_coherent(ComplexMat& Master, ComplexMat& Slave, int* offset_row, int* offset_col);
        /*2D FFTSHIFT(原地操作)*/
        int fftshift2(Mat& matrix);
        /*2D FFT
         参数1 输入矩阵
         参数2 输出矩阵
        */
        int fft2(Mat& Src, Mat& Dst);
        /*像元级配准(原地操作)
         参数1 主图像(复)(输入值/返回值)
         参数2 辅图像(复)(输入值/返回值)
         参数3 行偏移量(返回值)
         参数4 列偏移量(返回值)
        */
        int registration_pixel(ComplexMat& Master, ComplexMat& Slave, int* move_r = NULL, int* move_c = NULL);
        /*频域补零插值
         参数1 输入矩阵(复)
         参数2 输出矩阵(复)
         参数3 插值倍数(大于1, 且是2的n次幂)
        */
        int interp_paddingzero(ComplexMat& InputMatrix, ComplexMat& OutputMatrix, int interp_times);
        /*立方插值
         参数1 输入矩阵
         参数2 输出矩阵
         参数3 行偏移量
         参数4 列偏移量
        */
        int interp_cubic(ComplexMat& InputMatrix, ComplexMat& OutputMatrix, double offset_row, double offset_col);
        /*立方插值
        参数1 输入矩阵
        参数2 输出矩阵
        参数3 拟合系数
        */
        int interp_cubic(ComplexMat& InputMatrix, ComplexMat& OutputMatrix, Mat& Coefficient);
        /*计算每个像素的偏移量
         参数1 像素行号
         参数2 像素列号
         参数3 拟合系数
         参数4 行偏移量
         参数5 列偏移量
        */
        int every_subpixel_move(int i, int j, Mat& coefficient, double* offset_row, double* offset_col);
        /*计算卷积核权重*/
        double WeightCalculation(double offset);
        /*亚像素级配准
         参数1 主图像(复)
         参数2 辅图像(复)
         参数3 子块大小(大于1)
         参数4 插值倍数(大于1)
        */
        int registration_subpixel(ComplexMat& Master, ComplexMat& Slave, int blocksize, int interp_times);
        /** @brief 精配准

        @param master              主图像
        @param slave               辅图像
        @param blocksize           主图像参考块分块大小(blocksize×blocksize,blocksize为2的n次幂)
        @param interp_times        插值倍数(InSAR要求至少8倍插值)
        */
        int coregistration_subpixel(
            ComplexMat& master,
            ComplexMat& slave,
            int blocksize,
            int interp_times
        );
        /*拟合像素偏移量
         参数1 行序列号
         参数2 列序列号
         参数3 行偏移量
         参数4 列偏移量
         参数5 拟合系数(返回值)
        */
        int all_subpixel_move(Mat& Coordinate_x, Mat& Coordinate_y, Mat& offset_row, Mat& offset_col, Mat& para);
        /*根据粗配准偏移量筛选控制点
        * 参数1 原始图像行数
        * 参数2 原始图像列数
        * 参数3 行偏移
        * 参数4 列偏移
        * 参数5 控制点信息
        */
        int gcps_sift(int rows, int cols, int move_rows, int move_cols, Mat& gcps);


    private:
        char error_head[256];
        char parallel_error_head[256];

    };
}






#endif // !__REGISTRATION__H__
View Code

 6.4:Utils.h

#pragma once
#ifndef __UTILS__H__
#define __UTILS__H__
#include<fstream>
#include<iostream>
#include<vector>
#include<string>
#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include"opencv2/imgproc/imgproc.hpp"
#include"opencv2/opencv.hpp"
#include <omp.h>  /*多线程计算库*/

#include"ComplexMat.h"


#define INPUTMAXSIZE 1024
#define PI 3.141592653589793238
#define VEL_C 299792458.0

using cv::Mat;
using std::vector;
using std::string;
using InSAR::ComplexMat;

namespace InSAR
{
    /*********************************************************/
    /*                Delaunay三角网 节点类                  */
    /*********************************************************/
    class  tri_node
    {
    public:
        /*默认构造函数*/
        tri_node();
        /*拷贝构造函数*/
        tri_node(const tri_node& node);
        /*构造函数
        * 参数1 节点行数
        * 参数2 节点列数
        * 参数3 节点邻接边数
        * 参数4 节点相位
        */
        tri_node(int, int, int, double);
        ~tri_node();
        /*赋值函数(深拷贝赋值)*/
        tri_node operator = (const tri_node& src);
        /*获取节点相位
        * 参数1 相位指针(返回值)
        */
        int get_phase(double* phi) const;
        /*获取节点行列坐标
        * 参数1 行序号
        * 参数2 列序号
        */
        int get_pos(int* rows, int* cols) const;
        /*节点相位赋值
        * 参数1 输入相位
        */
        int set_phase(double phi);
        /*获取邻接边指针
        * 参数1 指向邻接边指针的指针(返回值)
        * 参数2 邻接边个数指针(返回值)
        */
        int get_neigh_ptr(long** ptr2ptr, int* num) const;
        /*改变解缠状态
        * 参数1 是否已经解缠
        */
        int set_status(bool b_unwrapped);
        /*改变平衡状态
        * 参数1 是否属于残差平衡三角形
        */
        int set_balance(bool b_balanced);
        /*打印邻接边序号
        *
        */
        int print_neighbour() const;
        /*获取邻接边个数
        * 参数1 邻接边个数指针
        */
        int get_num_neigh(int* num_neigh) const;
        /*获取与另一节点的距离
        * 参数1 另一节点
        * 参数2 距离
        */
        int get_distance(tri_node node, double* distance) const;
        /*获取解缠状态
        * 返回值(是否已解缠)
        */
        bool get_status() const;
        /*获取平衡状态
        * 返回值(是否平衡,默认是)
        */
        bool get_balance() const;
        /*返回是否节点属于残差三角形
        */
        bool is_residue_node() const;
        /*设置节点是否属于残差节点
        */
        int set_residue(bool b_res);
        /*获取形变速率*/
        double get_vel() const;
        /*获取高程误差*/
        double get_height() const;
        /*设置形变速率*/
        int set_vel(double vel);
        /*设置高程误差*/
        int set_height(double height);

    private:

        /*****************InSAR处理变量*******************/

        /*是否已解缠(默认未解缠)*/
        bool b_unwrapped;
        /*是否属于残差节点*/
        bool b_residue;
        /*是否属于平衡三角形的顶点(默认为是),同时在PS-InSAR中充当是否节点被丢弃的标志(为true表示不被丢弃, 为false表示被丢弃)*/
        bool b_balanced;
        /*节点行数(起始值为0)*/
        int rows;
        /*节点列数(起始值为0)*/
        int cols;
        /*节点邻接边数*/
        int num_neigh_edges;
        /*节点相位*/
        double phase;
        /*节点邻接边序号*/
        long* neigh_edges;

        /*****************PS-InSAR处理变量*******************/

        /*形变速率*/
        double vel;
        /*高程误差*/
        double epsilon_height;
    };

    /*********************************************************/
    /*             Delaunay三角网 三角形结构体               */
    /*********************************************************/
    struct triangle
    {
        /*三角形序号*/
        int num;
        /*点1*/
        int p1;
        /*点2*/
        int p2;
        /*点3*/
        int p3;
        /*三角形残差值*/
        double residue;
        /*相邻三角形序号1*/
        int neigh1;
        /*相邻三角形序号2*/
        int neigh2;
        /*相邻三角形序号3*/
        int neigh3;
        /*边1(从1开始)*/
        int edge1;
        /*边2(从1开始)*/
        int edge2;
        /*边3(从1开始)*/
        int edge3;

        /*默认构造函数*/
        triangle()
        {
            num = p1 = p2 = p3 = neigh1 = neigh2 = neigh3 = edge1 = edge2 = edge3 = 0;
            residue = 0.0;
        }
        /*拷贝构造函数*/
        triangle(const triangle& cp)
        {
            this->edge1 = cp.edge1;
            this->edge2 = cp.edge2;
            this->edge3 = cp.edge3;
            this->neigh1 = cp.neigh1;
            this->neigh2 = cp.neigh2;
            this->neigh3 = cp.neigh3;
            this->num = cp.num;
            this->p1 = cp.p1; this->p2 = cp.p2; this->p3 = cp.p3;
            this->residue = cp.residue;
        }
        /*赋值(深拷贝)*/
        triangle operator= (const triangle& cp)
        {
            this->edge1 = cp.edge1;
            this->edge2 = cp.edge2;
            this->edge3 = cp.edge3;
            this->neigh1 = cp.neigh1;
            this->neigh2 = cp.neigh2;
            this->neigh3 = cp.neigh3;
            this->num = cp.num;
            this->p1 = cp.p1; this->p2 = cp.p2; this->p3 = cp.p3;
            this->residue = cp.residue;
            return *this;
        }
    };


    /*********************************************************/
    /*             Delaunay三角网 三角形边结构体             */
    /*********************************************************/
    struct tri_edge
    {
        /**********InSAR变量**********/

        /*积分增益(序号从小到大为正)*/
        double gain;
        /*相位质量(用于质量图法解缠)*/
        double quality;
        /*边序列号*/
        int num;
        /*端点1*/
        int end1;
        /*端点2*/
        int end2;
        /*残差边标志*/
        bool isResidueEdge;
        /*网络边界标志*/
        bool isBoundry;


        /**********PS_InSAR变量**********/

        /*线性形变速度差系数(4 * pi / lambda * Ti)*/
        //double coef_delta_vel;
        /*高程误差系数(4 * pi * bperp_i / lambda / R_i / sin_theta_i  )*/
        //double coef_delta_height;
        /*线性形变速度差(定义为大坐标 - 小坐标)*/
        double delta_vel;
        /*高程误差(定义为大坐标 - 小坐标)*/
        double delta_height;
        /*模型相干系数*/
        double MC;
        /*端点相位差(相位差定义为:大序号端点减小序号端点)*/
        double phase_diff;

        /*默认构造函数*/
        tri_edge() {
            gain = 0.0;
            quality = 0.0;
            num = 0;
            end1 = 0; end2 = 0;
            isResidueEdge = false;
            isBoundry = false;
            delta_vel = 0.0;
            delta_height = 0.0; MC = 0.0; phase_diff = 0.0;
        }
        /*拷贝构造函数*/
        tri_edge(const tri_edge& cp)
        {
            gain = cp.gain;
            quality = cp.quality;
            num = cp.num;
            end1 = cp.end1; end2 = cp.end2;
            isResidueEdge = cp.isResidueEdge;
            isBoundry = cp.isBoundry;
            delta_vel = cp.delta_vel;
            delta_height = cp.delta_height; MC = cp.MC; phase_diff = cp.phase_diff;
        }
        /*赋值函数(深拷贝赋值)*/
        tri_edge operator = (const tri_edge& cp)
        {
            gain = cp.gain;
            quality = cp.quality;
            num = cp.num;
            end1 = cp.end1; end2 = cp.end2;
            isResidueEdge = cp.isResidueEdge;
            isBoundry = cp.isBoundry;
            delta_vel = cp.delta_vel;
            delta_height = cp.delta_height; MC = cp.MC; phase_diff = cp.phase_diff;
            return *this;
        }
    };

    /*********************************************************/
    /*          Delaunay三角网 三角形边序列号结构体          */
    /*********************************************************/

    struct edge_index
    {
        double quality;
        int num;
        edge_index() { num = 0; quality = 0.0; }
        friend bool operator < (struct edge_index a, struct edge_index b)
        {
            return a.quality > b.quality;
        }

    };

    /*-------------------------------------------------------*/
    /*                   规则网格节点结构体                  */
    /*-------------------------------------------------------*/

    struct node_index
    {
        /*节点行数(从0开始)*/
        int row;
        /*节点列数(从0开始)*/
        int col;
        /*默认构造函数*/
        node_index()
        {
            row = 0; col = 0;
        }
        /*拷贝构造函数*/
        node_index(const node_index& cp)
        {
            this->row = cp.row; this->col = cp.col;
        }
        /*赋值函数*/
        node_index operator = (const node_index& cp)
        {
            this->row = cp.row; this->col = cp.col;
            return *this;
        }
    };


    /*********************************************************/
    /*               干涉SAR处理基本函数类库                 */
    /*********************************************************/
    class  Utils
    {
    public:
        Utils();
        ~Utils();
        /** @brief 求int型矩阵的众数

        @param input                  输入矩阵(int型)
        @param out                    输出结果
        @return 成功返回0,否则返回-1
        */
        int get_mode_index(const Mat& input, int* out);
        /*计算矩阵梯度
         参数1 源矩阵
         参数2 行方向梯度(返回值)
         参数3 列方向梯度(返回值)
         参数4 是否补零使得梯度矩阵和源矩阵大小相同(默认补零)
        */
        int diff(Mat& Src, Mat& diff_1, Mat& diff_2, bool same = true);
        /*计算干涉相位
         参数1 主图像(复)
         参数2 辅图像(复)
         参数3 干涉相位(返回值)
        */
        int generate_phase(const ComplexMat& Master, const ComplexMat& Slave, Mat& phase);

        /** @brief 最大似然相干估算器

        @param master_image                       主图像(复)
        @param slave_image                        辅图像(复)
        @param coherence                          相干系数(返回值)
        @return 成功返回0,否则返回-1
        */
        int real_coherence(ComplexMat& master_image, ComplexMat& slave_image, Mat& coherence);
        /** @brief 最大似然相干估算器(带估计窗口尺寸接口)

        @param master_image                       主图像(复)
        @param slave_image                        辅图像(复)
        @param est_wndsize_rg                     估计窗口距离向尺寸(奇数)
        @param est_wndsize_az                     估计窗口方位向尺寸(奇数)
        @param coherence                          相干系数(返回值)
        */
        int real_coherence(
            const ComplexMat& master_image,
            const ComplexMat& slave_image,
            int est_wndsize_rg,
            int est_wndsize_az,
            Mat& coherence
        );
        /** @brief 频率无关相干估算器

         @param master_image                        主图像(复)
         @param slave_image                         辅图像(复)
         @param coherence                           相干系数(返回值)
         @return 成功返回0,否则返回-1
        */
        int complex_coherence(ComplexMat& master_image, ComplexMat& slave_image, Mat& coherence);
        /** @brief 频率无关相干估算器(带估计窗口尺寸接口)

        @param master_image                         主图像
        @param slave_image                          辅图像
        @param est_wndsize_rg                       估计窗口距离向尺寸(奇数)
        @param est_wndsize_az                       估计窗口方位向尺寸(奇数)
        @param coherence                            相关系数(返回值)
        @return 成功返回0,否则返回-1
        */
        int complex_coherence(
            const ComplexMat& master_image,
            const ComplexMat& slave_image,
            int est_wndsize_rg,
            int est_wndsize_az,
            Mat& coherence
        );
        /** @brief 根据干涉相位求相关系数
        @param phase                          输入相位
        @param coherence                      相关系数(返回值)
        @return 成功返回0,否则返回-1
        */
        int phase_coherence(Mat& phase, Mat& coherence);
        /** @brief 根据干涉相位求相关系数(带估计窗口尺寸接口)

        @param phase                          输入相位
        @param est_wndsize_rg                 估计窗口距离向尺寸(奇数)
        @param est_wndsize_az                 估计窗口方位向尺寸(奇数)
        @param coherence                      相关系数(返回值)
        @return 成功返回0,否则返回-1
        */
        int phase_coherence(
            const Mat& phase,
            int est_wndsize_rg,
            int est_wndsize_az,
            Mat& coherence
        );
        /*求解相位导数方差
        * 参数1 干涉相位
        * 参数2 相位导数方差(返回值)
        * 参数3 计算窗口大小(奇数)
        */
        int phase_derivatives_variance(Mat& phase, Mat& phase_derivatives_variance, int wndsize = 3);
        /*最大可积距离
        * 参数1 原始相位
        * 参数2 最大可积距离(返回值)
        * 参数3 保守值(最大积分距离不能超过该值)
        */
        int max_integrable_distance(Mat& phase, Mat& max_integrable_distance, double conservative_thresh = 20.0);
        /*FFTSHIFT
         参数1 待fftshift的矩阵(原地进行fftshift操作)
        */
        int fftshift(Mat& matrix);

        /*计算干涉相位图的残差值(点)
         参数1 干涉相位
         参数2 残差点矩阵(返回值)
        */
        int residue(Mat& phase, Mat& residue);
        /*计算Delaunay三角网络的残差值(并且标注残差边和残差节点,便于解缠时避开)
        * 参数1 Delaunay三角网三角形结构体数组
        * 参数2 Delaunay三角网三角形数量
        * 参数3 Delaunay三角网节点数组
        * 参数4 Delaunay三角网边结构体数组
        * 参数5 Delaunay三角网边数量
        */
        int residue(triangle* tri, int num_triangle, vector<tri_node>& nodes, tri_edge* edges, int num_edges);
        /** @brief 计算Delaunay三角网络的残差值(并且标注残差边和残差节点)

        @param triangle                              Delaunay三角网三角形结构体数组
        @param nodes                                 Delaunay三角网节点数组
        @param edges                                 Delaunay三角网边结构体数组
        @param distance_thresh                       边长度阈值(超过此阈值不参与残差点计算)
        @return 成功返回0,否则返回-1
        */
        int residue(
            vector<triangle>& triangle,
            vector<tri_node>& nodes,
            vector<tri_edge>& edges,
            double distance_thresh
        );
        /*计算mask(筛选高质量点)
        * 参数1 相关系数矩阵
        * 参数2 mask举矩阵(返回值)
        * 参数3 窗口半径
        * 参数4 阈值
        */
        int gen_mask(Mat& coherence, Mat& mask, int wnd_size, double thresh);
        /*计算mask(筛选高质量点)
        * 参数1 相关系数矩阵
        * 参数2 相位导数方差
        * 参数3 mask举矩阵(返回值)
        * 参数4 窗口半径
        * 参数5 相关系数阈值
        * 参数6 相位导数方差阈值
        */
        int gen_mask(
            Mat& coherence,
            Mat& phase_derivatives,
            Mat& mask, int wnd_size,
            double coh_thresh,
            double phase_derivative_thresh
        );
        /*根据设定阈值筛选残差点
        * 参数1 原始残差点矩阵
        * 参数2 筛选后残差点矩阵
        * 参数3 筛选阈值(大于0)
        * 参数4 残差点个数
        */
        int residue_sift(Mat& residue_src, Mat& residue_dst, double thresh, long* num_residue);
        /*缠绕相位至(-pi,pi)
         参数1 待缠绕相位
         参数2 缠绕后的相位(返回值)
        */
        int wrap(Mat& Src, Mat& Dst);

        /*按行或列累计积分
         参数1 待积分数据
         参数2 积分方向(dim = 1,按列计算 dim = 2,按行计算)
        */
        int cumsum(Mat& phase, int dim);
        /*叉乘运算(三维)
        * 参数1 向量一(n * 3)
        * 参数2 向量二(n * 3)
        * 参数3 输出
        */
        int cross(Mat& vec1, Mat& vec2, Mat& out);

        /*写入DIMACS文件(描述最小费用问题)
         参数1 目标文件名
         参数2 残差点矩阵
         参数3 相干系数矩阵
         参数4 残差点阈值(大于0)
        */
        int write_DIMACS(const char* DIMACS_file_problem, Mat& residue, Mat& coherence, double thresh);
        /*写入DIMACS文件(描述最小费用问题,不规则三角网络)
        * 参数1 目标文件名
        * 参数2 Delaunay三角形结构体数组
        * 参数3 Delaunay三角形数量
        * 参数4 Delaunay三角网节点数组
        * 参数5 Delaunay三角网边结构体数组
        * 参数6 Delaunay三角网边数量
        * 参数7 每个节点的费用
        */
        int write_DIMACS(
            const char* DIMACS_file_problem,
            triangle* tri,
            int num_triangle,
            vector<tri_node>& nodes,
            tri_edge* edges,
            long num_edges,
            Mat& cost
        );
        /** @brief 写入DIMACS文件(描述最小费用问题,Delaunay三角网络)

        @param DIMACS_file_problem                         目标DIMACS文件
        @param triange                                     Delaunay三角形结构体数组
        @param nodes                                       Delaunay三角网节点数组
        @param edges                                       Delaunay三角网边结构体数组
        @param cost                                        每个节点的费用
        @return 成功返回0,否则返回-1
        */
        int write_DIMACS(
            const char* DIMACS_file_problem,
            vector<triangle>& triangle,
            vector<tri_node>& nodes,
            vector<tri_edge>& edges,
            const Mat& cost
        );
        /*读取DIMACS文件(获取求解器求解结果)
         参数1 最小费用流问题解文件
         参数2 枝切路径1
         参数3 枝切路径2
         参数4 干涉相位图像行数
         参数5 干涉相位图像列数
        */
        int read_DIMACS(const char* DIMACS_file_solution, Mat& k1, Mat& k2, int rows, int cols);
        /*读取DIMACS文件(获取求解器求解结果)
        * 参数1 最小费用流问题解文件
        * 参数2 Delaunay三角网边结构体数组
        * 参数3 Delaunay三角网边数量
        * 参数4 Delaunay三角网节点数组
        * 参数5 Delaunay三角网三角形数组
        * 参数6 Delaunay三角网三角形数量
        */
        int read_DIMACS(
            const char* DIMACS_file_solution,
            tri_edge* edges,
            int num_edges,
            vector<tri_node>& nodes,
            triangle* tri,
            int num_triangle
        );
        /** @brief 读取DIMACS文件(获取求解器求解结果)

        @param DIMACS_file_solution                         最小费用流问题解文件
        @param edges                                        Delaunay三角网边结构体数组
        @param nodes                                        Delaunay三角网节点数组
        @param triangle                                     Delaunay三角网三角形数组
        @param return 成功返回0,否则返回-1
        */
        int read_DIMACS(
            const char* DIMACS_file_solution,
            vector<tri_edge>& edges,
            vector<tri_node>& nodes,
            vector<triangle>& triangle
        );
        /*将OpenCV Mat数据以二进制方式写入目标文件
        * 参数1 目标文件名
        * 参数2 待写入数据
        */
        int cvmat2bin(const char* Dst_file, Mat& Src);
        /*从二进制文件中读数据,并将数据转换成OpenCV Mat格式
        * 参数1 二进制文件
        * 参数2 目标矩阵
        */
        int bin2cvmat(const char* Src_file, Mat& Dst);
        /*InSAR多视处理(配准之后进行, 改变图像尺寸)
        * 参数1 主图像(SLC)
        * 参数2 辅图像(SLC)
        * 参数3 多视相位
        * 参数4 多视倍数(大于1)
        */
        int multilook(ComplexMat& Master, ComplexMat& Slave, Mat& phase, int multilook_times);
        /** @brief InSAR多视处理(不改变图像尺寸)

        @param master_slc                    主图像
        @param slave_slc                     辅图像
        @param multilook_rg                  距离向多视倍数
        @param multilook_az                  方位向多视倍数
        @param multilooked_phase             多视相位
        */
        int multilook(const ComplexMat& master, const ComplexMat& slave, int multilook_rg, int multilook_az, Mat& phase);
        /** @brief 将相位转换成cos和sin(实部和虚部)

        @param phase                     输入相位
        @param cos                       实部
        @param sin                       虚部
        @return 成功返回0,否则返回-1
        */
        int phase2cos(const Mat& phase, Mat& cos, Mat& sin);
        /*84坐标系转经纬高坐标系
        * 参数1 84坐标系坐标
        * 参数2 经纬高坐标系坐标(度/度/米)
        */
        int xyz2ell(Mat xyz, Mat& llh);
        /*经纬高坐标系转84坐标系
        * 参数1 经纬高坐标系坐标(纬度/经度/高度)
        * 参数2 84坐标系坐标
        */
        int ell2xyz(Mat llh, Mat& xyz);



        /*******************************************************/
        /*                     图像存储工具集                  */
        /*******************************************************/

        /*量化保存SLC功率图
        * 参数1 目标文件名
        * 参数2 功率量化参数(可视范围dB)
        * 参数3 单视复图像
        */
        int saveSLC(const char* filename, double db, ComplexMat& SLC);
        /*保存干涉相位图
        * 参数1 目标文件名
        * 参数2 颜色映射(jet/hsv/cool/parula等)
        * 参数3 待保存相位
        */
        int savephase(const char* filename, const char* colormap, Mat phase);
        /*图像重采样
        * 参数1 原图像
        * 参数2 目标图像
        * 参数3 目标图像高度
        * 参数4 目标图像宽度
        */
        int resampling(const char* Src_file, const char* Dst_file, int dst_height, int dst_width);
        /*量化SAR图像与干涉相位叠加
        * 参数1 量化SAR图像
        * 参数2 干涉相位图
        * 参数3 叠加图像
        * 参数4 SAR图像占比
        */
        int amplitude_phase_blend(
            const char* amplitude_file,
            const char* phase_file,
            const char* blended_file,
            double SAR_ratio = 0.9
        );



        /*******************************************************/
        /*                Delaunay三角网相关函数库             */
        /*******************************************************/

        /*从.edge文件读取Delaunay三角网的边信息
        * 参数1 .edge文件
        * 参数2 指向边结构体的指针(返回值,内存需要手动释放)
        * 参数3 指向边个数的指针(返回值)
        * 参数4 统计每个节点的邻接边数(返回值,内存需要手动释放)
        * 参数5 节点数
        */
        int read_edges(const char* filename, tri_edge** edges, long* num_edges, int** neighbours, long num_nodes);
        /** @brief 从.edge文件读取Delaunay三角网的边信息

        @param edge_file               .edge文件
        @param num_nodes               节点数
        @param edges                   Delaunay三角网边数组(返回值)
        @param node_neighbours         每个节点的邻接边数(返回值)
        @return  成功返回0, 否则返回-1
        */
        int read_edges(
            const char* edge_file,
            vector<tri_edge>& edges,
            vector<int>& node_neighbours,
            long num_nodes
        );
        /*初始化Delaunay三角网节点
        * 参数1 节点数组(返回值)
        * 参数2 相位(double型)
        * 参数3 相位mask(int 型)
        * 参数4 edges结构体数组
        * 参数5 edges个数
        * 参数6 每个节点的邻接边信息
        * 参数7 节点数
        */
        int init_tri_node(
            vector<tri_node>& node_array,
            Mat& phase,
            Mat& mask,
            tri_edge* edges,
            long num_edges,
            int* num_neighbour,
            int num_nodes
        );
        /** @brief 初始化Delaunay三角网节点

        @param node_array                 节点数组(返回值)
        @param phase                      相位值
        @param mask                       相位掩膜
        @param edges                      Delaunay三角网络边结构体数组
        @param node_neighbours            每个节点的邻边个数
        @param num_nodes                  节点数
        @return 成功返回0,否则返回-1
        */
        int init_tri_node(
            vector<tri_node>& node_array,
            const Mat& phase,
            const Mat& mask,
            const vector<tri_edge>& edges,
            const vector<int>& node_neighbours,
            int num_nodes
        );
        /** @brief 初始化Delaunay三角网络边相位差

        @param edges                  Delaunay三角网络边数组(已经使用read_edges函数初始化过的)
        @param node_array             Delaunay三角网络节点数组(已经使用init_tri_node函数初始化过的)
        @return 成功返回0,否则返回-1
        */
        int init_edge_phase_diff(
            vector<tri_edge>& edges,
            const vector<tri_node>& node_array
        );
        /*初始化Delaunay三角网边的相位质量
        * 参数1 相位质量图
        * 参数2 Delaunay三角网边结构体数组指针
        * 参数3 Delaunay三角网边结构体数组大小
        * 参数4 Delaunay三角网节点数组
        */
        int init_edges_quality(Mat& quality, tri_edge* edges, int num_edges, vector<tri_node>& nodes);
        /** @brief 初始化Delaunay三角网边的相位质量指数

        @param quality_index                  相位质量图指数(与相位质量相反)
        @param edges                          Delaunay三角网边结构体数组
        @param nodes                          Delaunay三角网节点数组
        @return 成功返回0, 否则返回-1
        */
        int init_edges_quality(
            const Mat& quality_index,
            vector<tri_edge>& edges,
            const vector<tri_node>& nodes
        );
        /*从.ele文件和.neigh文件读取Delaunay三角网的三角形信息
        * 参数1 .ele文件
        * 参数2 .neigh文件
        * 参数3 三角形结构体数组指针(返回值, 内存需要手动释放)
        * 参数4 三角形个数(返回值)
        * 参数5 Delaunay三角网节点数组
        * 参数6 Delaunay三角网边数组
        * 参数7 Delaunay三角网边数量
        */
        int read_triangle(
            const char* ele_file,
            const char* neigh_file,
            triangle** tri,
            int* num_triangle,
            vector<tri_node>& nodes,
            tri_edge* edges,
            int num_edgs
        );
        /** @brief 从.ele文件和.neigh文件读取Delaunay三角网的三角形信息

        @param ele_file                        .ele文件
        @param neigh_file                      .neigh文件
        @param triangle                        三角形结构体数组(返回值)
        @param nodes                           Delaunay三角网节点数组
        @param edges                           Delaunay三角网边数组
        @return 成功返回0,否则返回-1
        */
        int read_triangle(
            const char* ele_file,
            const char* neigh_file,
            vector<triangle>& triangle,
            vector<tri_node>& nodes,
            vector<tri_edge>& edges
        );
        /*写.node文件
        * 参数1 .node文件
        * 参数2 节点数组
        */
        int write_node_file(const char* filename, const Mat& mask);




        /*********************************************************/
        /*                PS-InSAR 常用函数                      */
        /*********************************************************/

        /*振幅离差指数法筛选PS点(D_A)
        * 参数1 SAR幅度矩阵组
        * 参数2 振幅离差阈值
        * 参数3 mask(满足条件的PS点位置mask为1,其他为0)
        */
        int PS_amp_dispersion(const vector<Mat>& amplitude, double thresh, Mat& mask);
        /*fifth-order butterworth filter(五阶巴特沃斯滤波器)
        * 参数1 grid_size
        * 参数2 n_win
        * 参数3 low_pass_wavelength
        * 参数4 滤波器系数(返回值)
        */
        int butter_lowpass(int grid_size, int n_win, double low_pass_wavelength, Mat& lowpass);
        /*circle_shift
        */
        int circshift(Mat& out, const cv::Point& delta);
        /*fftshift2
        */
        int fftshift2(Mat& out);
        /*ifftshift
        */
        int ifftshift(Mat& out);
        /*二维傅里叶变换
        * 参数1 输入矩阵
        * 参数2 输出结果
        */
        int fft2(Mat& Src, Mat& Dst);
        /*复数二维傅里叶变换
        * 参数1 输入矩阵
        * 参数2 输出结果
        */
        int fft2(ComplexMat& src, ComplexMat& dst);
        /*逆二维傅里叶变换
        * 参数1 输入矩阵
        * 参数2 输出结果
        */
        int ifft2(ComplexMat& src, ComplexMat& dst);
        /*求标准差
        * 参数1 输入矩阵
        * 参数2 标准差返回值
        */
        int std(const Mat& input, double* std);

        /*SAR图像干涉相位序列去平地
        * 参数1 干涉相位序列(原地操作)
        * 参数2 干涉组合
        * 参数3 卫星轨道参数(插值后, n_images×6)
        * 参数4 地面控制点信息(纬度/经度/高度/行/列, n_gcps × 5)
        * 参数5 SAR图像左上角在原SAR图像中的行数(可以直接设置为1)
        * 参数6 SAR图像左上角在原SAR图像中的列数(可以直接设置为1)
        * 参数7 收发方式(1单发单收, 2单发双收)
        * 参数8 波长
        */
        int PS_deflat(
            vector<Mat>& interf_phase,
            Mat& interf_combination,
            vector<Mat>& pos,
            vector<Mat>& gcps,
            Mat& start_row,
            Mat& start_col,
            int mode,
            double lambda
        );
        /*去平地(线性拟合法)
        * 参数1 干涉相位(原地操作)
        * 参数2 主星轨道参数(插值后, n_images×6)
        * 参数3 辅星轨道参数(插值后, n_images×6)
        * 参数4 地面控制点信息(纬度/经度/高度/行/列, n_gcps × 5)
        * 参数5 SAR图像左上角在原SAR图像中的行数(可以直接设置为1)
        * 参数6 SAR图像左上角在原SAR图像中的列数(可以直接设置为1)
        * 参数7 收发方式(1单发单收, 2单发双收)
        * 参数8 波长
        */
        int _PS_deflat(
            Mat& phase,
            Mat& pos1,
            Mat& pos2,
            Mat& gcps,
            int start_row,
            int start_col,
            int mode,
            double lambda
        );





        /*
        * 卫星轨道插值
        * 参数1:卫星轨道参数(未插值)
        * 参数2:插值时间间隔(s)
        * 参数3:插值结果
        */
        int stateVec_interp(Mat& stateVec, double time_interval, Mat& stateVec_interp);

        /** @brief 坐标转换工具函数

        @param coefficient       转换系数矩阵
        @param coord_in_1        原坐标矩阵1(1和2的顺序很重要,经度/行坐标在前)
        @param coord_in_2        原坐标矩阵2
        @param coord_out         转换结果矩阵
        */
        int coord_conversion(
            Mat& coefficient,
            Mat& coord_in_1,
            Mat& coord_in_2,
            Mat& coord_out
        );

        /** @brief 统计同质检验

        @param pixel1            待检验像元1幅度序列(size: n_images×1)
        @param pixel2            待检验像元2幅度序列(size: n_images×1)
        @param homo_flag         是否为同质像元(返回0则为同质像元,-1则为非同质像元)
        @param alpha             显著性水平(可以设定的值为 0.20,0.15,0.10,0.05,0.025,0.01,0.005,0.001。默认为0.05)
        @param method            检验方法("KS":Kolmogorov-Smirnov检验,"AD":Anderson-Darling检验, 默认为KS检验)
        @return                  正常运行返回0,报错返回-1
        */
        int homogeneous_test(
            const Mat& pixel1,
            const Mat& pixel2,
            int* homo_flag,
            double alpha = 0.05,
            const char* method = "KS"
        );

        /** @brief 时序SAR图像复相关矩阵估计

        @param slc_series               slc数据堆栈
        @param coherence_matrix         相关矩阵(复数, 返回值)
        @param est_window_width         估计窗口宽度(奇数)
        @param est_window_height        估计窗口高度(奇数)
        @param ref_row                  (若进行统计同质检验)参考点行坐标,不进行同质检验则不需要此参数
        @param ref_col                  (若进行统计同质检验)参考点列坐标,不进行同质检验则不需要此参数
        @param b_homogeneous_test       是否进行统计同质检验(同质检验参考像素默认为中间点像素)
        @param b_normalize              估计相关矩阵时slc序列是否归一化处理
        @return                         成功返回0,否则返回-1
        */
        int coherence_matrix_estimation(
            const vector<ComplexMat>& slc_series,
            ComplexMat& coherence_matrix,
            int est_window_width,
            int est_window_height,
            int ref_row,
            int ref_col,
            bool b_homogeneous_test = true,
            bool b_normalize = true
        );

        /** @brief 区域生长法解缠(delaunay三角网)

        @param nodes                       Delaunay三角网络节点数组
        @param edges                       Delaunay三角网络边结构体数组
        @param start_edge                  积分起始边序号(从1开始)
        @param distance_thresh             边长阈值,超过此阈值不通过此边积分
        @param quality_thresh              质量阈值,低于此阈值不通过此边积分
        @return 成功返回0,否则返回-1
        */
        int unwrap_region_growing(
            vector<tri_node>& nodes,
            const vector<tri_edge>& edges,
            size_t start_edge,
            double distance_thresh,
            double quality_thresh
        );

    private:
        char error_head[256];
        char parallel_error_head[256];

    };

}

#endif
View Code

7、g++ `pkg-config --cflags opencv4` -c Registration.cpp com_htzs_insar_jni_Registration.cpp ComplexMat.cpp Utils.cpp -I /opt/module/jdk1.8.0_212/include/ -I /opt/module/jdk1.8.0_212/include/linux/ -fPIC -std=c++11(编译生成o文件)

8、g++ -shared com_htzs_insar_jni_Registration.o Utils.o ComplexMat.o Registration.o -o libRegistration.so `pkg-config --libs opencv4`(编译生成so文件)

注:当然也可以使用CMake来编译生成动态链接库

 9、查看是否依赖了opencv的lib库(ldd -r libRegistration.so)

 10:最后将Java程序生成jar在linux上运行

package com.htzs.insar.jni;

import com.htzs.insar.utils.ArrayUtil;

import java.io.*;
import java.io.FileOutputStream;
import java.nio.*;

/**
 * 测试类
 * @author ywb
 * @createdDate 2022/1/4 14:48
 * @updatedDate
 */
public class Main {
    static {
//        System.load("E:\\test\\inSAR_src\\x64\\Debug\\inSAR_src.dll");
//        System.loadLibrary("InSAR_src7");
        System.load("/usr/lib/libRegistration.so");
    }

    public static void main(String[] arg) throws FileNotFoundException {
        Point[] p = new Point[19*19];
        Point[] center_coord = new Point[19*19];
        int width = 512;
        int height = 512;
        int master_original_width = 10000;
        int slave_original_width = 10000;
        int offset_row, offset_col;
        int count = 0;
        short[] master_real;
        short[] master_imag;
        short[] slave_real;
        short[] slave_imag;
        String master_real_file = "/home/htzs/data/master_real.dat";
        String master_imag_file = "/home/htzs/data/master_imag.dat";
        String slave_real_file = "/home/htzs/data/slave_real.dat";
        String slave_imag_file = "/home/htzs/data/slave_imag.dat";
//        String master_real_file = "E:\\outputDat\\master_real.dat";
//        String master_imag_file = "E:\\outputDat\\master_imag.dat";
//        String slave_real_file = "E:\\outputDat\\slave_real.dat";
//        String slave_imag_file = "E:\\outputDat\\slave_imag.dat";
        try {
            for (int i = 0; i < 19; i++) {
                for (int j = 0; j < 19; j++) {
                    offset_row = i * height + 1;
                    offset_col = j * width + 1;
                    master_real = getSLice(master_real_file, offset_row, offset_col, master_original_width, height, width);
                    master_imag = getSLice(master_imag_file, offset_row, offset_col, master_original_width, height, width);
                    slave_real = getSLice(slave_real_file, offset_row, offset_col, slave_original_width, height, width);
                    slave_imag = getSLice(slave_imag_file, offset_row, offset_col, slave_original_width, height, width);
                    p[count] = Registration.calcOffset(width, master_real, master_imag, slave_real, slave_imag);
                    center_coord[count] = new Point();
                    center_coord[count].setRow(offset_row - 1 + 0.5 * height);
                    center_coord[count].setCol(offset_col - 1 + 0.5 * width);
                    System.out.println("(" + i + "," + j + "):" + p[count].getRow() + " , " + p[count].getCol() + " , " + p[count].getCoherence());
//                    System.out.println(p[count].getRow());
//                    System.out.println(p[count].getCol());
//                    System.out.println("相关系数:" + p[count].getCoherence());
//                    writeDoubleArray("D:\\image\\cut\\master_real\\" + i  +"_"+ j + "_master_real.dat", master_real);
//                    writeDoubleArray("D:\\image\\cut\\master_imag\\" + i  +"_"+ j  + "_master_imag.dat", master_imag);
//                    writeDoubleArray("D:\\image\\cut\\slave_real\\" + i  +"_"+ j  + "_slave_real.dat", slave_real);
//                    writeDoubleArray("D:\\image\\cut\\slave_imag\\" + i  +"_"+ j  + "_slave_imag.dat", slave_imag);
                    count++;
                }
            }

            Fit fit = Registration.fitting(p, center_coord, 0.5);
            System.out.println("Fit:" + fit.getA0() + "," + fit.getA1()+ "," + fit.getA2()+ ","+ fit.getA3()+ "," + fit.getA4()+ ","  + fit.getA5()+ ",B:,"+ fit.getB0() + "," + fit.getB1()+ "," + fit.getB2()+ ","+ fit.getB3()+ "," + fit.getB4()+ ","  + fit.getB5());
            Point cen = new Point();
            cen.setRow(1536);
            cen.setCol(1536);
            slave_real = getSLice(slave_real_file, 1, 1, slave_original_width, height * 3, width * 3);
            slave_imag = getSLice(slave_imag_file, 1, 1, slave_original_width, height * 3, width * 3);
            double[] buf = Registration.registration(width * 3, slave_real, slave_imag, cen, fit, 0);
            System.out.println(buf.length);
        } catch (InSAR_JNIException e) {
            System.out.println(e.getMessage());
            return;
        }

    }

    /**
     * 从图像文件中切片
     *
     * @param imageFile      图像文件
     * @param offset_row     切片左上角在图像中的行数
     * @param offset_col     切片左上角在图像中的列数
     * @param original_width 原始图像宽度
     * @param height         切片高度
     * @param width          切片宽度
     * @return :切片数组
     **/
    public static short[] getSLice(
            String imageFile,
            int offset_row,
            int offset_col,
            int original_width,
            int height,
            int width
    ) {
        short[] slice = new short[height * width];
        int offset = ((offset_row - 1) * original_width + offset_col - 1) * Short.BYTES;
        byte[] byteBuf = new byte[width * Short.BYTES];
        short[] temp;
        int offset1 = 0;
        File file = new File(imageFile);
        try {
            RandomAccessFile fr = new RandomAccessFile(file, "rw");
            for (int i = 0; i < height; i++) {
                fr.seek(offset);
                fr.read(byteBuf, 0, width * Short.BYTES);
                temp = ArrayUtil.convertToShorts(byteBuf);
                System.arraycopy(temp, 0, slice, offset1, width);
                offset = offset + original_width * Short.BYTES;
                offset1 += width;
            }
            fr.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return slice;
    }

    /**
     * 将byte数组转换为double数组
     *
     * @param b 输入字节数组
     * @return 返回double数组
     **/
    public static double[] getDouble(byte[] b) {
        double[] ret = new double[b.length / 8];
        for (int i = 0; i < b.length / 8; i++) {
            long m;
            m = b[8 * i];
            m &= 0xff;
            m |= ((long) b[1 + 8 * i] << 8);
            m &= 0xffff;
            m |= ((long) b[2 + 8 * i] << 16);
            m &= 0xffffff;
            m |= ((long) b[3 + 8 * i] << 24);
            m &= 0xffffffffL;
            m |= ((long) b[4 + 8 * i] << 32);
            m &= 0xffffffffffL;
            m |= ((long) b[5 + 8 * i] << 40);
            m &= 0xffffffffffffL;
            m |= ((long) b[6 + 8 * i] << 48);
            m &= 0xffffffffffffffL;
            m |= ((long) b[7 + 8 * i] << 56);
            ret[i] = Double.longBitsToDouble(m);
        }

        return ret;
    }

    public static ByteBuffer asByteBuffer(ShortBuffer input) {
        if (null == input) {
            return null;
        }
        ByteBuffer buffer = ByteBuffer.allocate(input.capacity() * (Short.BYTES));
        while (input.hasRemaining()) {
            buffer.putDouble(input.get());
        }
        return buffer;
    }

    public static byte[] asByteArray(short[] input) {
        if (null == input) {
            return null;
        }
        return asByteBuffer(ShortBuffer.wrap(input)).array();
    }

    public static void writeDoubleArray(
            String imageFile,
            short[] indata
    ) throws FileNotFoundException {

        try {
            DataOutputStream out = new DataOutputStream(new FileOutputStream(imageFile, true));
            byte[] bytearray = asByteArray(indata);
            out.write(bytearray, 0, bytearray.length);
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

计算偏移量运行结果:

posted @ 2022-07-20 17:54  yiwanbin  阅读(769)  评论(0编辑  收藏  举报