使用 Python 組合 NBA 球星卡

相信對 NBA 感興趣的大兄弟必定不會對球星卡陌生吧,雖然不知道我們這個圈子對 NBA 感興趣的大兄弟多很少。可是,不感興趣也問題不大,本文闡述的方法實際上是通用的圖片合成方法。javascript

讓咱們來看一張球星卡:java

這種球星卡能夠劃分爲5個部分python

  1. 球員動做圖
  2. 球員姓名
  3. 球隊logo
  4. 底板背景圖
  5. 裝飾邊框圖

今天咱們要乾的事情就是找到這5個素材而後用 Python 把他們組合起來,那麼這個時候確定有大兄弟會有疑問了,直接用 PS 套起來不就行了嗎,講道理這樣作確實方便快捷,可是前提是你只作這一張卡,當你要爲聯盟大概450名球員製做球星卡的時候,你就須要一個腳原本幫助你完成了(對 PS 不太熟,若是 PS 也能夠,能夠告訴我哈)。git

這篇文章須要一點 Python 基礎,徹底不瞭解 Python 的大兄弟最好去學習一點基礎知識再看。github

OK 讓咱們開始吧!數據庫

準備素材

就像開頭提到的,咱們須要5種素材。這5種素材我都會提供若干個給你們練手。服務器

上面的圖片實際上只有4個素材,還有一個就是球員的名字了,球員的名字咱們能夠在組合過程當中使用 ImageDraw 和 ImageFont 加載球員姓名。app

爲了不字體路徑和中文亂碼的問題,我還提供了一個微軟雅黑的字體。函數

素材能夠在 這裏 clone 或者下載,聲明:本文全部涉及的素材和圖片僅供交流學習使用。佈局

開始寫代碼

咱們的場景是爲聯盟中的全部球員製做球星卡,那麼全部的球員天然是從數據庫裏面查出來的了,這裏爲了練習,咱們能夠 mock 一些數據(雖然,講道理,波什並不能放在 SUPER 裏面,可是這裏只有一張裝飾邊框圖,因此就勉爲其難的和吾皇放在一個等級了)。

mock_data = [
    {
        'id': 1966,
        'cn_name': '勒布朗-詹姆斯',
        'team_id': 5,
        'category': 'SUPER'
    },
    {
        'id': 1977,
        'cn_name': '克里斯-波什',
        'team_id': 14,
        'category': 'SUPER'
    }
]複製代碼

有數據以後,咱們就來遍歷這些球員,找到咱們須要的屬性,再傳入到一個組合函數中。

def compose_all(all):
    for player in all:
        id = player['id']
        # if id == 1966:
        if True:
            category = player['category']
            player_img =  str(id) + '.png'

            team_id = player['team_id']
            team_img = str(team_id) + '.png'
            name = player['cn_name']
            category = player_category.index(category) + 1
            category_img = 'card_bg_' + str(category) + '.png'

            output_name = str(id) + '.png'
            print('start compose ' + str(id))
            compose(player_img, name, team_img, category_img, output_name)複製代碼

這裏有個我的習慣,由於常常在服務器上寫一些腳本,全部if True:那個地方就是調試用的,當一個球員調試沒問題以後,註釋掉,跑代碼,這樣能夠不用再調整縮進了,不知道其餘的大兄弟這個地方喜歡怎麼寫。

在這裏我默認會對球員分檔(根據一些數據信息)

player_category = ["SUPER", "CORE", "BLUE", "SIX", "BENCH"]複製代碼

對應檔位的裝飾邊框分別爲card_bg_1.pngcard_bg_2.png等。
檢查5個素材是否都拿到了:

  1. 球員動做圖 -> player_img
  2. 球員姓名 -> name
  3. 球隊logo -> team_img
  4. 底板背景圖 —> None
  5. 裝飾邊框圖 -> card_bg_n.png (n 對應檔位)

還差一個底板背景圖,由於每一個球員底板背景圖都同樣,因此在組合函數中直接使用就行了。

在咱們去組合球星卡以前,還有一個問題須要解決,那就是咱們不能保證全部素材都在同一個目錄下,那麼咱們就須要給每一個素材指定一個目錄,這樣咱們在組合球星卡的時候就能夠一馬平川了。

team_path = './logo/'
player_path = './player_img/'
output_path = './trading_cards/'
font_file = './assets/msyh.ttf'
card_decorate_path = './assets/'複製代碼

設置好路徑以後寫上咱們的組合函數,爲了保證這個函數的正常運行,咱們須要導入三個模塊。

import os
import numpy as np
from PIL import Image, ImageFont, ImageDraw複製代碼

若是提示沒有找到模塊,請使用下面的命令進行安裝

pip install Pillow
pip install numpy複製代碼

Pillow 關於圖片處理的詳細文檔請參考 Pillow

下面是咱們的組合函數

def compose(player_img, name, team_logo, category_img, output_name):

    card_bg = card_decorate_path + 'bg.png'
    player_img_offset_height = 15

    if not os.path.isfile(player_path + player_img):
        need_manual_compose.append(player_img)
        print(player_path + player_img + ' is not exist')
        return

    player_img = Image.open(player_path + player_img).convert('RGBA')
    bg_img = Image.open(card_decorate_path + category_img).convert('RGBA')
    card_bg_img = Image.open(card_bg).convert('RGBA')
    logo = Image.open(team_path + team_logo).convert('RGBA')

    logo = logo.resize((100,100), Image.ANTIALIAS)

    card_bg_img.paste(player_img, (35,player_img_offset_height), player_img)
    card_bg_img.paste(bg_img, (0,0), bg_img)
    card_bg_img.paste(logo, (95,315), logo)

    font = ImageFont.truetype(font_file, 20)
    d = ImageDraw.Draw(card_bg_img)

    try:
        name = unicode(name, 'utf-8')
    except NameError:
        name = name
    d.text((12, 12), name, font=font, fill=(255,255,255))

    card_bg_img.save(output_path + output_name, quality=100)複製代碼

有幾個問題須要說明一下:

  1. 有些球星動做的素材可能找不到,那麼就將找不到的球員記錄下來,最後手工處理。
  2. 由於咱們要使用到 alpha 通道,因此須要 convert('RGBA')
  3. 在圖片 paste 以前必須保證團片和粘貼範圍像素同樣,不同的話就使用 resize 函數變成同樣的,Image.ANTIALIAS 參數的做用是抗鋸齒,這樣 resize 出來的圖片邊緣會更圓潤。
  4. b 圖片貼在 a 圖片上,使用 a.paste(b, (x,y), b),(x,y) 爲左上角的座標,第三個參數 b 是做爲 mask,若是不使用這個參數會致使 b 圖片透明的部分也覆蓋在 a 上面。
  5. 這個程序在 python2 和 python3 上均可以運行。

OK,讓咱們來看一看結果怎麼樣吧

恩,彷佛還不錯,可是你們會發現波什的手沒了,因此說一馬平川什麼的都是騙人的。

通過我我的的觀察,會發現大部分的球星動做圖都是和詹姆斯相似的(即球員的動做在圖片中的位置是靠下的),若是下移粘貼座標會致使球星卡的主要局域出現大面積的空白。一計不成,再生一計,咱們能夠對相似波什的動做圖作特殊處理,下移他們的粘貼座標就能夠了。

ok,問題來了,人眼一看就會知道哪一個動做圖高哪一個動做圖低,那麼 Python 怎麼才能知道呢?

能夠看到,每張動做圖的大小是同樣的,可是具體的動做在圖片中的分佈是不同的。

這個時候咱們就須要numpy這個庫來幫助咱們把圖片轉換成像素矩陣,而後咱們對矩陣進行逐行掃描並記錄有效像素出現的位置,這樣就能夠判斷哪些動做圖是偏高的。

def calculateUsefulHeight(img):
    img = Image.open(player_path + img).convert('RGBA')
    w, h = img.size
    mat = np.array(img)

    for i in range(mat.shape[0]):
        if not allEqual(mat[i]):
            return h - i

def allEqual(line):
    w = len(line)
    if not w:
        return True
    init_value = line[0][3]
    step = 10
    for i in range(int(round(w/step))):
        if line[i * step][3] == init_value:
            continue
        else:
            return False
    return True複製代碼

而後再在組合函數中加入對動做圖高的特殊處理的代碼就能夠了。

def compose(player_img, name, team_logo, category_img, output_name):
    ...
    # deal with high player image
    h = calculateUsefulHeight(player_img)
    # 這個地方的310是球星卡展現球員動做的最大高度
    if h > 310:
        offset = h - 310
        player_img_offset_height += offset複製代碼

再來跑一遍,看看效果如何。

不錯,這樣就能夠了,尤爲是一瞬間跑出來 450 張看起來效果還不錯的球星卡仍是很是的爽的。

OK了,到這裏應該就能夠結束了,源碼能夠在 這裏 獲得,裏面包含本文全部涉及的圖片,素材和代碼。

若是各位大兄弟,有更好的設計和佈局也歡迎和我交流。

相關文章
相關標籤/搜索