前言
這一系列的文章已經寫了第二篇了,所以這個系列將會轉變爲連載文章,每當我有什麼新的發現,都會更新。
本文demo地址: github.com/chouheiwa/O…
正文
現在關於OpenCV的很多有趣的例子,都是python的。
這篇文章的整體思路來源於 知乎 的 Maker畢 的文章: 蔡徐坤教你用OpenCV實現素描效果
上一篇文章中我們已經講述過了,圖像的存儲,以及一些相關的信息。這篇文章就不會重複了,如果不是很清楚的讀者可以看看第一篇文章。
這篇文章說是素描,其實與廣義素描差距很大,準確的說應該是叫邊框畫。
先上一下效果圖吧。
<figcaption></figcaption>
看起來是不是挺有意思的
步驟及原理
這裏我們還是要先講述一下步驟,這裏先展示下原圖
1. 將給定圖片轉灰度圖
轉成灰度圖片的過程是爲了消除其他影響因子(這一步也是很多圖片處理|文字識別等相關領域的第一步)。
將圖片從原來的三維層面,降到一維。
- (UIImage *)grayImage:(UIImage *)image {
cv::Mat cvImage;
UIImageToMat(image, cvImage);
cv::Mat gray;
// 將圖像轉換爲灰度顯示
cv::cvtColor(cvImage, gray, CV_RGB2GRAY);
cvImage.release();
// 將灰度圖片轉成UIImage
UIImage *nImage = MatToUIImage(gray);
gray.release();
return nImage;
}
處理完畢後,我們能看到原來的蔡老師變灰了。
2. 對灰度圖片進行高斯模糊
首先,先來講一下如何進行簡單的 模糊 處理
在上一篇文章中我們已經講過了,圖片其實就是一個二維數組。
所以圖片上的每一個像素,都有一個像素數值。
我們可以以當前像素點爲中心,取一個n * n的矩陣。
這裏假定我們選了一箇中心灰度值爲190的像素點,它的周邊像素的像素灰度值爲100(255爲純白色)的3*3的像素矩陣
模糊處理的簡單形式就是做平均,也就是將中間點的像素點和周圍8個像素點的灰度值取平均值。也就是(100 * 8 + 190) / 9 = 110
簡單的模糊處理就是這麼做的,不過高斯模糊是通過高斯函數去進行相應的計算,這裏我找到了一篇相當好的文章: 高斯模糊
- (UIImage *)gaussianblurImage:(UIImage *)image {
cv::Mat cvImage;
UIImageToMat(image, cvImage);
cv::Mat blur;
// 選取一個5 * 5 的核用於模糊
cv::GaussianBlur(cvImage, blur, cv::Size(5, 5), 0);
cvImage.release();
UIImage *blurImage = MatToUIImage(blur);
blur.release();
return blurImage;
}
有一個模糊的蔡老師出現了
3. 對圖像進行自適應二值化處理
這一步其實要講的就是二值化,其實他的概念很簡單。我們將灰度圖上的某一個像素點的灰度值與給定的一個值進行比較,小於這個給定值的我們將其灰度值設置爲0(黑色),大於的設置爲255(白色)。我們給定的比較值被稱之爲閾值
當然,這種二值化太過固化、死板。因爲真實的照片有可能有陰影之類的遮擋,會導致我們的全局二值化,產生很多的誤差,如下圖右上角所示:
因此我們需要採用自適應二值化的方法,這裏我們選擇採用自適應高斯二值化(效果如上圖右下角)
- (UIImage *)adaptiveThresholdImage:(UIImage *)image {
cv::Mat cvImage;
UIImageToMat(image, cvImage);
cv::Mat outImage;
cv::adaptiveThreshold(cvImage, outImage,
255,
cv::ADAPTIVE_THRESH_GAUSSIAN_C, // 這裏我們採用的是高斯自適應模糊
cv::THRESH_BINARY, // 二值化
5,
2);
cvImage.release();
UIImage *adaImage = MatToUIImage(outImage);
outImage.release();
return adaImage;
}
蔡老師的線條出現啦
4. 二值化圖片進行再次模糊
現在蔡老師的衣服都已經變成線條了,基礎的描邊效果已經達成。但是我們可以看到,圖片中比如說地面上,還有一些黑色的我們並不想要的點(我們稱這些點爲噪點)。以及蔡老師的線條還是有點細,所以我們需要將蔡老師的線條變粗些。
將上面的圖片再次進行高斯模糊。
蔡老師變得模糊了
5. 對模糊圖片再次進行二值化
這裏我們再次進行二值化操作,因爲現在圖片已經相對乾淨,且並無陰影等干擾項。我們可以直接使用全局二值化來加深邊框了(計算速度快)。
- (UIImage *)thresholdImage:(UIImage *)image {
cv::Mat cvImage;
UIImageToMat(image, cvImage);
cv::Mat outImage;
// 因爲這時的圖片已經比較乾淨且沒什麼陰影,所以選擇普通二值化,灰度值 > 200 (這個值可以調,我覺得220效果更好) 的就賦值爲255(白色)
cv::threshold(cvImage, outImage, 200, 255, cv::THRESH_BINARY);
cvImage.release();
UIImage *threImage = MatToUIImage(outImage);
outImage.release();
return threImage;
}
6. 對圖片進行噪點去除
現在需要去除圖片中的小的噪點,我們就需要進行一系列的操作了
關於這些操作,我們在圖像處理方面有專門的名詞描述:
腐蝕 與 膨脹
腐蝕:
腐蝕通俗的來說,就是將原本的圖像根據給定的核(爲我們自定義的一種形狀,一般爲n*n的正方形,n爲奇數)縮小。
只有當原本的圖像上對應核心周圍所有的點都有值時,我們才保留當前核心的值。
膨脹:
膨脹則正好相反,我們將給定的圖片根據給定的核放大。
當我們掃描核的任意一點上有值時,當前核心點將會被賦值
腐蝕和膨脹便是我們這步處理的關鍵。
它們之間的組合被我們稱之爲開運算和閉運算
開運算
我們先對圖片進行腐蝕運算,然後進行膨脹運算
最終效果將如上圖的左下角結果
我們和原圖進行比較可以發現。
開運算可以去除毛刺,小橋和孤立的小點(在腐蝕運算中小點會直接消失)。最終總的位置和形狀不變(膨脹運算會恢復)
閉運算
閉運算這裏因爲我們不會用到,因此不會過多贅述。
它和開運算的過程相反,先對原圖像進行膨脹運算後進行腐蝕運算。
我們的目的是處理圖片中的一些噪點,因此我們採用開運算來處理。
- (UIImage *)morphologyImage:(UIImage *)image {
cv::Mat cvImage;
UIImageToMat(image, cvImage);
// 將圖片取反,原黑變白,原白變黑
cv::bitwise_not(cvImage, cvImage);
cv::Mat outImage;
/// 獲取一個3*3的核
cv::Mat ken = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
/// 進行圖像的開運算(開運算需要對有數值的地方進行縮小,所以我們需要將圖片反色,即大部分有數值,而小部分沒有,才能達到效果)
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. 最後進行一次高斯模糊
我們最後在進行一次高斯模糊,使我們的圖像效果更好。
其他
視頻的轉換,這裏就不多寫了(正在研究過程中...)
這篇文章的對應demo請點擊網址,如果大家覺得還不錯,請盡情的用你麼的star來砸我。