Skip to content

我就是這樣製作VI-設計與程式間不對稱的harmony

從兩張 PNG 出發,用 Python 自動化生成完整品牌視覺識別系統的完整記錄。


一、什麼是 VI?為什麼要做?

VI(Visual Identity,視覺識別系統)是品牌對外溝通的「視覺語言規範」。 它回答一個核心問題:這個品牌在任何地方出現,應該長什麼樣子?

一套完整的 VI 套件至少包含:

類別內容
Logo 本體原始透明底版、各色底版本
Logo 變體水平、垂直、方形、圓形
色票HEX / RGB / CMYK 規格
字型規範主副字型、字級建議
使用規範保留空間、禁止行為
應用品範例名片、信頭、社群頭像

沒有 VI,每次設計師、工程師、行銷人員「自由發揮」,品牌就逐漸變形。 有了 VI,一個資料夾就能讓任何人做出一致的品牌表現。


二、起點:兩張 PNG

本次的原始素材只有兩張 PNG:

原始 Logo

兩張圖都是 Ascentek 的品牌 logo,內容相同但尺寸略有差異(一張是 16:9 展示版,另一張是較緊湊的版本)。

Logo 組成分析:

  • 左側:紙飛機 / A字形箭頭符號,由兩個三角形組成
    • 主三角:Teal 色(#03dcd0
    • 深色三角:近黑色(#181c20),製造立體/深度感
  • 右側:「Ascentek」品牌名稱,深色無襯線字型
  • 背景:透明(RGBA)

這兩個資訊就是整套 VI 的根基:形狀 + 顏色


三、品牌元素提取

3.1 色票提取

不靠肉眼猜色,用程式直接從像素讀取主色:

python
def get_dominant_colors(img, k=3):
    # 過濾透明像素,只分析有內容的部分
    pixels = [px[:3] for px in img.getdata() if px[3] > 0]
    # 用 Pillow 的 Adaptive Palette 聚類
    small = Image.new('RGB', (len(pixels), 1))
    small.putdata(pixels)
    pal = small.convert('P', palette=Image.ADAPTIVE, colors=k)
    return pal.getpalette()[:k*3]

提取結果:

角色HEXRGBCMYK(參考)
Primary#03dcd03, 220, 208C99 M0 Y5 K14
Secondary#204b4c32, 75, 76C58 M1 Y0 K70
Dark / Text#181c2024, 28, 32C25 M12 Y0 K88

3.2 Icon 邊界偵測

Logo 是「符號 + 文字」的組合圖,要做方形/圓形容器版本,必須把符號部分單獨截出來。

挑戰: 不能手動量像素,要讓程式自動找出「符號在哪裡結束、文字從哪裡開始」。

初始思路:找第一個空白欄(column gap)

python
# 錯誤版本 — 會在符號內部的小間隙就停止
for x in range(width):
    if not has_opaque_pixels(column[x]):
        return x  # 太早停了!

這個方法的問題:Ascentek 的 logo 符號由兩個分開的三角形組成,它們之間有小間隙。程式在這個內部間隙就停下來,結果只截到 teal 三角,深色三角被排除在外:

[teal △] [gap] [dark △] [  大間距  ] [Ascentek 文字]

         這裡被誤判為 icon 結尾

正確思路:找最寬的間距

icon 和文字之間的間距,一定比 icon 內部任何細縫都寬。 所以掃描所有 gap,取最寬的那個:

python
def find_icon_right(img):
    col_has = [any_opaque_pixel_in_column(x) for x in range(width)]
    
    # 收集所有 gap 的位置和寬度
    gaps = []
    for each gap in col_has:
        gaps.append((gap_start_x, gap_width))
    
    # 最寬 gap 的起點就是 icon 的右邊界
    return max(gaps, key=width)[start_x]

結果:邊界從 x=158(只有 teal)修正到 x=214(完整符號),深色三角成功納入。


四、工具選擇

工具用途選擇原因
Python 3腳本語言跨平台、生態豐富
Pillow(PIL)圖像處理核心純 Python、無需 numpy、處理 RGBA 完整支援
Montserrat品牌字型原 Logo 字型風格、SIL 授權可商用、已隨 Google Fonts 取得

為什麼不用 Photoshop / Figma?

  • 手動操作無法重現(換 logo 就要全部重做)
  • 腳本化後只需替換來源圖,重跑一次就更新全部 28 個檔案
  • 程式碼即文件:流程邏輯清楚記錄在腳本裡

五、核心技術:顏色適配

5.1 深色底適配(Dark Background Adaptation)

這是整套做法中最重要的一個判斷:

問題: Logo 上有深色(#181c20)的三角形和文字。把這個 Logo 直接放在深色底上,深色元素就消失不見了。

Copilot 的做法(錯誤): 把整個 Logo 染成單一顏色(全部變 teal),深色三角和文字都消失,Logo 失去立體感。

正確做法: 像素級判斷 — 只把「接近黑色」的像素改成白色,teal 部分原封不動:

python
def adapt_dark(img):
    data = list(img.getdata())
    out = []
    for r, g, b, a in data:
        if a < 10:                          # 透明:不動
            out.append((0, 0, 0, 0))
        elif r < 80 and g < 80 and b < 80:  # 深色:改白
            out.append((255, 255, 255, a))
        else:                               # 其他(teal):保留
            out.append((r, g, b, a))
    ...

結果對比:

效果
淺色底(原色)teal 三角 + 深色三角 + 深色文字 ✓
深色底(適配後)teal 三角 + 白色三角 + 白色文字 ✓

5.2 單色剪影(Monochrome)

印刷場景有時需要純黑或純白版本。做法是保留 alpha 通道的形狀,把所有有色像素填成指定顏色:

python
def silhouette(img, color_rgb):
    alpha = img.split()[3]          # 取出形狀遮罩
    base = Image.new('RGBA', img.size, color_rgb + (255,))
    base.putalpha(alpha)            # 把遮罩套回去
    return base

5.3 Circle Primary 的陷阱

最初的圓形主色版本:把原始 icon(teal)放在 teal 底 → 完全看不見。

正確做法:先把 icon 轉成白色剪影,再放在 teal 底:

python
icon_white = silhouette(icon, C_WHITE)  # 先變白色剪影
circ = on_canvas(icon_white, C_PRIMARY) # 再放 teal 底

六、Logo 變體生成邏輯

6.1 水平版(Horizontal)

Logo 本身就是水平構圖(符號在左、文字在右),所以直接加底色就是水平版,不需要重新排版,也不需要再疊加文字(疊加文字 = Copilot 版的重影 bug 來源)。

python
# 正確做法:logo 已含文字,只需加背景
img = on_canvas(logo, bg_color)

水平淺色版水平深色版

6.2 垂直版(Vertical)

垂直版需要「符號在上、文字在下」的構圖。由於來源 PNG 是合體圖(符號+文字),必須:

  1. 用邊界偵測截出純符號(icon)
  2. 放大符號至合適比例
  3. 用 Pillow 重新 render Montserrat 字型作為文字部分
  4. 垂直組合
python
# 截出純符號
icon = logo.crop((0, 0, icon_right_x, logo.height))
# 重新排版
canvas.paste(icon, centered_top)
draw.text(centered_bottom, 'Ascentek', font=fnt('SemiBold'))

6.3 方形 / 圓形容器

單純把符號(icon)放進正方形容器,加上 15% padding:

  • 方形:直接輸出 400×400
  • 圓形:建立同尺寸圓形遮罩,paste 時套用

七、應用品設計

7.1 名片(Business Card)

規格:3.5×2 吋 @ 300 dpi = 1050×600 px,雙面。

正面設計原則:

  • 左側 10px teal 邊條(視覺定錨)
  • Logo 置於左上
  • teal 分隔線區隔 logo 與個人資訊區
  • 文字層次:姓名(SemiBold 52pt)→ 職稱(Regular 30pt)→ 聯絡資訊(Regular 28pt)
  • 網址用 Primary 色(teal)強調
  • 底部 14px teal 底條收尾

背面設計原則:

  • 深色底(#181c20
  • 頂底各一條 teal 細線
  • 置中放深色底適配版 logo

名片正面名片背面

名片是生出來了,但規格大小與 layout 這種事情就很主觀了,因人而異就不多說了。

7.2 信頭(Letterhead)

規格:A4 @ 300 dpi = 2480×3508 px。

設計結構:

┌─────────────────────────────────────┐  ← 深色 header (280px)
│  [Logo]              Ascentek       │
│                      www...         │
├─────────────────────────────────────┤  ← teal 分隔線 (18px)
│                                     │
│  正文區域                            │
│                                     │
│                                     │
├─────────────────────────────────────┤  ← teal 頁尾分隔線
│  聯絡資訊 footer                     │
└─────────────────────────────────────┘  ← teal 底條

關鍵細節:Header 內的 Logo 需使用深色適配版(adapt_dark),才能在深色底上正常顯示。


八、與 AI 協作過程中踩到的坑

坑 1:重影(Ghost Text)

現象: 名片和信頭上出現兩個「Ascentek」文字。

根本原因: 用的是「完整 logo(含文字)」作為底圖,然後又用 draw.text() 疊了一次文字。

解法: 不要在有文字的 logo 上再加文字。水平版直接用 logo 原圖,垂直版從純符號重新組合。

坑 2:深色底 Logo 看不見

現象: Logo 放深色背景時,文字和深色三角消失,只剩 teal 部分浮在背景上。

解法: 逐像素判斷,將深色像素轉白,保留 teal 不動(adapt_dark 函數)。

坑 3:Icon 截切不完整

現象: 方形/圓形容器只有 teal 三角,深色三角被排除在外。

根本原因: 用「第一個空白欄」找邊界,但符號內部兩個三角之間就有小間隙,提早停止了。

解法: 改成找「最寬的 gap」,icon 和文字之間的間距一定比 icon 內部的縫還寬。

坑 4:Circle Primary 隱形

現象: teal 圖標放在 teal 底上完全看不見。

解法: 圓形主色版必須先把 icon 轉成白色剪影(silhouette),再放在 teal 底。

坑 5:美學這種事情,是很難絕對判斷的

畢竟芝蘭之室云云與海濱的人,這些就留待自身判斷了。個人對於生成的這張名片覺得相對 LOGO 是大了一點就是了。


九、最終輸出結構

VI-ClaudeVersion/

├── logo/                          # 所有 Logo 變體(PNG + SVG)
│   ├── logo_original.png          # 透明底,最具彈性
│   ├── logo_horizontal_light.png/svg  # 淺底主力版
│   ├── logo_horizontal_dark.png/svg   # 深底主力版
│   ├── logo_vertical_light.png/svg    # 垂直構圖(淺底)
│   ├── logo_vertical_dark.png/svg     # 垂直構圖(深底)
│   ├── logo_square_light.png/svg      # 方形容器
│   ├── logo_square_dark.png/svg
│   ├── logo_circle_light.png/svg      # 圓形容器
│   ├── logo_circle_dark.png/svg
│   ├── logo_circle_primary.png/svg    # 白剪影 on teal(App icon 用)
│   ├── logo_black.png                 # 單色黑(印刷)
│   └── logo_white.png                 # 單色白(印刷)

├── applications/                  # 應用品範例
│   ├── business_card_front.png
│   ├── business_card_back.png
│   ├── letterhead.png
│   └── letterhead.pdf

├── palette.txt                    # 色票(HEX + RGB + CMYK)
├── VI_GUIDELINES.md               # 使用規範
├── VI_Preview.png                 # A4 展示頁
├── VI_Preview.pdf
└── generate_vi.py                 # 生成腳本(可重複執行)

std-logo/                          # 精簡可攜版(移植到新專案用)
├── logo_original.png
├── logo_horizontal_light/dark .png/.svg
├── logo_square_light/dark .png
├── logo_circle_light/dark/primary .png
├── logo_black/white .png
├── palette.txt
└── VI_GUIDELINES.md

十、這套做法可以複用在哪裡?

前提條件

要讓這個流程套用到其他品牌,來源素材需要具備:

  1. PNG with transparency(透明背景) — 才能讓程式正確識別 Logo 範圍
  2. 向量清晰度或夠高解析度 — 建議 Logo 寬度 >= 600px,才能縮放後仍清晰
  3. 色彩簡潔 — 3–5 個主色最適合。漸層或照片感的 Logo 需要額外處理邏輯

移植新品牌的步驟

  1. 把新品牌的透明 PNG 放進資料夾
  2. 修改 generate_vi.py 開頭的兩行:
python
LOGO_SRC = '你的新logo.png'
BRAND    = '你的品牌名'
  1. 如果色票不同,修改 C_PRIMARY / C_SECONDARY / C_DARK
  2. 執行 python generate_vi.py
  3. 全套 28 個檔案一次生成完畢

應用到各種場景的選檔指引

場景選用檔案
網站 headerlogo_horizontal_light.svg(淺底)/ logo_horizontal_dark.svg(深底)
HTML faviconlogo_circle_primary.png → 轉 .ico
App 啟動圖示logo_circle_dark.png(1024×1024 再縮)
GitHub / LinkedIn 頭像logo_square_dark.png
PowerPoint 封面logo_original.png(透明底,任意背景)
單色印刷logo_black.pnglogo_white.png
提案 / Pitch Decklogo_horizontal_light.png(淺底頁)或 logo_horizontal_dark.png(深底頁)

CSS 快速接入

css
:root {
  --color-primary:   #03dcd0;
  --color-secondary: #204b4c;
  --color-dark:      #181c20;
  --font-brand:      'Montserrat', sans-serif;
}

Tailwind 快速接入

js
// tailwind.config.js
theme: {
  extend: {
    colors: {
      primary:   '#03dcd0',
      secondary: '#204b4c',
      dark:      '#181c20',
    },
    fontFamily: {
      brand: ['Montserrat', 'sans-serif'],
    },
  }
}

十一、核心思維總結

做 VI 的本質不是「畫好看的圖」,而是制定規則並讓它可以被執行

從這次的流程中提煉出幾個關鍵原則:

  1. 不要手工操作可以自動化的事 — 色票提取、尺寸換算、背景加工,全部交給程式
  2. 從像素看真相 — 色票不靠肉眼,邊界不靠目測,用程式碼讀取原始數據
  3. 把設計決策寫成函數adapt_dark()silhouette()find_icon_right(),每個決策都有明確的邏輯和名字
  4. 腳本即文件 — 整個流程記錄在 generate_vi.py 裡,讀程式碼就知道做了什麼
  5. 一鍵可重現 — 換 logo 換品牌,重跑一次腳本就全部更新,沒有「只有我知道怎麼做」的黑盒

最好的 VI 系統,是一個不需要原作者在場也能被任何人正確使用的系統。

彩蛋: 其實原始的 LOGO 也是 AI 跑了幾張之後老闆挑出來的⋯⋯


製作工具:Python 3 · Pillow · Montserrat(SIL OFL)
生成腳本:VI-ClaudeVersion/generate_vi.py
標準素材包:std-logo/

Ascentek數位知識庫