iOS 使用 openCV 拼接图像的问题
Issue with stitching images using openCV for iOS
我正在尝试采用此处的代码:
https://github.com/foundry/OpenCVStitch
进入我的程序。但是,我 运行 碰壁了。此代码将已存在的图像拼接在一起。我正在尝试制作的程序会将用户拍摄的图像拼接在一起。我得到的错误是,当我将图像传递给拼接函数时,它说它们的大小无效 (0 x 0)。
这里是拼接函数:
- (IBAction)stitchImages:(UIButton *)sender {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray* imageArray = [NSArray arrayWithObjects:
chosenImage, chosenImage2, nil];
UIImage* stitchedImage = [CVWrapper processWithArray:imageArray]; // error occurring within processWithArray function
dispatch_async(dispatch_get_main_queue(), ^{
NSLog (@"stitchedImage %@",stitchedImage);
UIImageView *imageView = [[UIImageView alloc] initWithImage:stitchedImage];
self.imageView = imageView;
[self.scrollView addSubview:imageView];
self.scrollView.backgroundColor = [UIColor blackColor];
self.scrollView.contentSize = self.imageView.bounds.size;
self.scrollView.maximumZoomScale = 4.0;
self.scrollView.minimumZoomScale = 0.5;
self.scrollView.contentOffset = CGPointMake(-(self.scrollView.bounds.size.width-self.imageView.bounds.size.width)/2, -(self.scrollView.bounds.size.height-self.imageView.bounds.size.height)/2);
[self.spinner stopAnimating];
});
});
}
chosenImage 和 chosenImage2 是用户使用这两个函数拍摄的图像:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
savedImage = info[UIImagePickerControllerOriginalImage];
// display photo in the correct UIImageView
switch(image_location){
case 1:
chosenImage = info[UIImagePickerControllerOriginalImage];
self.imageView2.image = chosenImage;
image_location++;
break;
case 2:
chosenImage2 = info[UIImagePickerControllerOriginalImage];
self.imageView3.image = chosenImage2;
image_location--;
break;
}
// if user clicked "take photo", it should save photo
// if user clicked "select photo", it should not save photo
/*if (should_save){
UIImageWriteToSavedPhotosAlbum(chosenImage, nil, nil, nil);
}*/
[picker dismissViewControllerAnimated:YES completion:NULL];
}
- (IBAction)takePhoto:(UIButton *)sender {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = NO;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
//last_pressed = 1;
should_save = 1;
[self presentViewController:picker animated:YES completion:NULL];
}
stitchesImages 函数将这两个图像的数组传递给此函数:
+ (UIImage*) processWithArray:(NSArray*)imageArray
{
if ([imageArray count]==0){
NSLog (@"imageArray is empty");
return 0;
}
cv::vector<cv::Mat> matImages;
for (id image in imageArray) {
if ([image isKindOfClass: [UIImage class]]) {
cv::Mat matImage = [image CVMat3];
NSLog (@"matImage: %@",image);
matImages.push_back(matImage);
}
}
NSLog (@"stitching...");
cv::Mat stitchedMat = stitch (matImages); // error occurring within stitch function
UIImage* result = [UIImage imageWithCVMat:stitchedMat];
return result;
}
这就是程序 运行 遇到问题的地方。当它传递本地保存在应用程序文件中的图像时,它工作正常。但是,当传递保存在变量(chosenImage 和 chosenImage2)中的图像时,它不起作用。
这是在 processWithArray 函数中调用并导致错误的缝合函数:
cv::Mat stitch (vector<Mat>& images)
{
imgs = images;
Mat pano;
Stitcher stitcher = Stitcher::createDefault(try_use_gpu);
Stitcher::Status status = stitcher.stitch(imgs, pano);
if (status != Stitcher::OK)
{
cout << "Can't stitch images, error code = " << int(status) << endl;
//return 0;
}
return pano;
}
错误是"Can't stitch images, error code = 1"。
您正在达到内存限制。包含的四个演示图像为 720 x 960 像素,而您使用的是来自设备相机的全分辨率图像。
这是导致崩溃的仪器中的分配跟踪,拼接来自相机的两张图像...
这个 github 示例的目的是说明一些事情...
(1) 如何将 openCV 与 iOS;
集成
(2) 如何使用包装器分离Objective-C和C++代码;
(3)如何在openCV中实现最基本的拼接功能。
最好将其视为 iOS+openCV 的 'hello world' 项目,并且其设计目的不是为了稳健地处理相机图像。如果您想按原样使用我的代码,我建议您首先将相机图像缩小到可管理的大小(例如,长边最多 1000 张)。
无论如何,您使用的 openCV 框架与项目一样古老。感谢您的问题,我刚刚更新了它(现在是 arm64 友好的),尽管内存限制仍然适用。
V2,OpenCVSwiftStitch 可能是您实验的更有趣的起点 - 界面是用 Swift 编写的,它使用 cocoaPods 来跟上 openCV 版本(尽管目前固定为2.4.9.1 因为 2.4.10 破坏了一切)。所以它仍然说明了这三点,并且还展示了如何使用 Objective-C 包装器作为中介将 Swift 与 C++ 一起使用。
我也许可以改进内存处理(通过传递指针)。如果是这样,我会将更新推送到 v1 和 v2。如果你可以做出任何改进,请发送拉取请求。
更新 我又看了一眼,我很确定如果不深入研究 openCV 拼接算法,就不可能改进内存处理。图像已经分配到堆上,因此无需对其进行改进。我希望最好的选择是平铺和缓存 openCV 似乎作为过程的一部分创建的中间图像。如果我对此有任何进一步了解,我将 post 进行更新。同时,调整相机图像的大小是可行的方法。
更新 2
一段时间后,我找到了问题的根本原因。当您使用来自 iOS 相机的图像作为输入时,如果这些图像是纵向的,它们将具有不正确的 openCV 输入尺寸(和方向)。这是因为所有 iOS 相机照片都是原生拍摄的 'landscape left'。像素尺寸是横向的,右侧是主页按钮。要显示纵向,'imageOrientation' 标志设置为 UIImageOrientationRight。这 只是 指示 OS 将图像向右旋转 90 度以进行显示。
图像存储时未旋转,横向左侧。不正确的像素方向会导致更高的内存需求,unpredictable/broken 会导致 openCV。
我在最新版本的 openCV 中有 fixed thisSwiftStitch:当必要的图像在添加到 openCV 管道之前按像素旋转时。
我正在尝试采用此处的代码:
https://github.com/foundry/OpenCVStitch
进入我的程序。但是,我 运行 碰壁了。此代码将已存在的图像拼接在一起。我正在尝试制作的程序会将用户拍摄的图像拼接在一起。我得到的错误是,当我将图像传递给拼接函数时,它说它们的大小无效 (0 x 0)。
这里是拼接函数:
- (IBAction)stitchImages:(UIButton *)sender {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray* imageArray = [NSArray arrayWithObjects:
chosenImage, chosenImage2, nil];
UIImage* stitchedImage = [CVWrapper processWithArray:imageArray]; // error occurring within processWithArray function
dispatch_async(dispatch_get_main_queue(), ^{
NSLog (@"stitchedImage %@",stitchedImage);
UIImageView *imageView = [[UIImageView alloc] initWithImage:stitchedImage];
self.imageView = imageView;
[self.scrollView addSubview:imageView];
self.scrollView.backgroundColor = [UIColor blackColor];
self.scrollView.contentSize = self.imageView.bounds.size;
self.scrollView.maximumZoomScale = 4.0;
self.scrollView.minimumZoomScale = 0.5;
self.scrollView.contentOffset = CGPointMake(-(self.scrollView.bounds.size.width-self.imageView.bounds.size.width)/2, -(self.scrollView.bounds.size.height-self.imageView.bounds.size.height)/2);
[self.spinner stopAnimating];
});
});
}
chosenImage 和 chosenImage2 是用户使用这两个函数拍摄的图像:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
savedImage = info[UIImagePickerControllerOriginalImage];
// display photo in the correct UIImageView
switch(image_location){
case 1:
chosenImage = info[UIImagePickerControllerOriginalImage];
self.imageView2.image = chosenImage;
image_location++;
break;
case 2:
chosenImage2 = info[UIImagePickerControllerOriginalImage];
self.imageView3.image = chosenImage2;
image_location--;
break;
}
// if user clicked "take photo", it should save photo
// if user clicked "select photo", it should not save photo
/*if (should_save){
UIImageWriteToSavedPhotosAlbum(chosenImage, nil, nil, nil);
}*/
[picker dismissViewControllerAnimated:YES completion:NULL];
}
- (IBAction)takePhoto:(UIButton *)sender {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = NO;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
//last_pressed = 1;
should_save = 1;
[self presentViewController:picker animated:YES completion:NULL];
}
stitchesImages 函数将这两个图像的数组传递给此函数:
+ (UIImage*) processWithArray:(NSArray*)imageArray
{
if ([imageArray count]==0){
NSLog (@"imageArray is empty");
return 0;
}
cv::vector<cv::Mat> matImages;
for (id image in imageArray) {
if ([image isKindOfClass: [UIImage class]]) {
cv::Mat matImage = [image CVMat3];
NSLog (@"matImage: %@",image);
matImages.push_back(matImage);
}
}
NSLog (@"stitching...");
cv::Mat stitchedMat = stitch (matImages); // error occurring within stitch function
UIImage* result = [UIImage imageWithCVMat:stitchedMat];
return result;
}
这就是程序 运行 遇到问题的地方。当它传递本地保存在应用程序文件中的图像时,它工作正常。但是,当传递保存在变量(chosenImage 和 chosenImage2)中的图像时,它不起作用。
这是在 processWithArray 函数中调用并导致错误的缝合函数:
cv::Mat stitch (vector<Mat>& images)
{
imgs = images;
Mat pano;
Stitcher stitcher = Stitcher::createDefault(try_use_gpu);
Stitcher::Status status = stitcher.stitch(imgs, pano);
if (status != Stitcher::OK)
{
cout << "Can't stitch images, error code = " << int(status) << endl;
//return 0;
}
return pano;
}
错误是"Can't stitch images, error code = 1"。
您正在达到内存限制。包含的四个演示图像为 720 x 960 像素,而您使用的是来自设备相机的全分辨率图像。
这是导致崩溃的仪器中的分配跟踪,拼接来自相机的两张图像...
这个 github 示例的目的是说明一些事情...
(1) 如何将 openCV 与 iOS;
集成
(2) 如何使用包装器分离Objective-C和C++代码;
(3)如何在openCV中实现最基本的拼接功能。
最好将其视为 iOS+openCV 的 'hello world' 项目,并且其设计目的不是为了稳健地处理相机图像。如果您想按原样使用我的代码,我建议您首先将相机图像缩小到可管理的大小(例如,长边最多 1000 张)。
无论如何,您使用的 openCV 框架与项目一样古老。感谢您的问题,我刚刚更新了它(现在是 arm64 友好的),尽管内存限制仍然适用。
V2,OpenCVSwiftStitch 可能是您实验的更有趣的起点 - 界面是用 Swift 编写的,它使用 cocoaPods 来跟上 openCV 版本(尽管目前固定为2.4.9.1 因为 2.4.10 破坏了一切)。所以它仍然说明了这三点,并且还展示了如何使用 Objective-C 包装器作为中介将 Swift 与 C++ 一起使用。
我也许可以改进内存处理(通过传递指针)。如果是这样,我会将更新推送到 v1 和 v2。如果你可以做出任何改进,请发送拉取请求。
更新 我又看了一眼,我很确定如果不深入研究 openCV 拼接算法,就不可能改进内存处理。图像已经分配到堆上,因此无需对其进行改进。我希望最好的选择是平铺和缓存 openCV 似乎作为过程的一部分创建的中间图像。如果我对此有任何进一步了解,我将 post 进行更新。同时,调整相机图像的大小是可行的方法。
更新 2
一段时间后,我找到了问题的根本原因。当您使用来自 iOS 相机的图像作为输入时,如果这些图像是纵向的,它们将具有不正确的 openCV 输入尺寸(和方向)。这是因为所有 iOS 相机照片都是原生拍摄的 'landscape left'。像素尺寸是横向的,右侧是主页按钮。要显示纵向,'imageOrientation' 标志设置为 UIImageOrientationRight。这 只是 指示 OS 将图像向右旋转 90 度以进行显示。
图像存储时未旋转,横向左侧。不正确的像素方向会导致更高的内存需求,unpredictable/broken 会导致 openCV。
我在最新版本的 openCV 中有 fixed thisSwiftStitch:当必要的图像在添加到 openCV 管道之前按像素旋转时。