iOS-OpenCV之蔡徐坤教你玩轉(zhuǎn)邊框畫

前言

這一系列的文章已經(jīng)寫了第二篇了,所以這個(gè)系列將會(huì)轉(zhuǎn)變?yōu)檫B載文章,每當(dāng)我有什么新的發(fā)現(xiàn),都會(huì)更新。

本文demo地址: github.com/chouheiwa/O…

正文

現(xiàn)在關(guān)于OpenCV的很多有趣的例子,都是python的。

這篇文章的整體思路來(lái)源于 知乎Maker畢 的文章: 蔡徐坤教你用OpenCV實(shí)現(xiàn)素描效果

上一篇文章中我們已經(jīng)講述過(guò)了,圖像的存儲(chǔ),以及一些相關(guān)的信息。這篇文章就不會(huì)重復(fù)了,如果不是很清楚的讀者可以看看第一篇文章。

這篇文章說(shuō)是素描,其實(shí)與廣義素描差距很大,準(zhǔn)確的說(shuō)應(yīng)該是叫邊框畫。

先上一下效果圖吧。

image

<figcaption></figcaption>

看起來(lái)是不是挺有意思的

步驟及原理

這里我們還是要先講述一下步驟,這里先展示下原圖

1. 將給定圖片轉(zhuǎn)灰度圖

轉(zhuǎn)成灰度圖片的過(guò)程是為了消除其他影響因子(這一步也是很多圖片處理|文字識(shí)別等相關(guān)領(lǐng)域的第一步)。

將圖片從原來(lái)的三維層面,降到一維。


- (UIImage *)grayImage:(UIImage *)image {
    cv::Mat cvImage;

    UIImageToMat(image, cvImage);

    cv::Mat gray;
    // 將圖像轉(zhuǎn)換為灰度顯示
    cv::cvtColor(cvImage, gray, CV_RGB2GRAY);

    cvImage.release();
    // 將灰度圖片轉(zhuǎn)成UIImage
    UIImage *nImage = MatToUIImage(gray);

    gray.release();

    return nImage;
}

處理完畢后,我們能看到原來(lái)的蔡老師變灰了。

2. 對(duì)灰度圖片進(jìn)行高斯模糊

首先,先來(lái)講一下如何進(jìn)行簡(jiǎn)單的 模糊 處理

在上一篇文章中我們已經(jīng)講過(guò)了,圖片其實(shí)就是一個(gè)二維數(shù)組。

所以圖片上的每一個(gè)像素,都有一個(gè)像素?cái)?shù)值。

我們可以以當(dāng)前像素點(diǎn)為中心,取一個(gè)n * n的矩陣。

這里假定我們選了一個(gè)中心灰度值為190的像素點(diǎn),它的周邊像素的像素灰度值為100(255為純白色)的3*3的像素矩陣

模糊處理的簡(jiǎn)單形式就是做平均,也就是將中間點(diǎn)的像素點(diǎn)和周圍8個(gè)像素點(diǎn)的灰度值取平均值。也就是(100 * 8 + 190) / 9 = 110

簡(jiǎn)單的模糊處理就是這么做的,不過(guò)高斯模糊是通過(guò)高斯函數(shù)去進(jìn)行相應(yīng)的計(jì)算,這里我找到了一篇相當(dāng)好的文章: 高斯模糊

- (UIImage *)gaussianblurImage:(UIImage *)image {
    cv::Mat cvImage;

    UIImageToMat(image, cvImage);

    cv::Mat blur;
    // 選取一個(gè)5 * 5 的核用于模糊
    cv::GaussianBlur(cvImage, blur, cv::Size(5, 5), 0);
    cvImage.release();
    UIImage *blurImage = MatToUIImage(blur);
    blur.release();

    return blurImage;
}

有一個(gè)模糊的蔡老師出現(xiàn)了

3. 對(duì)圖像進(jìn)行自適應(yīng)二值化處理

這一步其實(shí)要講的就是二值化,其實(shí)他的概念很簡(jiǎn)單。我們將灰度圖上的某一個(gè)像素點(diǎn)的灰度值與給定的一個(gè)值進(jìn)行比較,小于這個(gè)給定值的我們將其灰度值設(shè)置為0(黑色),大于的設(shè)置為255(白色)。我們給定的比較值被稱之為閾值

當(dāng)然,這種二值化太過(guò)固化、死板。因?yàn)檎鎸?shí)的照片有可能有陰影之類的遮擋,會(huì)導(dǎo)致我們的全局二值化,產(chǎn)生很多的誤差,如下圖右上角所示:

因此我們需要采用自適應(yīng)二值化的方法,這里我們選擇采用自適應(yīng)高斯二值化(效果如上圖右下角)

- (UIImage *)adaptiveThresholdImage:(UIImage *)image {
    cv::Mat cvImage;

    UIImageToMat(image, cvImage);

    cv::Mat outImage;

    cv::adaptiveThreshold(cvImage, outImage,
                          255,
                          cv::ADAPTIVE_THRESH_GAUSSIAN_C, // 這里我們采用的是高斯自適應(yīng)模糊
                          cv::THRESH_BINARY, // 二值化
                          5,
                          2);

    cvImage.release();

    UIImage *adaImage = MatToUIImage(outImage);

    outImage.release();

    return adaImage;
}

蔡老師的線條出現(xiàn)啦

4. 二值化圖片進(jìn)行再次模糊

現(xiàn)在蔡老師的衣服都已經(jīng)變成線條了,基礎(chǔ)的描邊效果已經(jīng)達(dá)成。但是我們可以看到,圖片中比如說(shuō)地面上,還有一些黑色的我們并不想要的點(diǎn)(我們稱這些點(diǎn)為噪點(diǎn))。以及蔡老師的線條還是有點(diǎn)細(xì),所以我們需要將蔡老師的線條變粗些。

將上面的圖片再次進(jìn)行高斯模糊。

蔡老師變得模糊了

5. 對(duì)模糊圖片再次進(jìn)行二值化

這里我們?cè)俅芜M(jìn)行二值化操作,因?yàn)楝F(xiàn)在圖片已經(jīng)相對(duì)干凈,且并無(wú)陰影等干擾項(xiàng)。我們可以直接使用全局二值化來(lái)加深邊框了(計(jì)算速度快)。

- (UIImage *)thresholdImage:(UIImage *)image {
    cv::Mat cvImage;

    UIImageToMat(image, cvImage);

    cv::Mat outImage;
    // 因?yàn)檫@時(shí)的圖片已經(jīng)比較干凈且沒(méi)什么陰影,所以選擇普通二值化,灰度值 > 200 (這個(gè)值可以調(diào),我覺(jué)得220效果更好) 的就賦值為255(白色)
    cv::threshold(cvImage, outImage, 200, 255, cv::THRESH_BINARY);

    cvImage.release();

    UIImage *threImage = MatToUIImage(outImage);

    outImage.release();

    return threImage;
}

6. 對(duì)圖片進(jìn)行噪點(diǎn)去除

現(xiàn)在需要去除圖片中的小的噪點(diǎn),我們就需要進(jìn)行一系列的操作了

關(guān)于這些操作,我們?cè)趫D像處理方面有專門的名詞描述:

腐蝕膨脹

腐蝕:

腐蝕通俗的來(lái)說(shuō),就是將原本的圖像根據(jù)給定的核(為我們自定義的一種形狀,一般為n*n的正方形,n為奇數(shù))縮小。

只有當(dāng)原本的圖像上對(duì)應(yīng)核心周圍所有的點(diǎn)都有值時(shí),我們才保留當(dāng)前核心的值。

膨脹:

膨脹則正好相反,我們將給定的圖片根據(jù)給定的核放大。

當(dāng)我們掃描核的任意一點(diǎn)上有值時(shí),當(dāng)前核心點(diǎn)將會(huì)被賦值

腐蝕膨脹便是我們這步處理的關(guān)鍵。

它們之間的組合被我們稱之為開(kāi)運(yùn)算閉運(yùn)算

開(kāi)運(yùn)算

我們先對(duì)圖片進(jìn)行腐蝕運(yùn)算,然后進(jìn)行膨脹運(yùn)算

最終效果將如上圖的左下角結(jié)果

我們和原圖進(jìn)行比較可以發(fā)現(xiàn)。

開(kāi)運(yùn)算可以去除毛刺,小橋和孤立的小點(diǎn)(在腐蝕運(yùn)算中小點(diǎn)會(huì)直接消失)。最終總的位置和形狀不變(膨脹運(yùn)算會(huì)恢復(fù))

閉運(yùn)算

閉運(yùn)算這里因?yàn)槲覀儾粫?huì)用到,因此不會(huì)過(guò)多贅述。

它和開(kāi)運(yùn)算的過(guò)程相反,先對(duì)原圖像進(jìn)行膨脹運(yùn)算后進(jìn)行腐蝕運(yùn)算。

我們的目的是處理圖片中的一些噪點(diǎn),因此我們采用開(kāi)運(yùn)算來(lái)處理。

- (UIImage *)morphologyImage:(UIImage *)image {
    cv::Mat cvImage;

    UIImageToMat(image, cvImage);
    // 將圖片取反,原黑變白,原白變黑
    cv::bitwise_not(cvImage, cvImage);

    cv::Mat outImage;
    /// 獲取一個(gè)3*3的核
    cv::Mat ken = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
    /// 進(jìn)行圖像的開(kāi)運(yùn)算(開(kāi)運(yùn)算需要對(duì)有數(shù)值的地方進(jìn)行縮小,所以我們需要將圖片反色,即大部分有數(shù)值,而小部分沒(méi)有,才能達(dá)到效果)
    cv::morphologyEx(cvImage, outImage, cv::MORPH_OPEN, ken);

    ken.release();

    cvImage.release();

    cv::bitwise_not(outImage, outImage);

    UIImage *morphologyImage = MatToUIImage(outImage);

    outImage.release();

    return morphologyImage;
}

圖片干凈了很多

7. 最后進(jìn)行一次高斯模糊

我們最后在進(jìn)行一次高斯模糊,使我們的圖像效果更好。

其他

視頻的轉(zhuǎn)換,這里就不多寫了(正在研究過(guò)程中...)

這篇文章的對(duì)應(yīng)demo請(qǐng)點(diǎn)擊網(wǎng)址,如果大家覺(jué)得還不錯(cuò),請(qǐng)盡情的用你么的star來(lái)砸我。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容