iT邦幫忙

2022 iThome 鐵人賽

DAY 7
0

練習用圖

  1. grassland.jpg
  2. dog.jpg

內容

  1. 將OpenCV影像處理技巧轉換成Functions,提高可讀性與重複利用性,如下。

    1.1 讀取中文路徑圖檔,並轉換為BGR

    def cv_imread(image_path):
        image = cv2.imdecode(np.fromfile(image_path, dtype=np.uint8), -1)
        image = cv2.cvtColor(image, cv2.COLOR_BGRA2BGR)
        return image
    

    1.2 顯示圖檔

    def show_img(name, image):
        cv2.imshow(name, image)
        cv2.waitKey(0)   
    

    1.3 Numpy儲存中文路徑圖檔

    def cv_save(image, result_path):
        cv2.imencode('.jpg', image)[1].tofile(result_path)   
    

    1.4 比例縮小圖檔

    def resize_img(image, pos):
        image = cv2.resize(image, dsize=None, fx=pos, fy=pos)
        return image  
    

    1.5 旋轉圖片

    def rotate_img(image):
        # 讀取圖片大小
        (h, w, d) = image.shape
        # 找到圖片中心
        center = (w // 2, h // 2)
        # 代表隨機順逆時針旋轉0-2度
        angle = random.randint(-40, 40) / 20
        # 縮放倍數為1.03倍,避免旋轉時小狗圖案被裁切掉
        M = cv2.getRotationMatrix2D(center, angle, 1.04)
        # (w, h )代表圖片縮放與旋轉後,需裁切成的尺寸
        image = cv2.warpAffine(image, M, (w, h))
        return image 
    
  2. 漫水填充法與前後景合成

    2.1 漫水填充法去背

    • 程式碼

      def image_matting(image):
          h, w = image.shape[:2]
          mask = np.zeros([h + 2, w + 2], np.uint8)
          cv2.floodFill(image, mask, (2, 2), (255, 255, 255), 
                        (130, 130, 130), (50, 50, 50), 
                        cv2.FLOODFILL_FIXED_RANGE)
          return image
      

    2.2 前後景影像合成

    • 程式碼

      def prduce_pic(image1, image2, x, y):
          image2_copy = image2.copy()
          # 前景上小狗圖像去背
          image2_copy = image_matting(image2_copy)
          # show_img('floodFill', image2_copy)
      
          # 前景上產生小狗圖像的mask
          image2gray = cv2.cvtColor(image2_copy, cv2.COLOR_BGR2GRAY)
          ret, mask = cv2.threshold(image2gray, 254, 255, cv2.THRESH_BINARY)
          # 開運算去除mask中白色雜訊
          kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
          mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
          # show_img('mask',  mask)
      
          # 定義背景上的小狗ROI區域
          rows, cols, channels = image2.shape
          roi = image1[y:y + rows, x:x + cols]
      
          # 背景上ROI區域摳出小狗圖案mask
          image1_bg = cv2.bitwise_and(roi, roi, mask=mask)
          # show_img('image1_bg', image1_bg)
      
          # 前景上產生小狗圖像的inverse mask
          mask_inv = cv2.bitwise_not(mask)
          # show_img('mask_inv', mask_inv)
      
          # 前景上小狗圖像的inverse mask內填充小狗圖案
          image2_fg = cv2.bitwise_and(image2, image2, mask=mask_inv)
          # show_img('image2_fg', image2_fg)
      
          # 將「摳出小狗圖案mask的背景ROI區域」與「填充小狗圖案的前景」相加
          dst = cv2.add(image1_bg, image2_fg)
          # show_img('dst', dst)
      
          # 用dst替換掉背景原圖中含有小狗的區域
          image1[y:y + rows, x:x + cols] = dst
          # show_img('image1', image1)
      
          return image1
      

    2.3 影像合成過程

    • 背景原圖

    • 前景原圖

    • 前景上小狗圖像去背

    • 前景上產生小狗圖像的mask

    • 定義背景上的小狗ROI區域

    • 背景上ROI區域摳出小狗圖案mask

    • 前景上產生小狗圖像的inverse mask

    • 前景上小狗圖像的inverse mask內填充小狗圖案

    • 將「摳出小狗圖案mask的背景ROI區域」與「填充小狗圖案的前景」相加

    • 用dst替換掉背景原圖中含有小狗的區域

  3. 完整程式碼

import numpy as np
import random
import cv2

# 讀取中文路徑圖檔,並轉換為BGR
def cv_imread(image_path):
    image = cv2.imdecode(np.fromfile(image_path, dtype=np.uint8), -1)
    image = cv2.cvtColor(image, cv2.COLOR_BGRA2BGR)
    return image

# 顯示圖檔
def show_img(name, image):
    cv2.imshow(name, image)
    cv2.waitKey(0)

# Numpy儲存中文路徑圖檔
def cv_save(image, result_path):
    cv2.imencode('.jpg', image)[1].tofile(result_path)    
    
# 比例縮小圖檔
def resize_img(image, pos):
    image = cv2.resize(image, dsize=None, fx=pos, fy=pos)
    return image

# 旋轉圖片
def rotate_img(image):
    # 讀取圖片大小
    (h, w, d) = image.shape
    # 找到圖片中心
    center = (w // 2, h // 2)
    # 代表隨機順逆時針旋轉0-2度
    angle = random.randint(-40, 40) / 20
    # 縮放倍數為1.03倍,避免旋轉時小狗圖案被裁切掉
    M = cv2.getRotationMatrix2D(center, angle, 1.04)
    # (w, h )代表圖片縮放與旋轉後,需裁切成的尺寸
    image = cv2.warpAffine(image, M, (w, h))
    return image

# 漫水填充法去背
def image_matting(image):
    h, w = image.shape[:2]
    mask = np.zeros([h + 2, w + 2], np.uint8)
    cv2.floodFill(image, mask, (2, 2), (255, 255, 255), 
                  (130, 130, 130), (50, 50, 50), 
                  cv2.FLOODFILL_FIXED_RANGE)
    return image

# 前後景合成
def prduce_pic(image1, image2, x, y):
    image2_copy = image2.copy()
    # 前景上小狗圖像去背
    image2_copy = image_matting(image2_copy)
    # show_img('floodFill', image2_copy)

    # 前景上產生小狗圖像的mask
    image2gray = cv2.cvtColor(image2_copy, cv2.COLOR_BGR2GRAY)
    ret, mask = cv2.threshold(image2gray, 254, 255, cv2.THRESH_BINARY)
    # 開運算去除mask中白色雜訊
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
    # show_img('mask',  mask)

    # 背景上定義小狗ROI區域
    rows, cols, channels = image2.shape
    roi = image1[y:y + rows, x:x + cols]

    # 背景上ROI區域摳出小狗圖案mask
    image1_bg = cv2.bitwise_and(roi, roi, mask=mask)
    # show_img('image1_bg', image1_bg)

    # 前景上產生小狗圖像的inverse mask
    mask_inv = cv2.bitwise_not(mask)
    # show_img('mask_inv', mask_inv)

    # 前景上小狗圖像的inverse mask內填充小狗圖案
    image2_fg = cv2.bitwise_and(image2, image2, mask=mask_inv)
    # show_img('image2_fg', image2_fg)

    # 將「摳出小狗圖案mask的背景」與「填充小狗圖案的前景」相加
    dst = cv2.add(image1_bg, image2_fg)
    # show_img('dst', dst)

    # 用dst替換掉背景中含有小狗的區域
    image1[y:y + rows, x:x + cols] = dst
    # show_img('image1', image1)

    return image1

if __name__ == '__main__':
    result_path = './result/'
    front_path = './dog.jpg'
    back_path = './grassland.jpg'

    front = cv_imread(front_path)
    # show_img('front', front)

    for i in range(1, 10):
        front1 = rotate_img(front)
        front1 = resize_img(front1, 0.9615)
        back = cv_imread(back_path)
        # show_img('back', back)
        result = prduce_pic(back, front1, random.randrange(0, 1550), 
                            random.randrange(500, 700))
        cv_save(result, result_path + str(i) + '.jpg')
        print('  成功儲存第 {} 張圖片: {}'.format(i, str(i) + '.jpg'))
        print('=' * 50)
    print('※程式執行完畢')
  1. 執行結果:成功產出9張dog與grassland的合成圖


小結

  1. 下一站,我們前往「OpenCV邊緣偵測」。

讓我們繼續看下去...


參考資料

  1. OpenCV之ROI和泛洪填充
  2. OpenCV 按位bitwise运算、掩膜mask运算详解 表格+图解 Python代码实例详解 基础实用款
  3. 【沒錢ps,我用OpenCV!】Day 22 - 綜合運用1,用 OpenCV 來P圖囉! 來運用各種之前學習的各種東西吧! merge two images
  4. 探討與改善-增加訓練樣本(二)

上一篇
《第6天》OpenCV影像處理(四)
下一篇
《第8天》OpenCV邊緣偵測
系列文
Object Detection and Image Processing with Python30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言