在OpenCV C ++中全景到微小的星球

问题描述

我尝试使用C ++和OpenCV将全景图转换为微小的星球,但图像结果嘈杂。我不太确定我做错了哪一部分。我认为这与颜色有关。

我尝试使用C ++和OpenCV将全景图转换为微小的星球,但图像结果嘈杂。我不太确定我做错了哪一部分。我认为这与颜色有关。

我提到的教程 http://codeofthedamned.com/index.php/the-little-planet-effect

全景源

enter image description here

小图像结果

enter image description here

#import <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h>
#import "OpenCVWrapper.h"

using namespace cv;

@implementation OpenCVWrapper

+ (UIImage*)createTinyPlanetFromImage: (UIImage*)image {
    Mat pano;
    UIImagetoMat(image,pano);
    
    Mat grayMat;
    RenderProjection(pano,1000.0,grayMat);
    
    return MatToUIImage(grayMat);
}

void RenderProjection(Mat &pano,long len,Mat &output) {
    const double k_pi = 3.1415926535897932384626433832795;
    const double k_pi_inverse = 0.31830988618379067153776752674503;
    
    output.create(len,len,CV_16UC3);
    long half_len = len / 2;
    cv::Size sz = pano.size();
    
    for (long indexX = 0; indexX < len; ++indexX) {
        for (long indexY = 0; indexY < len; ++indexY) {
            double sphereX = (indexX - half_len) * 10.0 / len;
            double sphereY = (indexY - half_len) * 10.0 / len;
            double Qx,Qy,Qz;

            if (GetIntersection(sphereX,sphereY,Qx,Qz)) {
                double theta  = std::acos(Qz);
                double phi    = std::atan2(Qy,Qx) + k_pi;
                theta         = theta * k_pi_inverse;
                phi           = phi * (0.5 * k_pi_inverse);
                double Sx     = min(sz.width -2.0,sz.width  * phi);
                double Sy     = min(sz.height-2.0,sz.height * theta);

                output.at<Vec3s>(int(indexY),int(indexX)) = BilinearSample(pano,Sx,Sy);
            }
        }
    }
}

bool GetIntersection(double u,double v,double &x,double &y,double &z) {
  double Nx    = 0.0;
  double Ny    = 0.0;
  double Nz    = 1.0;
  double dir_x = u - Nx;
  double dir_y = v - Ny;
  double dir_z = -1.0 - Nz;
 
  double a = (dir_x * dir_x) + (dir_y * dir_y) + (dir_z * dir_z);
  double b = (dir_x * Nx) + (dir_y * Ny) + (dir_z * Nz);
 
  b *= 2;
  double d = b * b;
  double q = -0.5 * (b - std::sqrt(d));
 
  double t = q / a;
 
  x = (dir_x * t) + Nx;
  y = (dir_y * t) + Ny;
  z = (dir_z * t) + Nz;
 
  return true;
}

Vec3s BilinearSample(Mat &image,double x,double y) {
    Vec3s c00 = image.at<Vec3s>(int(y),int(x));
    Vec3s c01 = image.at<Vec3s>(int(y),int(x) + 1);
    Vec3s c10 = image.at<Vec3s>(int(y) + 1,int(x));
    Vec3s c11 = image.at<Vec3s>(int(y) + 1,int(x) + 1);

    double X0 = x - floor(x);
    double X1 = 1.0 - X0;
    double Y0 = y - floor(y);
    double Y1 = 1.0 - Y0;

    double w00 = X0 * Y0;
    double w01 = X1 * Y0;
    double w10 = X0 * Y1;
    double w11 = X1 * Y1;

    short r = short(c00[2] * w00 + c01[2] * w01
                 + c10[2] * w10 + c11[2] * w11);
    short g = short(c00[1] * w00 + c01[1] * w01
                 + c10[1] * w10 + c11[1] * w11);
    short b = short(c00[0] * w00 + c01[0] * w01
                 + c10[0] * w10 + c11[0] * w11);

    return make_BGR(b,g,r);
}

Vec3s make_BGR(short blue,short green,short red) {
  Vec3s result;
  result[0] = blue;
  result[1] = green;
  result[2] = red;
 
  return result;
}

@end

解决方法

当我替换UIImageToMat(image,pano);时,问题解决了;和MatToUIImage(grayMat);使用此代码,我们可以删除此标头#import

static void UIImageToMat2(UIImage *image,cv::Mat &mat) {
    assert(image.size.width > 0 && image.size.height > 0);
    assert(image.CGImage != nil || image.CIImage != nil);

    // Create a pixel buffer.
    NSInteger width = image.size.width;
    NSInteger height = image.size.height;
    cv::Mat mat8uc4 = cv::Mat((int)height,(int)width,CV_8UC4);

    // Draw all pixels to the buffer.
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    if (image.CGImage) {
        // Render with using Core Graphics.
        CGContextRef contextRef = CGBitmapContextCreate(mat8uc4.data,mat8uc4.cols,mat8uc4.rows,8,mat8uc4.step,colorSpace,kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault);
        CGContextDrawImage(contextRef,CGRectMake(0,width,height),image.CGImage);
        CGContextRelease(contextRef);
    } else {
        // Render with using Core Image.
        static CIContext* context = nil; // I do not like this declaration contains 'static'. But it is for performance.
        if (!context) {
            context = [CIContext contextWithOptions:@{ kCIContextUseSoftwareRenderer: @NO }];
        }
        CGRect bounds = CGRectMake(0,height);
        [context render:image.CIImage toBitmap:mat8uc4.data rowBytes:mat8uc4.step bounds:bounds format:kCIFormatRGBA8 colorSpace:colorSpace];
    }
    CGColorSpaceRelease(colorSpace);

    // Adjust byte order of pixel.
    cv::Mat mat8uc3 = cv::Mat((int)width,(int)height,CV_8UC3);
    cv::cvtColor(mat8uc4,mat8uc3,cv::COLOR_RGBA2BGR);

    mat = mat8uc3;
}

static UIImage *MatToUIImage2(cv::Mat &mat) {

    // Create a pixel buffer.
    assert(mat.elemSize() == 1 || mat.elemSize() == 3);
    cv::Mat matrgb;
    if (mat.elemSize() == 1) {
        cv::cvtColor(mat,matrgb,cv::COLOR_GRAY2RGB);
    } else if (mat.elemSize() == 3) {
        cv::cvtColor(mat,cv::COLOR_BGR2RGB);
    }

    // Change a image format.
    NSData *data = [NSData dataWithBytes:matrgb.data length:(matrgb.elemSize() * matrgb.total())];
    CGColorSpaceRef colorSpace;
    if (matrgb.elemSize() == 1) {
        colorSpace = CGColorSpaceCreateDeviceGray();
    } else {
        colorSpace = CGColorSpaceCreateDeviceRGB();
    }
    CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
    CGImageRef imageRef = CGImageCreate(matrgb.cols,matrgb.rows,8 * matrgb.elemSize(),matrgb.step.p[0],kCGImageAlphaNone|kCGBitmapByteOrderDefault,provider,NULL,false,kCGRenderingIntentDefault);
    UIImage *image = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    CGDataProviderRelease(provider);
    CGColorSpaceRelease(colorSpace);

    return image;
}