官术网_书友最值得收藏!

3.2 用簡單的矩陣操作處理圖像

3.2.1 對圖像進行復制與粘貼

如果我們想在上面的圖片中加入其他元素,應該如何做呢?比如在3.1節的圖中再加入一個球,順便添加一些文字。

這里要加入一個球的話,由于圖中已經有球了,所以實際上從圖中復制即可。我們注意到在這張342×548的圖中,球的縱坐標在300左右、橫坐標在360左右,大小約為60像素,可以用numpy的數組切片img[280:340, 330:390] ,得到一個子矩陣,繼而給原圖中的其他位置賦值。添加文字的話,在查詢opencv的文檔后,可以用cv2.putText實現這一功能,如圖3-4所示。

圖3-4 復制足球并添加文字

3.2.2 把圖像當成矩陣進行處理——二維碼轉換成矩陣

3.2.1小節進行的復制與粘貼只是處理矩陣的最簡單操作。本書將講一些稍微難一點的操作,就是將二維碼轉換成矩陣。當然,這里只是將二維碼轉換成0/1的矩陣而已,至于變成矩陣后如何從矩陣提取其中的內容,大家感興趣的話可以進一步研究。

我們以如圖3-5所示的景略集智數據科學的官方主頁(jizhi.im)二維碼為例,這里實際上是由25×25個像素點組成的,每個像素點或黑或白,但是這張圖片并不止25×25個像素點,而是220×220個,那么問題就來了,如何將220×220個點映射到25×25個點的坐標上去?

圖3-5 原始大小(220,220)二維碼

我們首先讀取圖片,并轉換為[0,255]的灰度值編碼:

    img_jizhiQR = cv2.imread("./3.png")
    img_jizhiQR = cv2.cvtColor(img_jizhiQR, cv2.COLOR_BGR2GRAY)
    print(img_jizhiQR.shape)

運算結果:

          # out:
        (220,220)

接下來,從220×220個點映射到25×25個點,實際上直接做一個簡單線性變換即可,即如果坐標是(20,20),則映射后的坐標點應該是(20/220*25, 20/220*25)=(0, 0),并且實際上很多點變換后對應(0, 0)這個點。

此步驟的代碼如下:

運行結果如圖3-6所示。

圖3-6 原始二維碼中像素值大小分布

這里有一部分點取值小于40,而另一部分點取值大于40。統計這一步的原因是,我們的映射會產生一些錯位,即切割不準確,此時有一些本來應該是黑色的點,切的時候進來了一些白色,造成干擾。此時需要設定一個閾值,即只有大于若干白色點,我們才認為是白色,否則是黑色。很明顯,這里可以設定這一閾值為40,查看結果如圖3-7所示。

    plt.imshow(np_25_counts>40, cmap="gray")

圖3-7 原始二維碼壓縮到(25×25)的結果(設定二值化閾值為40)

我們注意到這個結果與變換前的二維碼看起來完全相同,掃描后仍然是景略集智數據科學的主頁https://jizhi.im。而實際上,這張圖片的形狀已經由之前的220×220縮小到了25×25。此時就可以對照二維碼的編碼標準,用我們縮小后的矩陣逐一解讀二維碼信息。

這里進一步展開說明:

第一,為什么有很多二維碼工具我們還要講這方面的內容。其實不是要解決生活中二維碼的問題,而是以二維碼這個最像矩陣的圖像為例談談如何將圖像當成矩陣處理;并且現在越來越多的數據分析公司喜歡把自己的招聘網頁設計成奇怪的二維碼,然后有人解出來的在招聘時加分,至于如何解決這類問題,其難點不外乎找出映射函數(比如映射為圓形)以及各種閾值(奇怪的顏色形狀)。

第二,有SQL經驗的讀者會隱約感覺到,這里實際就是對圖片坐標進行映射后進行了類似AVG(value) GROUP BY KEY的操作。有深度學習基礎的讀者,更是能意識到,這個問題實際上就類似深度神經網絡中的一個池化(Pooling)過程,會在5.3節詳細介紹。那么這個問題能不能用深度神經網絡框架Tensorflow解決呢?答案是可以的,具體代碼的含義后續會解釋,當然這本書后續案例基本上都是使用Keras包裝Tensorflow,以簡化難度。

查看結果(如圖3-8所示):

sns.distplot(res.ravel())

圖3-8 查看結果

這里的閾值目測可能在130左右,可以用kmeans算法找到這個閾值,然后進行分類,其結果如圖3-9所示:

    from sklearn.cluster import KMeans
    kmeans = KMeans(n_clusters=2, random_state=0). fit(res.ravel().reshape(-1,1))
    min(res.ravel()[kmeans.labels_==1])
    minVal = min(res.ravel()[kmeans.labels_==1])
    print(minVal)
    # 122.77778
    plt.imshow(res[0,:,:,0] > 122.78, cmap="gray")

圖3-9 二維碼圖

此時掃描此圖,結果仍然是https://jizhi.im。

最后,我們增加難度。讀者應該注意到,各個網站公眾號實際使用的二維碼邊緣上有空白,中間有Logo。這種情況下,應該如何排除這些因素?作者這里提供一種思路,首先將之前的程序整理成函數:

這里仍然是使用TensorFlow執行卷積計算,然后對結果進行Kmeans分類,找出閾值,進行黑白的二值化。之前的代碼比較分散,因此這里寫成函數的形式,提高了復用率。接下來將函數應用在進行部分簡單處理后的圖像上,其結果如圖3-10(從原始二維碼圖片(左),去掉邊緣以及logo(中),最后將大小變成25×25的整個過程)所示:

圖3-10 運行結果

主站蜘蛛池模板: 岱山县| 黄冈市| 阿拉善左旗| 寻乌县| 巨野县| 六枝特区| 边坝县| 顺平县| 双鸭山市| 邵阳县| 剑河县| 宁远县| 咸宁市| 日喀则市| 龙口市| 梅河口市| 肥城市| 永春县| 南乐县| 新宁县| 集贤县| 邛崃市| 从化市| 百色市| 子长县| 彭阳县| 赞皇县| 剑川县| 涡阳县| 乌苏市| 柳江县| 盐城市| 霞浦县| 永城市| 张掖市| 巴彦淖尔市| 垦利县| 双鸭山市| 阿拉尔市| 通州区| 洪湖市|