warpPerspectiveを使って画像を透視変換して拡大したい

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 973

akito0705

score 11

参考サイトを用いて写真の中にある名刺(紙)を拡大、抽出するアプリを作成したいと考えています。
実機で試したところ、透視変換の関数warpPerspectiveの使い方がよくわからず、一色に塗り潰された画像が出力されてしまいます。
短形取得、描画まではできているのですが、その先のcontours->point2fへの変換など座標の扱い方がわかりません。src,dstに何を入れればいいのか、間違えがあればその指摘をお願いします。
問題の部分はOpenCVWrapper.mmの一番下の部分です。

よろしくお願いします。

import UIKit

class ViewController: UIViewController, UIImagePickerControllerDelegate,
UINavigationControllerDelegate{

    @IBOutlet var cameraView : UIImageView!
    @IBOutlet var label : UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = "Tap the [Start] to take a picture"


    }

    // カメラの撮影開始
    @IBAction func startCamera(_ sender : AnyObject) {

        let sourceType:UIImagePickerController.SourceType =
            UIImagePickerController.SourceType.camera
        // カメラが利用可能かチェック
        if UIImagePickerController.isSourceTypeAvailable(
            UIImagePickerController.SourceType.camera){
            // インスタンスの作成
            let cameraPicker = UIImagePickerController()
            cameraPicker.sourceType = sourceType
            cameraPicker.delegate = self
            self.present(cameraPicker, animated: true, completion: nil)

        }
        else{
            label.text = "error"

        }
    }

    // 撮影が完了時した時に呼ばれる
    func imagePickerController(_ imagePicker: UIImagePickerController,
                               didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]){

        if let pickedImage = info[.originalImage]
            as? UIImage {

            cameraView.contentMode = .scaleAspectFit
            cameraView.image = pickedImage

        }

        //閉じる処理
        imagePicker.dismiss(animated: true, completion: nil)
        label.text = "Tap the [Save] to save a picture"

    }

    // 撮影がキャンセルされた時に呼ばれる
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        picker.dismiss(animated: true, completion: nil)
        label.text = "Canceled"
    }


    // 写真を保存
    @IBAction func savePicture(_ sender : AnyObject) {
        let image:UIImage! = cameraView.image

        if image != nil {
            UIImageWriteToSavedPhotosAlbum(
                image,
                self,
                #selector(ViewController.image(_:didFinishSavingWithError:contextInfo:)),
                nil)
        }
        else{
            label.text = "image Failed !"
        }

    }

    // 書き込み完了結果の受け取り
    @objc func image(_ image: UIImage,
                     didFinishSavingWithError error: NSError!,
                     contextInfo: UnsafeMutableRawPointer) {

        if error != nil {
            print(error.code)
            label.text = "Save Failed !"
        }
        else{
            label.text = "Save Succeeded"
        }
    }

    // アルバムを表示
    @IBAction func showAlbum(_ sender : AnyObject) {
        let sourceType:UIImagePickerController.SourceType =
            UIImagePickerController.SourceType.photoLibrary

        if UIImagePickerController.isSourceTypeAvailable(
            UIImagePickerController.SourceType.photoLibrary){
            // インスタンスの作成
            let cameraPicker = UIImagePickerController()
            cameraPicker.sourceType = sourceType
            cameraPicker.delegate = self
            self.present(cameraPicker, animated: true, completion: nil)

            label.text = "Tap the [Start] to save a picture"
        }
        else{
            label.text = "error"

        }

    }

    //画像変換
    @IBAction func photochange(_ sender : AnyObject) {
        cameraView.image = OpenCVWrapper.makeGray(from: cameraView.image)
    }

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

@implementation OpenCVWrapper

+(NSString *)openCVVersionString{
    return [NSString stringWithFormat: @"openCV Version %s", CV_VERSION];
}

+(UIImage * )makeGrayFromImage:(UIImage *)image{
    cv::Mat imageMat;
    UIImageToMat(image, imageMat);

    if(imageMat.channels() == 1)return image;

    cv::Mat grayMat;
    cv::cvtColor(imageMat, grayMat, cv::COLOR_BGR2GRAY);
    cv::threshold(grayMat, grayMat, 200, 255, cv::THRESH_TOZERO_INV);
    cv::bitwise_not(grayMat, grayMat);
    cv::threshold(grayMat, grayMat, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
    cv::Point finalpt;

    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;
    cv::findContours(grayMat, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_TC89_L1);
    int max_level = 0;
    for(int i = 0; i < contours.size(); i++) {
        // ある程度の面積が有るものだけに絞る
        double a = contourArea(contours[i],false);
        if(a > 15000) {
            //輪郭を直線近似する
            std::vector<cv::Point> approx;
            cv::approxPolyDP(cv::Mat(contours[i]), approx, 0.01 * cv::arcLength(contours[i], true), true);
            // 矩形のみ取得
            if (approx.size() == 4) {
                cv::drawContours(imageMat, contours, i, cv::Scalar(255, 0, 0, 255), 3, 16, hierarchy, max_level);
            }
        }
    }

    cv::Point2f src[4]; // 変換元
    cv::Point2f dst[4]; // 変換先
    cv::Mat perspective_matrix = cv::getPerspectiveTransform(src, dst);
    cv::warpPerspective(imageMat, imageMat, perspective_matrix, imageMat.size(), cv::INTER_LINEAR);
    return MatToUIImage(imageMat);
}

@end
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>



@interface OpenCVWrapper : NSObject
+(NSString * ) openCVVersionString;

+(UIImage * )makeGrayFromImage:(UIImage * )image;
@end

参考サイト
https://dev.classmethod.jp/smartphone/avfoundation-opencv-findcontours/
https://i-app-tec.com/ios/camera.html

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

0

(私の知らない言語ですが)コードを見た感じ,
src,dst共に,まともな値が入れられていないように見えます.

  • srcには元の画像上における名刺の四隅の座標(approxPolyDPで得られた座標かな)
  • dstには,「拡大、抽出」した結果の四隅座標(例えば,変換結果用の画像バッファの四隅の座標とか)

を入れたら良い感じになりませんか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/28 00:28

    回答ありがとうございます。
    srcに入れる座標ですが、contoursからsrcへ入れたいのですが、std::vector<std::vector<cv::Point>>からPoint2fに入れる方法がわかりません。
    また、dstですが、これは最大サイズにしたいのですが指定する座標がわかりません。
    よろしくお願いします。

    キャンセル

  • 2018/11/28 10:13

    > srcに入れる座標
    approxが4点の座標なのではないのでしょうか?

    > dst
    出力画像の四隅の座標を使うだけではダメなのですか?
    (リファレンスを見た感じだとwarpPerspectiveはインプレースで使えないっぽいので結果用の画像を用意する必要があるように思います)

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.21%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる