OpenCV:如何将图像垂直拼接成长图像而不是全景图/水平

Posted

技术标签:

【中文标题】OpenCV:如何将图像垂直拼接成长图像而不是全景图/水平【英文标题】:OpenCV: How to stitch images into a long image vertically instead of panoramas/horizontally 【发布时间】:2021-11-18 07:55:42 【问题描述】:

我尝试创建一个可以垂直拼接 iPhone 屏幕截图的应用程序,如下图所示。它会忽略图像数组的重叠部分,并以正确的顺序缝合独特的部分。

我在互联网上查看了一些教程,我认为openCV 框架可以完成这项工作,因为它支持拼接图像全景/水平。所以我将这个 pod 集成到我的项目中,并使其适用于全景图像拼接。

但是我在垂直拼接图像时遇到了麻烦。从CVWrapper.mm 我可以看到processWithArray 这个方法会旋转并从图像数组创建新的拼接图像。目前,我在数组中的所有图像的方向都是up

我的问题:是否可以改变这个方向,所以让openCV 从上到下而不是从左到右缝合图像?我有点迷失在那些方向上,现在不知道我应该从哪里开始。任何提示/建议将不胜感激。

CVWrapper.mm

+ (UIImage*) processWithArray:(NSArray*)imageArray

    if ([imageArray count]==0)
        NSLog (@"imageArray is empty");
        return 0;
        
    std::vector<cv::Mat> matImages;

    for (id image in imageArray) 
        NSLog(@"image orientation: %ld", (long)[image imageOrientation]);
        
        if ([image isKindOfClass: [UIImage class]]) 
            /*
             All images taken with the iPhone/iPa cameras are LANDSCAPE LEFT orientation. The  UIImage imageOrientation flag is an instruction to the OS to transform the image during display only. When we feed images into openCV, they need to be the actual orientation that we expect them to be for stitching. So we rotate the actual pixel matrix here if required.
             */
            UIImage* rotatedImage = [image rotateToImageOrientation];
            cv::Mat matImage = [rotatedImage CVMat3];
            NSLog (@"matImage: %@",image);
            matImages.push_back(matImage);
        
    
    NSLog (@"stitching...");
    cv::Mat stitchedMat = stitch (matImages);
    UIImage* result =  [UIImage imageWithCVMat:stitchedMat];
    return result;


UIImage+Rotate.m

- (UIImage *)rotateToImageOrientation 

    // No-op if the orientation is already correct
    if (self.imageOrientation == UIImageOrientationUp) return self;
    
    // We need to calculate the proper transformation to make the image upright.
    // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
    CGAffineTransform transform = CGAffineTransformIdentity;
    
    switch (self.imageOrientation) 
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;
            
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            transform = CGAffineTransformTranslate(transform, self.size.width, 0);
            transform = CGAffineTransformRotate(transform, M_PI_2);
            break;
            
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, 0, self.size.height);
            transform = CGAffineTransformRotate(transform, -M_PI_2);
            break;
        case UIImageOrientationUp:
        case UIImageOrientationUpMirrored:
            break;
    
    
    switch (self.imageOrientation) 
        case UIImageOrientationUpMirrored:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, self.size.width, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
            
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, self.size.height, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
        case UIImageOrientationUp:
        case UIImageOrientationDown:
        case UIImageOrientationLeft:
        case UIImageOrientationRight:
            break;
    
    
    // Now we draw the underlying CGImage into a new context, applying the transform
    // calculated above.
    CGContextRef ctx = CGBitmapContextCreate(NULL, self.size.width, self.size.height,
                                             CGImageGetBitsPerComponent(self.CGImage), 0,
                                             CGImageGetColorSpace(self.CGImage),
                                             CGImageGetBitmapInfo(self.CGImage));
    CGContextConcatCTM(ctx, transform);
    switch (self.imageOrientation) 
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            // Grr...
            CGContextDrawImage(ctx, CGRectMake(0,0,self.size.height,self.size.width), self.CGImage);
            break;
            
        default:
            CGContextDrawImage(ctx, CGRectMake(0,0,self.size.width,self.size.height), self.CGImage);
            break;
    
    
    // And now we just create a new UIImage from the drawing context
    CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
    UIImage *img = [UIImage imageWithCGImage:cgimg];
    CGContextRelease(ctx);
    CGImageRelease(cgimg);
    return img;

视图控制器

    @objc func stitchButtonTapped(sender: UIButton) 
        for imageView in [self.imageView1, self.imageView2, self.imageView3] 
            imageView.isHidden = true
        

        DispatchQueue.global().async  [weak self] in
            guard let images = self?.images else  return 
            let stitchedImage = CVWrapper.process(with: images)

            DispatchQueue.main.async 
                guard let imageView = self?.longImageView else  return 
                UIView.transition(with: imageView,
                                  duration: 1.6,
                                  options: .transitionCrossDissolve,
                                  animations: 
                                    self?.longImageView.image = stitchedImage
                                    self?.longImageView.isHidden = false
                                  ,
                                  completion: nil)
            
        
    

原3截图

拼接新的长图

【问题讨论】:

【参考方案1】:

好吧,我已经解决了这个问题。简而言之,在处理它们之前,我将每个图像逆时针旋转 90 度。所以我从不在UIImage+Rotate.m中使用方法rotateToImageOrientation

现在,这个拼接器正在处理大多数应用内屏幕截图,但仍有人无法工作。详情请至my blog

【讨论】:

以上是关于OpenCV:如何将图像垂直拼接成长图像而不是全景图/水平的主要内容,如果未能解决你的问题,请参考以下文章

CV实战 | 使用OpenCV进行图像全景拼接

opencv如何将两幅图片拼接

OpenCV:全景拼接 - 忽略图像的一部分

Opencv图像拼接或全景

OpenCv 3d 拼接全景图

OpenCV-Python 图像全景拼接stitch及黑边处理