@欄目:



Similar documents
6-1-1極限的概念

證 券 簡 易 下 單 :2121 證 券 簡 易 下 單 1. 主 工 具 列 的 視 窗 搜 尋 器 直 接 輸 入 點 擊 主 選 單 證 券 專 區 下 單 特 殊 下 單 2121 證 券 簡 易 下 單 畫 面 說 明 1. 下 單 區 2. 個 股 行 情 資 訊 與

所 3 學 分 課 程, 及 兩 門 跨 領 域 課 程 共 6 學 分 以 上 課 程 學 生 在 修 課 前, 必 須 填 寫 課 程 修 課 認 定 表, 經 班 主 任 或 指 導 教 授 簽 名 後 始 認 定 此 課 程 學 分 ) 10. 本 規 章 未 盡 事 宜, 悉 依 學 位

Microsoft Word doc

目 錄 壹 題 目 1: 新 增 商 品 ( 商 品 名 稱 為 玉 井 芒 果 乾 禮 盒 )... 3 貳 題 目 2: 新 增 商 品 ( 商 品 名 稱 為 紅 磚 布 丁 精 選 禮 盒 )... 5 參 題 目 3: 新 增 商 品 ( 商 品 名 稱 為 晶 鑽 XO 醬 禮 盒 ).

寫 作 背 景 導 讀 [98] L Lyman Frank Baum

章節

第 6. 節 不 定 積 分 的 基 本 公 式 我 們 可 以 把 已 經 知 道 反 導 函 數 之 所 有 函 數 都 視 為 不 定 積 分 的 基 本 公 式 基 本 公 式 涵 蓋 的 範 圍 愈 大, 我 們 求 解 積 分 就 愈 容 易, 但 有 記 憶 不 易 的 情 事 研 讀

<4D F736F F D20B3E6A4B830312D2D2DBCC6BD75BB50BEE3BCC6AABAA55BB4EEB942BAE22E646F6378>

目 錄 項 目 內 容 頁 數 1 手 機 要 求 3 2 登 記 程 序 3 3 登 入 程 序 4 4 輸 入 買 賣 指 示 6 5 更 改 指 示 14 6 取 消 指 示 18 7 查 詢 股 票 結 存 21 8 查 詢 買 賣 指 示 23 9 更 改 密 碼 查 詢 股

101年度社會福利方案 網路線上操作手冊

Microsoft PowerPoint - 資料庫正規化(ccchen).ppt

第二組掃描器規範書

(Microsoft Word - MOODLE990201\266i\266\245\244\342\245U )

Microsoft Word - 第四章.doc

實德證券網上交易系統示範

Microsoft Word - ch07

e-Submission System Quick Reference Guide for Publication Related Matters (Chinese version)

PowerPoint 簡報

授 課 老 師 章 節 第 一 章 教 學 教 具 間 3 分 鐘 粉 筆 CNC 銑 床 教 學 內 容 CNC 銑 床 之 基 本 操 作 教 材 來 源 數 值 控 制 機 械 實 習 Ⅰ 1. 了 解 CNC 銑 床 的 發 展 2. 了 解 CNC 銑 床 刀 具 的 選 用 3. 了 解

二 兒 歌 選 用 情 形 ( ) 2 ( ) ( )

投影片 1

CONTENTS 訓 練 內 容 設 計 法 056 淡 季 期 的 訓 練 058 旺 季 期 的 訓 練 060 針 對 爬 坡 賽 的 訓 練 內 容 062 賽 後 的 資 料 分 析 PART4/ 鏑 木 毅 先 生 的 建 言 活 用 於 越 野 路 跑 的 心 跳 訓

NCKU elearning Manual

研究一:n人以『剪刀、石頭、布』猜拳法猜拳一次,決定一人勝

內 政 統 計 通 報

128 提 示 樞 紐 分 析 表 的 用 途 樞 紐 分 析 表 是 指 可 以 用 來 快 速 合 併 和 比 較 大 量 資 料 的 互 動 式 表 格, 透 過 它 可 以 詳 細 分 析 數 值 資 料, 特 別 適 用 於 下 列 情 況 : 需 要 從 含 有 大 量 資 料 的 清

目 錄 頁 1. 歡 迎 使 用 網 上 預 約 面 談 訪 問 系 統 新 用 戶 新 用 戶 登 入 帳 戶 程 序 啟 動 網 上 預 約 面 談 訪 問 帳 戶 核 對 帳 戶 的 地 址 資 料

肆 研 究 方 法 進 行 本 研 究 前, 我 們 首 先 對 研 究 中 所 用 到 名 詞 作 定 義 定 義 : 牌 數 : 玩 牌 時 所 使 用 到 撲 克 牌 數 次 數 : 進 行 猜 心 術 遊 戲 時, 重 複 分 牌 次 數 數 : 進 行 猜 心 術 遊 戲 時, 每 次 分

關 於 教 育 部 學 習 拍 立 得 教 育 部 於 (103) 年 度 整 合 各 縣 市 政 府 部 屬 機 構 大 學 及 民 間 的 數 位 資 源 與 服 務, 依 不 同 類 型, 分 別 匯 集 於 教 育 大 市 集 教 育 百 科 教 育 媒 體 影 音 教 育 部 學 習 拍


四 修 正 幼 兒 園 師 資 類 科 應 修 學 分 數 為 四 十 八 學 分, 並 明 定 學 分 數 抵 免 之 相 關 規 定 及 規 範 修 習 幼 兒 園 教 育 專 業 課 程 之 最 低 年 限 ( 修 正 條 文 第 五 條 ) 五 發 給 修 畢 師 資 職 前 教 育 證 明

目 錄 一 系 統 登 入... 2 ( 一 ) 系 統 登 入 畫 面... 2 ( 二 ) 首 次 登 入 請 先 註 冊... 3 ( 三 ) 忘 記 單 位 帳 號... 8 ( 四 ) 忘 記 密 碼 ( 五 ) 健 保 卡 更 換 ( 六 ) 重 寄 確 認 信.


16

五 四 五 說 ( 代 序 ) 李 澤 厚 劉 再 復 I I II IV V VII 第 一 篇 五 四 新 文 化 運 動 批 評 提 綱 附 論 一 中 國 貴 族 精 神 的 命 運 ( 提 綱 )

Layout 1

瑞興銀行

(DP_MFP_Training

sle cover 1

長跨距暨挑高建築特殊結構系統之調查分析

題組一 文書排版

骨 折 別 日 數 表 1. 鼻 骨 眶 骨 ( 含 顴 骨 ) 14 天 11. 骨 盤 ( 包 括 腸 骨 恥 骨 坐 骨 薦 骨 ) 40 天 2. 掌 骨 指 骨 14 天 12. 臂 骨 40 天 3. 蹠 骨 趾 骨 14 天 13. 橈 骨 與 尺 骨 40 天 4. 下 顎 ( 齒

1 CH1 環 境 介 面 及 面 板 設 定 1-1 Word 2010 環 境 介 面 與 功 能 區 1-2 環 境 介 面 色 調 處 理 1-3 自 訂 快 速 存 取 工 具 列 1-4 Word 選 項 控 制 CH2 文 字 資 料 2-1 建 立 文 字 2-2 貼 入 網 頁 文

(Microsoft Word - \246\250\301Z\272\336\262z.doc)

節 數 內 容 網 頁 設 計 (1): 利 用 Google Sites 制 作 簡 單 文 字 網 頁, 連 結 不 同 版 面 網 頁 設 計 (2): 在 文 字 網 頁 上 加 插 圖, 上 載 網 頁 影 片 設 計 (1): 利 用 Windows Movie

前 項 第 三 款 所 定 有 機 農 產 品 及 有 機 農 產 加 工 品 驗 證 基 準, 如 附 件 一 第 七 條 驗 證 機 構 受 理 有 機 農 產 品 及 有 機 農 產 加 工 品 之 驗 證, 應 辦 理 書 面 審 查 實 地 查 驗 產 品 檢 驗 及 驗 證 決 定 之

???T????????

life930106

簽 呈

奇 妙 的 24 摘 要 從 撲 克 牌 中 隨 機 抽 取 4 張 牌 可 以 有 1820 種 牌 組, 在 這 1820 種 牌 組 中, 有 1362 組 可 經 由 四 則 運 算 的 方 式, 算 出 24 點, 有 458 組 無 解 快 速 求 解 的 方 法 有 相 加 法 因 數

Microsoft Word - Draft circular on Sub Leg Apr (chi)_Traditional

1

Microsoft Word - 雲林區_免試平台_國中模擬選填_操作手冊.doc

HSBC Holdings plc Interim Report Chinese

BSP 烤箱 - 封面-2

二零零六至零七年施政報告

chapter1.indd

<30332EAAFEA5F3A440A142A447A142A454A142A57CA147BEC7A5CDB14DB77EC3D2B7D3BEC7B2DFA661B9CF2E786C73>

PROSPECT EXPLORATION 壹 前 言 第 9 卷 第 2 期 中 華 民 國 100 年 2 月


人 們 在 為 生 活 空 間 中 的 物 品 選 擇 色 彩 時, 不 自 覺 地 會 反 應 出 大 腦 對 色 彩 的 解 釋, 設 計 師 若 能 掌 握 色 彩 所 隱 藏 的 訊 息, 便 可 以 充 分 利 用 並 創 造 出 極 具 魅 力 的 產 品 視 覺 對 知 覺 的 影 響

本 題 各 點 彼 此 均 有 相 互 關 聯, 作 答 不 完 整, 將 影 響 各 評 分 點 之 得 分, 請 注 意 檔 名 儲 存 錯 誤, 該 題 一 律 0 分 計 算 深 淺 圖 表.xlsx 請 依 下 方 題 目 敘 述 操 作 ( 佔 總 分 :) 儲 存 格 範

ART_RAE16_ticket_cn_p.1


《數學奠基活動模組示例》

會 員 專 區 使 用 手 冊 目 錄 一 基 本 介 紹 會 員 專 區 登 入 位 置 主 畫 面 與 網 站 架 構 : 功 能 導 覽 列 說 明 :... 3 二 DOI 查 詢 與 維 護... 4 三 DOI 註 冊 期 刊 類 型...

<4D F736F F D20B2C433B3B92020B971B8F4A4C0AA52A7DEA5A9>

1

(Microsoft Word \245\277\244\361\273P\244\317\244\361.doc)

Microsoft Word - BM900HD-2F電腦設定.doc

Microsoft PowerPoint - 使用 Word 編輯與排版文件 (II).ppt

進 入 系 統 1. 請 於 首 頁 右 側 使 用 者 登 入 輸 入 帳 號 密 碼 驗 證 碼 後, 點 選 登 入 進 入 系 統 2. 直 接 點 選 右 側 的 進 入 系 統, 直 接 進 入 題 目 檢 索 頁 面 直 接 進 入 系 統 後, 您 仍 可 瀏 覽 選 擇 您 所 需

格 成 績 證 明 第 六 條 第 七 條 本 系 大 四 課 程 中 規 劃 日 本 韓 國 越 南 專 題 研 究, 學 生 需 於 大 四 時 修 習 該 課 程, 並 於 規 定 期 間 內 提 出 專 題 報 告, 取 得 合 格 成 績 證 明 本 系 規 定 學 生 畢 業 時 需 取

目 錄

教育實習問與答:

教 師 相 關 ( 升 等, 依 業 務 需 002 交 通 管 科 評 鑑, 評 量, 徵,C031, 聘, 各 項 考 試 委 C051,C054, 員, 通 訊 錄 等 ),C057, C058,C063 各 項 會 議 紀 錄 依 業 務 需 C001,, 002,130 交 通 管 科 (

PhotoImpact

校 長 遴 選 者 就 相 關 遴 選 事 項, 有 程 序 外 之 接 觸 遴 選 會 委 員 在 任 期 間 因 故 無 法 執 行 任 務 或 有 不 適 當 之 行 為 者, 由 各 該 主 管 機 關 解 聘 之 ; 其 缺 額, 依 第 一 項 至 第 五 項 規 定 聘 ( 派 ) 委

Microsoft Word - LongCard_Promo_2013_FAQ_tc_pdf.doc

iPhone版操作手冊

2 2.1 A H ir@abchina.com 2

PART 2 系 統 篇 仔 細 檢 查 記 憶 體 和 顯 示 卡 AIDA64 Everest 操 作 : 使 用 AIDA64 檢 測 主 機 溫 度 AIDA64 DirectX AIDA

Acronis Backup & Recovery 11 進階版本

Microsoft Word - 立法會十四題附件.doc

xls

2 飲 料 調 製 丙 級 技 術 士 技 能 檢 定 必 勝 寶 典 Beverage Modulation Preparation 應 考 綜 合 注 意 事 項 A1 A2 A3 A4 A5 A6 B7 B8 B9 B10 B11 B12 C13

支 持 機 構 : 社 會 文 化 司 主 辦 機 構 : 澳 門 學 聯 澳 門 青 年 研 究 協 會 電 話 : 傳 真 : 網 址 : 報 告 主 筆 : 李 略 博 士 數 據 錄


內 容 2 建 立 網 站 4 界 面 介 紹 5 基 本 操 作 8 新 增 Portlet 8 拖 拉 操 作 9 版 面 結 構 12 更 換 版 型 14 更 換 Banner 17 主 要 操 作 1 9 網 頁 文 章 19 控 制 台 23 檔 案 總 管 與 圖 片 總 管 24 網

連江縣政府所屬學校兼任代課及代理教師聘任實施要點(草案)

修 課 特 殊 規 定 : 一 法 律 系 學 生 最 低 畢 業 學 分 128;101 學 年 度 修 讀 法 律 系 雙 主 修 學 生 應 修 畢 法 律 專 業 目 64 學 分 ( 限 修 習 本 校 法 律 系 開 設 課 程, 不 得 以 原 學 系 或 外 校 課 程 抵 免 -

一、 資格條件:

75 叁 積 木 遊 戲 的 教 學 功 能 一 促 進 體 能 發 展 二 發 展 社 會 技 巧 Ramsey 1991 Beaty 1995 ( ) ( ) ( ) 三 學 習 情 緒 處 理 國 教 之 友 第 59 卷 第 3 期 19

Microsoft Word - 全華Ch2-05.doc

目 錄 引 言 P 署 長 陳 鴻 祥 先 生 講 辭 P.6 10 副 署 長 營 運 服 務 吳 啟 明 先 生 講 辭 穩 步 求 進 P An Invisible Man Meets the Mummy 副 署 長 規 管 服 務 陳 帆

業 是 國 家 的 根 本, 隨 著 科 技 的 進 步 與 社 會 的 富 裕, 增 加 肥 料 的 施 用 量 與 農 病 蟲 害 防 治 方 法 的 提 升, 使 得 糧 食 產 量 有 大 幅 的 增 長, 但 不 當 的 農 業 操 作, 如 過 量 的 肥 料 農 藥 施 用 等, 對

包 裝 維 生 素 礦 物 質 類 之 錠 狀 膠 囊 狀 食 品 營 養 標 示 應 遵 行 事 項 一 本 規 定 依 食 品 安 全 衛 生 管 理 法 第 二 十 二 條 第 三 項 規 定 訂 定 之 二 本 規 定 所 稱 維 生 素 礦 物 質 類 之 錠 狀 膠 囊 狀 食 品, 指

虛擬交易所97年GVE3簡易版.doc

untitled

Transcription:

撰 寫 影 像 處 理 程 式 難 不 倒 我!! - 簡 單 的 數 位 影 像 處 理 ( C# 篇 ) 文 : 井 民 全 個 人 程 式 設 計 心 得 http://mqjing.twbbs.org/~ching/course/course.htm 之 前 向 大 家 介 紹 如 何 取 得 無 名 小 站 的 照 片, 不 知 道 有 沒 有 人 去 詴 詴 看? 如 今 我 們 已 經 會 把 線 上 的 照 片 下 載 回 來, 大 家 會 不 會 想 知 道 [ 如 何 自 己 寫 一 個 秀 圖 的 軟 體 ] 甚 至 是 一 個 [ 影 像 處 理 軟 體 ]? 也 許 有 人 會 問 : 已 經 有 ACDSee 這 麼 優 秀 的 秀 圖 工 具, 為 什 麼 還 需 要 自 己 寫 一 個 相 同 功 能 的 軟 體 呢? 在 這 裡 我 們 並 不 是 要 你 寫 一 個 程 式 去 跟 ACDSee 打 對 台, 這 篇 文 章 主 要 的 目 的 是 希 望 透 過 這 樣 的 教 學 讓 你 瞭 解, 如 何 把 秀 圖 與 簡 單 的 影 像 處 理 功 能 加 入 既 有 的 軟 體 中, 進 而 擴 充 原 來 軟 體 的 功 能. 你 也 可 以 藉 由 這 篇 文 章, 知 道 顯 示 一 張 圖 片 是 如 此 簡 單 的 事 情. 其 實 如 果 你 有 將 9 月 號 電 腦 王 的 下 載 無 名 小 站 照 片 的 程 式 拿 來 實 作, 很 快 的 你 就 會 發 現 你 需 要 一 個 能 夠 顯 示 圖 片 的 功 能. 本 文 正 好 可 以 當 作 續 集 來 閱 讀. 讓 我 們 一 起 來 學 習 數 位 影 像 處 理 吧! 我 們 希 望 用 電 腦 來 處 理 照 片, 那 麼 起 步 走 的 第 一 件 事 就 是 要 瞭 解 電 腦 如 何 處 理 圖 片. 我 們 希 望 知 道 : 照 片 如 何 被 表 示, 照 片 如 何 被 儲 存, 而 我 們 又 該 怎 麼 去 操 作 它 呢? 顯 示 在 電 腦 螢 幕 上 的 圖 片 或 者 是 照 片, 基 本 上 可 以 用 點 陣 圖 來 表 示. 點 陣 圖 將 圖 片 視 為 一 堆 點 的 方 形 集 合. 我 們 可 以 想 像 電 腦 裡 面 的 照 片 就 是 由 一 個 一 個 像 點 所 組 成 的 一 個 方 形 點 陣 列, 這 些 點 我 們 稱 之 為 像 素 (pixel). 在 一 張 全 彩 (full-color) 或 者 是 真 實 顏 色 (true-color) 點 陣 圖 中, 每 個 像 素 可 以 由 24 個 位 元 (bit) 來 表 示. 也 就 是 紅 色 8 個 位 元, 綠 色 8 個 位 元, 藍 色 8 個 位 元, 總 共 24 個 位 元 可 用 來 表 達 2 24 =16777216 種 顏 色. 所 以 我 們 說 一 個 點 陣 圖 中 的 每 個 像 點 都 以 24 位 元 單 位 儲 存, 稱 為 一 個 24bit BMP 檔. 在.Net Framework 上, 我 們 使 用 PixelFormat 類 別 的 Format24bppRgb 來 表 示 這 種 格 式. 當 然 也 有 些 點 陣 列 圖 使 用 32 bit 來 存 放 一 個 像 素 ( 同 理 可 用 Format32bppRgb 表 示 ), 而 多 出 來 的 那 8 個 bit 通 常 用 來 表 示 透 明 度. 下 面 是 一 個 簡 單 的 24bit BMP 檔 像 點 示 意 圖.

圖 說 : 點 陣 列 影 像 像 點 示 意 圖 圖 檔 : 點 陣 列 影 像 示 意 圖.png 使 用 BMP 檔 案 格 式 表 示 圖 形 資 料, 所 需 的 容 量 可 能 非 常 龐 大. 以 一 張 寬 高 500 x 375 照 片 為 例, 就 需 要 562500 個 位 元 組 的 容 量 儲 存 該 張 照 片. 所 以 大 多 數 的 圖 片 都 使 用 壓 縮 的 方 式, 將 圖 形 儲 存 在 檔 案 中 以 節 省 所 需 要 的 磁 碟 空 間. 當 需 要 的 時 候 才 解 壓 縮 回 適 當 的 格 式 進 行 處 理. 常 見 的 圖 形 檔 案 格 式 有 JPEG, BMP, GIF, PNG. 每 一 種 圖 檔 格 式 都 有 自 己 的 一 套 複 雜 方 法 用 來 表 示 每 一 個 像 點 的 資 訊. 你 可 以 在 網 路 或 坊 間 書 籍 得 到 相 關 圖 檔 格 式 的 詳 細 說 明. 我 們 的 重 點 將 放 在 如 何 寫 程 式 操 作 影 像. 我 們 目 前 只 要 知 道 怎 麼 從 各 種 影 像 檔 格 式 轉 換 成 我 們 能 夠 處 理 的 R( 紅 色 ), G( 綠 色 ), B( 藍 色 ) 模 式. 使 用 程 式 庫 幫 我 們 解 碼 圖 形 檔 案 的 格 式 通 常 是 非 常 的 複 雜, 主 要 的 原 因 是 其 採 用 了 不 同 的 視 覺 模 型 來 解 釋 顏 色 與 資 訊, 同 時 為 了 不 同 的 顯 示 器, 有 些 影 像 格 式 還 跟 顯 示 裝 置 相 關. 例 如 : 最 常 見 的 BMP 就 有 分 為 裝 置 相 關 與 裝 置 無 關 兩 種 模 式, JPEG 則 使 用 一 連 串 複 雜 的 演 算 法, 包 含 離 散 餘 弦 轉 換 (Discrete Cosine Transform) 等, 將 影 像 以 頻 率 的 方 式 表 示, 基 於 人 類 視 覺 對 高 頻 不 敏 感 的 性 質 進 行 對 影 像 的 壓 縮. 除 非 你 是 資 訊 系 的 學 生, 否 則 通 常 我 們 都 是 使 用 影 像 相 關 的 程 式 庫, 幫 我 們 進 行 解 碼 的 動 作, 將 壓 縮 過 的 資 料 重 新 轉 譯 成 一 般 人 比 較 容 易 接 受 的 RGB 色 彩 模 型,

以 便 進 行 影 像 處 理. 如 果 你 不 用 程 式 庫, 那 麼 你 可 能 要 自 己 撰 寫 轉 譯 的 程 式. 對 於 這 種 壓 縮 / 解 壓 縮 程 式, 我 們 稱 之 為 Codec. 事 實 上 這 類 影 像 處 理 的 商 用 程 式 庫 非 常 多, 例 如 : LEADTOOLS 公 司 就 提 供 了 一 組 影 像 處 理 工 具 Raster Imaging SDKs. 這 個 程 式 庫 就 支 援 超 過 150 種 不 同 的 影 像 檔 案 格 式 ( 包 含 了 最 新 的 JPEG 檔 案 格 式 JPEG2000 與 較 少 見 到 的 LZW 格 式 ), 其 中 還 提 供 了 有 關 掃 描 器, 色 彩 轉 換, 與 一 些 有 用 的 高 速 影 像 處 理 功 能. Intel 與 鼎 鼎 有 名 的 MathWorks 公 司 也 都 有 提 供 功 能 強 大 的 影 像 處 理 程 式 工 具. 其 實 只 要 祭 出 Google 大 神, 就 會 列 出 一 堆 前 人 已 經 寫 好 的 免 費 工 具, 你 可 以 輸 入 Image Library 搜 尋 看 看 相 關 的 資 訊. 如 果 你 使 用.Net Framework 搭 配 C# 程 式 語 言, 而 且 呢... 只 想 要 能 夠 讀 取 常 見 影 像 檔. 如 : JPEG, PNG, GIF 或 BMP, 那 根 本 不 用 找 相 關 的 程 式 庫, 因 為 微 軟 已 經 幫 你 寫 好 解 碼 程 式 了. 就 位 於.Net Framework 中 的 Image 類 別, 這 個 類 別 搭 配 Graphics 類 別 一 併 提 供 了 基 本 的 影 像 處 理 工 具, 例 如 : 在 影 像 上 畫 圓, 在 影 像 上 畫 出 指 定 的 字 串 等. 當 然 了 Image 類 別 也 提 供 了 存 檔 的 功 能, 也 就 是 你 可 以 把 Image 物 件 的 影 像 存 成 指 定 的 格 式, 如 jpg, png, gif 或 者 是 bmp 等 常 用 的 格 式. 接 下 來, 我 們 將 從 一 步 一 步 的 介 紹 如 何 使 用 Image 類 別, 來 進 行 簡 單 的 影 像 處 理, 並 針 對 緩 慢 的 SetPixel/ GetPixel 提 出 高 效 率 解 決 方 案, 讓 你 的 影 像 處 理 程 式 更 專 業. 首 先, 我 們 要 秀 出 一 張 影 像. 如 何 秀 出 一 張 影 像? 首 先 我 們 使 用 Visual Studio 建 立 一 個 新 的 專 案 並 且 放 入 一 個 按 鈕. 整 個 流 程 如 下 :

圖 檔 : Visual Studio 2005 建 構 專 案 步 驟 _1

圖 說 : Visual Studio.Net 2005 建 構 專 案 的 步 驟 圖 檔 : Visual Studio 2005 建 構 專 案 步 驟 _2 經 過 一 連 串 的 操 作 後, Visual Studio 會 幫 你 把 整 個 程 式 的 骨 架 建 立 好, 其 中 button1_click 這 個 方 法, 就 是 按 鈕 的 事 件 處 理 函 式. 接 下 來, 我 們 要 寫 一 個 新 的 類 別 專 門 用 來 秀 圖, 給 他 一 個 名 字 叫 做 ImageForm. 假 設 我 們 希 望 讓 載 入 的 圖 片 能 夠 放 在 一 個 獨 立 的 視 窗 中, 以 便 針 對 獨 立 視 窗 中 的 影 像 作 處 理. 所 以 呢... 讓 這 個 秀 圖 類 別 ImageForm 繼 承 Form 應 該 沒 錯! 這 樣 的 設 計 會 使 得 ImageForm 擁 有 視 窗 的 性 質. 也 就 是 說, 當 我 們 建 立 ImageForm 物 件 並 且 顯 示 它 時, 會 以 視 窗 的 方 式 來 呈 現 ImageForm 的 外 觀 與 行 為. 使 用 C# 程 式 語 言 的 寫 法 如 下, // 建 立 一 個 專 門 秀 圖 的 Form 類 別 class ImageForm : Form {

另 外, 當 秀 圖 視 窗 ImageForm 顯 示 在 桌 面 上 時, 希 望 能 立 即 秀 出 我 們 載 入 的 照 片. 所 以 呢... 我 們 應 該 把 載 入 圖 檔 的 程 式 碼 放 在 秀 圖 類 別 的 建 構 子 中. 這 樣 的 安 排 會 使 得 每 次 新 建 立 一 個 ImageForm 物 件, 就 會 載 入 圖 形 檔, 另 外 把 秀 圖 程 式 放 到 OnPaint 方 法 中. 會 使 得 每 次 視 窗 重 繪 時, 都 會 重 新 把 照 片 畫 上 去. 使 得 ImageForm 物 件 的 視 窗 外 表, 就 是 我 們 的 圖 片. 所 以 程 式 碼 的 架 構, 應 該 要 長 的 像 下 面 這 樣. // 建 立 一 個 專 門 秀 圖 的 Form 類 別 class ImageForm : Form { Image image; // 建 構 子 public ImageForm() { // 載 入 影 像 的 程 式 碼 放 在 這 裡... protected override void OnPaint(PaintEventArgs e) { // 顯 示 出 影 像 的 程 式 碼 放 在 這 裡 由 於 前 面 建 立 專 案 時, 拉 了 一 個 按 鈕 進 來. 我 們 作 這 個 動 作 的 目 的 是 希 望 當 使 用 者 按 下 Load 鈕 時, 直 接 開 啟 顯 示 照 片 的 視 窗. 所 以 這 時 候 就 應 該 把 建 構 秀 圖 物 件 的 程 式 碼, 放 到 Load 按 鈕 的 事 件 處 理 函 式 中, 作 法 如 下. // Load 按 鈕 事 件 處 理 函 式 private void button1_click(object sender, EventArgs e) { ImageForm MyImage= new ImageForm(); // 建 立 秀 圖 物 件 MyImage.Show();// 顯 示 秀 圖 照 片 完 整 的 C# 秀 圖 程 式 碼, 列 表 如 下 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Drawing.Imaging; // for ImageFormat namespace WindowsApplication1 {

public partial class Form1 : Form { public Form1() { InitializeComponent(); // Load 按 鈕 事 件 處 理 函 式 private void button1_click(object sender, EventArgs e) { ImageForm MyImage= new ImageForm(); // 建 立 秀 圖 物 件 MyImage.Show();// 顯 示 秀 圖 照 片 // 建 立 一 個 專 門 秀 圖 的 Form 類 別 class ImageForm : Form { Image image; // 建 構 子 public ImageForm() { // Step 1: 載 入 影 像 image = Image.FromFile(@"c:\ 淡 蘭 古 道 _ 山 貂 嶺 段.bmp"); this.text = @"c:\ 淡 蘭 古 道 _ 山 貂 嶺 段.bmp"; protected override void OnPaint(PaintEventArgs e) { // Step 2: 調 整 視 窗 的 大 小 this.height=image.height; this.width=image.width; // Step 3: 顯 示 出 影 像 e.graphics.drawimage(image, 0, 0,Width,Height); 你 可 以 詴 詴 看, 把 image = Image.FromFile(@"c:\ 淡 蘭 古 道 _ 山 貂 嶺 段.bmp"); 改 成 下 面 幾 種 不 同 格 式 的 照 片 檔, 看 看 是 否 能 夠 正 確 秀 出 相 片. image = Image.FromFile(@"c:\ 淡 蘭 古 道 _ 山 貂 嶺 段.gif"); // 載 入 gif image = Image.FromFile(@"c:\ 淡 蘭 古 道 _ 山 貂 嶺 段.png"); // 載 入 png image = Image.FromFile(@"c:\ 淡 蘭 古 道 _ 山 貂 嶺 段.jpg"); // 載 入 jpg

圖 說 : ImageForm 秀 圖 結 果 圖 檔 : ImageForm 秀 圖 結 果.png 另 外, 如 果 你 希 望 顯 示 出 一 個 對 話 框, 讓 使 用 者 選 擇 想 要 處 理 的 影 像 檔. 你 可 以 使 用 OpenFileDialog 類 別. 詳 細 的 作 法 如 下 : // 使 用 OpenFileDialog 開 檔 的 範 例 public partial class Form1 : Form { // < 略 > private void button1_click(object sender, EventArgs e) { // Step 1: 建 立 OpenFiledialog 元 件 OpenFileDialog openfiledialog1 = new OpenFileDialog(); openfiledialog1.initialdirectory = "c:\\"; // Step 2: 顯 示 出 對 話 框 if (openfiledialog1.showdialog() == DialogResult.OK) { // Step 3: 建 立 秀 圖 物 件

ImageForm MyImage = new ImageForm(openFileDialog1.FileName); MyImage.Show();// 顯 示 秀 圖 照 片 CurrentImage = MyImage; class ImageForm : Form { // < 略 > // 建 構 子 public ImageForm(String Filename) { LoadImage(Filename); // 載 入 影 像 公 用 程 式 public void LoadImage(String Filename) { // Step 1: 載 入 影 像 image = Image.FromFile(Filename); this.text = Filename; // 設 定 標 題 // Step 2: 調 整 視 窗 的 大 小 this.height = image.height; this.width = image.width; 圖 說 : 使 用 OpenFileDialog 選 擇 檔 案 圖 檔 : 使 用 OpenFileDialog 選 擇 檔 案.png

放 大 縮 小 形 變 旋 轉 還 記 得 前 面 提 到 每 次 重 繪 視 窗 的 時 候, 都 會 呼 叫 OnPaint 方 法. 如 果 想 要 進 行 影 像 的 幾 何 轉 換, 如 放 大, 縮 小 或 旋 轉 等 功 能, 你 都 可 以 使 用 功 能 強 大 的 Graphics 類 別 方 法 完 成. 其 實 大 部 分 的 畫 圖 函 式, 你 在 Graphics 的 方 法 中 都 可 以 找 到. 例 如 : 畫 線 或 畫 曲 線 你 可 以 使 用 DrawLine 方 法, 畫 圓 你 可 以 使 用 DrawArc, 畫 框 你 可 以 使 用 DrawRectangle 方 法. 在 這 篇 文 章 中, 我 們 將 使 用 Graphics 所 提 供 的 畫 影 像 方 法 DrawImage, 來 進 行 放 大 縮 小 旋 轉 等 扭 轉 影 像 的 功 能 展 示. 下 面 是 DrawImage 方 法 的 原 型. public void DrawImage(Image image, Point[] p); 其 中 第 一 個 參 數 image 為 要 畫 上 的 影 像, 而 Point 陣 列 p 則 標 示 了 要 繪 製 影 像 的 目 標 三 個 幾 何 座 標 點 : p[0] 表 示 目 標 的 影 像 左 上 角 位 置 p[1] 表 示 目 標 的 影 像 右 上 角 位 置 p[2] 表 示 目 標 的 影 像 左 下 角 位 置 如 果 Point[] p 表 示 的 是 正 方 形, 那 畫 出 來 的 影 像 就 是 依 照 比 例 的 正 方 形. 圖 說 : 利 用 DrawImage 將 原 始 影 像 放 大 兩 倍 的 設 定 範 例. 其 中 Height 與 Width 是 原 始 影 像 的 高 與 寬. 圖 檔 : 放 大 兩 倍.png // 放 大 2 倍 程 式 碼 this.height = image.height * 2; this.width = image.width * 2; e.graphics.drawimage(image, new Point[] { new Point(0, 0), new Point(2*image.Width, 0), new Point(0, 2*image.Height) );

// 縮 小 1/2 倍 程 式 碼 e.graphics.drawimage(image, new Point[] { new Point(0, 0), new Point(image.Width/2, 0), new Point(0, image.height/2) ); 如 果 指 定 的 目 標 座 標 為 平 行 四 邊 形, 則 繪 製 出 來 的 影 像 則 是 等 比 例 的 平 行 四 邊 形. 下 面 是 利 用 平 行 四 邊 形 形 變 模 擬 旋 轉 的 範 例. 圖 說 : 形 變 模 擬 旋 轉 範 例 圖 檔 : 形 變 的 示 意 圖.png // 形 變 旋 轉 程 式 碼 (cx 為 視 窗 的 寬 度, cy 為 視 窗 的 高 度 ) e.graphics.drawimage(image, new Point[] { new Point(cx / 2, 0), new Point(cx, cy / 2), new Point(0, cy / 2) ); 讀 取 影 像 的 RGB 值 在 此 之 前, 我 們 使 用 Image 類 別 來 儲 存 影 像 以 及 顯 示 影 像. 其 實.Net Framework 還 提 供 了 另 一 個 類 別 Bitmap. Bitmap 類 別 提 供 了 像 點 (pixel) 操 作 的 服 務, 我 們 可 以 利 用 內 附 GetPixel 方 法 得 到 指 定 位 置 的 R,G,B 顏 色 資 訊. 進 行 點 對 點 的 影 像 處 理. 例 如 我 們 可 以 利 用 這 個 方 法 進 行 彩 色 影 像 轉 成 黑 白 影 像 的 處 理. 我 們 也 可 以 把 整 張 影 像 的 顏 色 資 訊 儲 存 成 一 個 3 維 的 整 數 陣 列, 以 方 便 將 來 的 影 像 處 理 工 作. 下 面 是 實 作 一 個 函 式 getrgbdata 將 影 像 資 料 轉 成 整 數 陣 列 的 範 例. 呼 叫 getrgbdata 將 傳 回 一 個 三 維 陣 列. [0] 代 表 Red, [1] 代 表 Green, [2] 代 表 Blue.

詳 細 程 式 碼, 可 以 像 這 樣 寫 : // 傳 回 RGB 陣 列 資 訊 public int[,,] getrgbdata() { // Step 1: 利 用 Bitmap 將 image 包 起 來 Bitmap bimage = new Bitmap(image); int Height = bimage.height; int Width = bimage.width; int[,,]rgbdata=new int[width,height,3]; // Step 2: 取 得 像 點 顏 色 資 訊 for(int y=0;y<height;y++){ for(int x=0;x<width;x++){ Color color=bimage.getpixel(x,y); rgbdata[x, y, 0] = color.r; rgbdata[x, y, 1] = color.g; rgbdata[x, y, 2] = color.b; return rgbdata; 將 彩 色 照 片 變 成 黑 白 照 片 我 們 舉 一 個 簡 單 的 數 位 影 像 處 理 的 例 子. 假 設 我 們 想 要 把 一 張 彩 色 照 片 變 成 一 張 黑 白 灰 階 顏 色 的 照 片, 基 本 上 只 要 把 整 個 影 像 上, 所 有 像 點 (pixel) 的 R,G,B 值 設 成 一 個 固 定 的 灰 階 值 即 可. 一 般 來 說, 這 個 灰 階 值 可 以 為 R G B. 利 用 GetPixel 取 得 目 前 影 像 的 顏 色 資 料, 3 接 著 使 用 SetPixel 方 法 把 灰 階 值 寫 進 去. 這 樣 就 可 以 完 成 簡 單 的 黑 白 影 像 的 工 作. 詳 細 的 作 法 如 下. public void dogray(int[,,]rgbdata) { // Step 1: 建 立 Bitmap 元 件 Bitmap bimage = new Bitmap(image); int Height = bimage.height; int Width = bimage.width; // Step 2: 設 定 像 點 資 料 for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) {

int gray = (rgbdata[x, y, 0] + rgbdata[x, y, 1] + rgbdata[x, y, 2]) / 3; bimage.setpixel(x, y, Color.FromArgb(gray, gray, gray)); // Step 3: 更 新 顯 示 影 像 image = bimage; this.refresh(); 效 率 問 題 的 討 論 如 果 你 去 實 作 上 面 的 程 式, 你 會 發 現 使 用 Bitmap 類 別 的 SetPixel 與 GetPixel 方 法 存 取 影 像 資 訊 非 常 的 緩 慢. 為 了 解 決 這 個 問 題, 讓 我 們 先 來 分 析 為 什 麼 會 慢? 我 們 已 經 知 道 SetPixel 是 用 來 設 定 顏 色 資 料 用 的, GetPixel 是 用 來 讀 取 顏 色 資 訊 的 方 法. 所 以 若 要 針 對 影 像 中 每 一 個 像 點 作 影 像 處 理, 那 麼 每 取 一 個 像 點 的 彩 色 資 訊, 就 要 呼 叫 一 次 GetPixel, 計 算 後 得 到 新 的 顏 色 值, 也 要 呼 叫 SetPixel 設 定. 若 影 像 中 每 個 座 標 的 彩 色 資 料 都 要 存 取, 舉 一 個 例 子 : 一 個 300*400 大 小 的 影 像, 使 用 SetPixel 與 GetPixel 進 行 顏 色 的 設 定, 將 需 要 120000 *2 =240000 次 的 函 式 呼 叫. 這 還 只 是 影 像 存 取 的 動 作 而 已, 不 包 含 中 間 的 影 像 處 理 函 式 所 需 要 的 時 間, 這 在 效 率 上 是 一 個 驚 人 的 浪 費. 這 也 就 是 為 什 麼 很 少 人 直 接 使 用 這 兩 個 函 式 來 存 取 數 位 影 像 的 顏 色 資 訊. 在 這 裡 有 必 要 提 出 一 個 有 效 率 的 方 法, 來 解 決 這 個 問 題. 如 果 有 學 過 C/C++ 的 人, 應 該 對 這 種 情 況 非 常 熟 悉. 老 朋 友 又 來 了 --- 指 標. 如 果 能 使 用 指 標 直 接 存 取 存 放 在 Bitmap 物 件 中 的 影 像 資 料, 那 就 可 以 免 去 數 以 十 萬 計 的 函 式 呼 叫. 然 而 如 果 你 想 使 用 指 標, 那 你 必 頇 使 用 unsafe 這 個 關 鍵 字, 來 宣 告 使 用 指 標 的 那 些 程 式 段 落 可 能 會 危 害 系 統. 這 在 受 管 理 的 程 式 如 C# 等 語 言 必 頇 特 別 加 以 註 明. 另 外, 如 果 你 想 在 程 式 中 使 用 unsafe 程 式 片 段, 那 麼 整 合 開 發 環 境 (IDE) 也 要 作 相 關 的 設 定, 使 得 該 專 案 允 許 使 用 unsafe 程 式 碼. 一 般 預 設 的 情 況 下, 這 個 選 項 通 常 都 是 disable. 致 能 unsafe 選 項, 在 Visual Studio.Net 2005 的 詳 細 設 定 如 下

圖 說 : unsafe 關 鍵 字 與 Visual Studio 2005 的 相 關 設 定 圖 檔 : unsafe 關 鍵 字.png 當 我 們 在 IDE 上 勾 選 了 允 許 unsafe 程 式 片 段 的 選 項 後, 接 下 來 就 是 講 解 如 何 使 用 指 標 存 取 Bitmap 物 件 中 存 放 的 影 像 資 料. 如 果 你 使 用 以 前 的 C++, 沒 問 題! 那 就 直 接 將 指 標 指 向 目 的 地 就 可 以 直 接 存 取 了. 但 是 在 受 管 理 的 環 境 下, 我 們 要 先 將 指 標 的 目 的 記 憶 體 區 塊 鎖 在 系 統 記 憶 體 中, 以 免 自 動 記 憶 體 管 理 程 式 把 這 整 段 區 塊 移 到 其 他 位 址 去 了. 下 面 是 Bitmap 類 別 提 供 的 鎖 住 記 憶 體 內 容 方 法. [C#] Bitmap:: LockBits 方 法 // 回 傳 該 Bmp 影 像 的 屬 性 public BitmapData LockBits( Rectangle rect, // 指 定 要 鎖 住 的 影 像 範 圍 ImageLockMode flags, // 指 定 記 憶 體 的 拴 鎖 模 式 PixelFormat format // 指 定 這 個 Bitmap 的 資 料 格 式 );

使 用 LockBits 方 法, 會 傳 回 一 個 BitmapData 元 件 表 示 Bitmap 的 內 容, 我 們 來 看 看 BitmapData 類 別 的 成 員, 有 哪 些 資 料 可 以 幫 助 我 們 作 指 標 定 位. Height Bitmap 元 件 的 高 度 ( 以 pixel 為 單 位 ) PixelFormat Bitmap 元 件 內 的 pixel 資 訊 格 式 Resereved 保 留 Scan0 Bitmap 影 像 資 料 的 起 始 位 址 Stride Bitmap 元 件 的 scan 寬 度 ( 單 位 byte) Width Bitmap 元 件 的 寬 度 ( 以 pixel 為 單 位 ) 其 中, 我 們 將 使 用 到 Height, Scan0, Stride, Width 等 重 要 資 訊. 而 Scan0 就 是 影 像 資 料 的 起 始 位 址. 指 標 就 是 要 指 向 這 個 位 址. 綜 合 上 面 的 說 明, 若 想 要 直 接 存 取 Bitmap 元 件 的 內 容, 可 以 分 為 下 面 三 個 簡 單 的 步 驟. Step 1: 鎖 住 Bitmap 整 個 影 像 內 容 因 為 自 動 記 憶 體 管 理 員 的 關 係, 所 以 首 先 我 們 要 先 鎖 住 整 個 影 像 的 內 容. 而 在 記 憶 體 拴 鎖 模 式 方 面, 我 們 設 定 為 ImageLockMode.ReadWrite, 這 是 因 為 我 們 希 望 等 一 會 兒 直 接 讀 取 Bitmap 上 的 影 像 資 料, 經 過 計 算 後, 接 著 直 接 將 結 果 寫 回 Bitmap 影 像 物 件 中. 最 後 我 們 指 定 pixel 目 前 資 料 的 操 作 格 式 為 Format24bppRgb. 程 式 碼 範 例 如 下 : BitmapData bmdata = bimage.lockbits(new Rectangle(0, 0, bimage.width, bimage.height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); Step 2: 取 得 影 像 資 料 的 起 始 位 址 System.IntPtr Scan0 = bmdata.scan0; Step 3: 直 接 利 用 指 標 進 行 影 像 處 理 // 這 是 一 個 顏 色 反 轉 的 範 例 unsafe{ byte* p = (byte*)(void*)scan0; p[0] = (byte)(255 - p[0]); // 彩 色 資 料 反 轉 // 最 後 別 忘 了 解 開 記 憶 體 鎖 bimage.unlockbits(bmdata); 在 這 裡 有 些 細 節 要 提 醒 一 下. 由 於 Bitmap 內 部 存 放 彩 色 像 點 的 方 式 是 以 WORD 為 單 位, 也 就 是 說, 如 果 一 行 上 面 的 像 點 總 和 長 度 不 是 WORD 長 度 的 倍 數, 那 麼 就 會 用 一 些 無 用 的 byte 空 間 補 上 來, 以 便 湊 齊 WORD 的 倍 數. 這 些 bytes 不 表 示 任 何 東 西, 他 們 只 是 單 純 地

用 來 湊 數 用 的. 我 們 稱 這 個 動 作 為 Padding. 這 些 Padding bytes 對 我 們 有 甚 麼 關 係 呢? 有! 我 們 要 注 意 的 是, 在 每 次 移 動 指 標 的 時 候, 每 次 到 一 行 的 最 後 像 點 時, 不 要 忘 記 跳 過 那 些 Padding bytes, 才 能 繼 續 往 下 存 取 像 點 資 訊. 如 果 不 跳 過 Padding bytes 將 會 造 成 錯 誤. 用 圖 來 說 明, 可 能 會 讓 你 更 明 白. 圖 說 : Padding bytes 與 影 像 資 料 的 關 係. 圖 檔 : BitmapData 的 資 料 說 明.png 計 算 每 行 有 幾 個 Padding bytes 長 度 的 計 算 方 式 如 下 : int ByteNumber_Width = bimage.width * 3; // 計 算 每 一 行 後 面 幾 個 Padding bytes int ByteOfSkip = stride - ByteNumber_Width; 高 效 率 影 像 處 理 實 作 -- 顏 色 反 轉 下 面 是 一 個 高 效 率 直 接 使 用 指 標 存 取 影 像 的 程 式 碼 範 例, 以 後 我 們 將 一 直 套 用 這 個 高 效 率 程 式 骨 架, 進 行 一 系 列 的 影 像 處 理. // 高 效 率 反 轉 圖 片 // 修 改 與 參 考 Christian Graus 先 生 的 文 章 // 詳 見 http://www.codeproject.com/cs/media/csharpgraphicfilters11.asp public static bool Invert(Bitmap bimage) { // Step 1: 先 鎖 住 存 放 圖 片 的 記 憶 體 BitmapData bmdata = bimage.lockbits(new Rectangle(0, 0, bimage.width, bimage.height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = bmdata.stride; // Step 2: 取 得 像 點 資 料 的 起 始 位 址

System.IntPtr Scan0 = bmdata.scan0; // 計 算 每 行 的 像 點 所 佔 據 的 byte 總 數 int ByteNumber_Width = bimage.width * 3; // 計 算 每 一 行 後 面 幾 個 Padding bytes int ByteOfSkip = stride - ByteNumber_Width; // Step 3: 直 接 利 用 指 標, 更 改 圖 檔 的 內 容 int Height = bimage.height; unsafe { byte* p = (byte*)(void*)scan0; for (int y = 0; y < Height; y++) { for (int x = 0; x < ByteNumber_Width; x++) { p[0] = (byte)(255 - p[0]); // 彩 色 資 料 反 轉 ++p; p += ByteOfSkip; // 跳 過 剩 下 的 Padding bytes bimage.unlockbits(bmdata); return true; 現 在 請 你 把 以 前 的 程 式 改 寫, 你 會 發 現 使 用 指 標 這 個 方 法 相 當 有 效 率, 原 本 數 十 秒 的 影 像 處 理 動 作, 現 在 可 能 不 到 一 秒 就 可 以 完 成. 由 此 可 知, 數 十 萬 次 的 SetPixel/GetPixel 函 式 呼 叫 是 多 麼 的 浪 費 時 間. 當 然, 我 們 付 出 的 代 價 是 如 果 沒 有 好 好 思 考 程 式 的 行 為, 可 能 會 讓 程 式 不 安 全 地 執 行. 另 外, 程 式 的 可 讀 性 也 變 差 了. 下 面 是 顏 色 反 轉 與 灰 階 化 影 像 執 行 的 結 果.

圖 說 : 去 除 彩 色 以 及 顏 色 反 轉 的 結 果 圖 檔 : 去 除 彩 色 以 及 顏 色 反 轉 的 結 果.png 寫 過 C++ 程 式 語 言 的 人 都 知 道 指 標 不 易 掌 控. 為 了 讓 程 式 的 可 讀 性 與 安 全 性 問 題, 鎖 在 一 個 區 域 不 讓 它 擴 散 在 程 式 碼 的 每 一 個 角 落, 一 個 有 效 的 方 法 就 是 建 造 高 效 率 的 影 像 處 理 轉 換 函 式 集, 把 所 有 關 於 指 標 部 分 不 安 全 的 程 式 碼 放 在 這 些 固 定 的 函 式 中, 而 所 有 的 影 像 處 理 程 式 則 完 全 建 立 在 這 些 工 具 上. 這 樣 做 的 目 的 是 當 發 生 問 題 時, 我 們 可 以 很 容 易 的 把 目 光 焦 聚 在 這 些 函 式, 並 進 行 檢 視. 高 效 率 影 像 處 理 工 具 由 前 面 的 解 說, 我 們 知 道 利 用 指 標 的 直 接 存 取 影 像 資 料 威 力, 現 在 我 們 再 把 剛 剛 使 用 SetPixel/GetPixel 完 成 的 讀 取 影 像 RGB 值 的 函 式, 改 寫 成 高 效 率 版 本 的 setrgbdata 與 getrgbdata. 我 們 希 望 把 所 有 不 安 全 的 程 式 都 封 裝 在 getrgbdata_unsafe() 以 及 setrgbdata_unsafe() 上 面. 由 於 篇 幅 的 關 係, 所 以 我 們 只 列 出 高 效 率 版 本 讀 取 影 像 資 料 程 式, 如 果 你 想 要 知 道 如 何 setrgbdata_unsafe() 如 何 實 作, 請 至 電 腦 王 下 載 完 整 程 式 範 例. ( 高 效 率 版 本 ) 讀 取 影 像 資 料 程 式. // 高 效 率 圖 形 轉 換 工 具 -- 讀 取 影 像 資 料

public int[,,] getrgbdata_unsafe() { Bitmap bimage = new Bitmap(image); return getrgbdata(bimage); public static int[,,] getrgbdata(bitmap bimage) { // Step 1: 先 鎖 住 存 放 圖 片 的 記 憶 體 BitmapData bmdata = bimage.lockbits(new Rectangle(0, 0, bimage.width, bimage.height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); int stride = bmdata.stride; // Step 2: 取 得 像 點 資 料 的 起 始 位 址 System.IntPtr Scan0 = bmdata.scan0; // 計 算 每 行 的 像 點 所 佔 據 的 byte 總 數 int ByteNumber_Width = bimage.width * 3; // 計 算 每 一 行 後 面 幾 個 Padding bytes int ByteOfSkip = stride - ByteNumber_Width; int Height = bimage.height; int Width = bimage.width; int[,,] rgbdata = new int[width, Height, 3]; // Step 3: 直 接 利 用 指 標, 把 影 像 資 料 取 出 來 // 請 注 意 先 取 B 然 後 是 G, 最 後 才 是 R unsafe { byte* p = (byte*)(void*)scan0; for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { rgbdata[x, y, 2] = p[0]; // B ++p; rgbdata[x,y,1]=p[0]; // G ++p; rgbdata[x,y,0]=p[0]; // R ++p; p += ByteOfSkip; // 跳 過 剩 下 的 Padding bytes bimage.unlockbits(bmdata); return rgbdata;

在 這 裡 要 特 別 說 明 一 下, 因 為 Windows 24bit Bitmap 圖 檔 格 式 在 R,G,B 的 顏 色 排 列 方 式, 是 先 放 B, 中 間 放 G 最 後 才 是 放 R, 也 就 是 說 從 低 位 元 到 高 位 元 的 排 列 方 式 為 B,G,R. 所 以 我 們 使 用 讀 取 顏 色 的 順 序 也 要 依 照 此 順 序 一 筆 一 筆 的 讀 回 來. 現 在 有 了 getrgbdata_unsafe() / setrgbdata_unsafe() 這 兩 個 函 式 工 具, 我 們 就 可 以 將 心 力 集 中 在 數 位 影 像 的 彩 色 資 訊 上, 而 非 影 像 的 特 殊 檔 案 格 式 上 打 轉. 其 實 東 西 只 要 一 複 雜, 那 就 容 易 出 錯. 這 是 一 個 不 變 的 道 理. 現 在 就 馬 上 寫 一 個 顏 色 濾 鏡 的 程 式 出 來, 給 大 家 看 看. 彩 色 濾 鏡 實 作 基 本 上, 一 個 紅 色 濾 鏡 程 式, 就 是 對 每 一 個 像 點 只 留 下 紅 色 資 訊, 其 他 顏 色 的 資 訊 全 部 設 定 為 0. 很 簡 單 吧. 詳 細 的 作 法 如 下 : // 高 效 率 影 像 處 理 -- 紅 色 濾 鏡 範 例 private void button4_click(object sender, EventArgs e) { // Step 1: 取 出 顏 色 資 料 int[,,] rgbdata = CurrentImage.getRGBData_unsafe(); // Step 2: 數 位 影 像 處 理 // 將 所 有 顏 色 Channel 資 料 改 成 0, 只 留 下 紅 色 資 訊 int Width = rgbdata.getlength(0); int Height = rgbdata.getlength(1); // int r; for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { // r=rgbdata[x, y, 0]; // Red Channel 保 留 不 動 rgbdata[x, y, 1] = 0; // Green Channel 改 成 0 rgbdata[x, y, 2] = 0; // Blue Channel 改 成 0 // Step 3: 將 處 理 後 的 資 料 寫 回 CurrentImage CurrentImage.setRGBData_unsafe(rgbData); 因 為 篇 幅 的 關 係, 綠 色 與 藍 色 濾 鏡 的 程 式 實 作, 就 留 給 各 位 當 作 業. 我 們 先 來 看 看 彩 色 濾 鏡 的 執 行 結 果 :

圖 說 : 高 效 能 影 像 處 理 彩 色 濾 鏡 圖 檔 : 高 效 能 影 像 處 理 彩 色 濾 鏡.png 增 加 亮 度 實 作 對 於 一 張 很 暗 的 數 位 照 片, 如 果 想 要 增 加 亮 度 最 簡 單 的 方 法 就 是 對 R G B 加 入 一 個 固 定 的 亮 度 值. 例 如 我 們 希 望 增 加 的 亮 度 值 為 30, 那 麼 作 法 就 是 R+30, G+30, B+30. 這 麼 簡 單. 然 而 在 這 個 想 法 中, 有 個 關 鍵 那 就 是 你 必 頇 檢 查 [ 增 加 亮 度 的 結 果 是 否 超 過 可 儲 存 的 容 量 範 圍 ]. 為 什 麼 要 作 檢 查 呢? 原 因 是 在 24bit BMP 影 像 格 式 的 情 況 下, 那 麼 每 一 個 顏 色 只 使 用 8 個 位 元 存 放, 也 就 是 說 一 像 點 的 顏 色 分 量 只 能 表 示 2 8 256種 顏 色 變 化, 可 存 放 的 顏 色 範 圍 為 [0 255], 所 以 當 你 存 放 的 值 超 過 255 時, 就 會 產 生 錯 誤. 這 裡 要 特 別 的 注 意. 檢 查 數 值 大 小 的 程 式 碼, 列 表 如 下 : // 檢 查 運 算 結 果 g 的 大 小 if (g > 255)

g = 255; if(g<0) g=0; 完 整 的 增 加 亮 度 程 式 作 法 如 下 : // 高 效 率 影 像 處 理 範 例 -- 增 加 亮 度 30 private void button7_click(object sender, EventArgs e) { // Step 1: 取 出 顏 色 資 料 int[,,] rgbdata = CurrentImage.getRGBData_unsafe(); int Width = rgbdata.getlength(0); int Height = rgbdata.getlength(1); // Step 2: 增 加 亮 度 30 int g; for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { for (int c = 0; c < 3; c++) { g = rgbdata[x, y, c]; g += 30; if (g > 255) g = 255; rgbdata[x, y, c] = g; // Step 3: 將 處 理 後 的 資 料 寫 回 CurrentImage CurrentImage.setRGBData_unsafe(rgbData); 下 面 是 執 行 的 結 果.

圖 說 : 左 邊 是 比 較 暗 的 原 圖, 右 邊 是 將 原 圖 每 個 點 加 上 30 的 結 果 圖 檔 : 增 加 亮 度.png 影 像 存 檔 接 下 來, 要 如 何 將 影 像 處 理 的 結 果 存 起 來 呢? 使 用.Net Framework 的 好 處 是 微 軟 都 幫 你 寫 好 了 存 檔 的 相 關 程 式 碼, 只 要 你 呼 叫 Image 類 別 的 Save 方 法 即 可 完 成. 部 分 存 檔 格 式 使 用 範 例 如 下. image.save(@"c:\result.bmp", ImageFormat.Bmp); image.save(@"c:\result.gif", ImageFormat.Gif); image.save(@"c:\result.png", ImageFormat.Png); image.save(@"c:\result.jpg", ImageFormat.Jpeg); image.save(@"c:\result.exif", ImageFormat.Exif); 圖 說 : 影 像 處 理 存 檔 的 結 果 圖 檔 : 影 像 處 理 存 檔 的 結 果.png 結 論

這 次 我 們 介 紹 了 如 何 使 用.Net Framework 搭 配 C# 程 式 語 言, 進 行 簡 單 的 影 像 處 理, 我 們 也 提 到 了 如 何 使 用 指 標 直 接 存 取 影 像 內 容, 提 高 影 像 處 理 的 效 率. 科 學 家 始 終 企 圖 將 人 類 的 智 能 轉 移 到 電 腦 上, 利 用 機 械 自 動 化 取 代 人 類 的 大 腦. 這 樣 的 努 力 過 去 稱 之 為 人 工 智 能 或 人 工 智 慧. 近 年 來, 人 們 轉 而 將 心 力 專 注 在 特 定 的 領 域 上. 例 如 : 自 動 車 導 航, 自 動 文 字 辨 識, 自 動 人 臉 偵 測 與 辨 識, 自 動 尋 找 電 路 版 上 的 瑕 疵 等 專 門 領 域 的 應 用. 在 這 些 領 域 上 的 應 用 很 多 都 已 經 有 良 好 的 商 業 用 途. 影 像 處 理 可 說 是 許 多 應 用 的 第 一 步. 今 天 教 大 家 的 內 容 是 影 像 處 理 的 墊 腳 石, 希 望 大 家 有 所 收 穫. 下 一 期 我 們 將 會 介 紹 如 何 使 用 Java 進 行 簡 單 的 影 像 處 理. 指 標 的 概 念 是 什 麼, 在 電 腦 程 式 語 言 中, 我 們 可 以 用 變 數 來 存 放 不 同 型 態 的 資 料. 如 果 我 們 要 用 一 個 變 數 來 存 放 整 數, 那 麼 我 們 就 要 宣 告 一 個 整 數 型 態 的 變 數. 例 如 : int 這 個 資 料 型 態 是 用 來 存 放 整 數 資 料. C# 程 式 的 寫 法 就 像 int x=1234; 這 樣. 把 1234 這 個 整 數 值 存 放 到 x 這 個 整 數 型 態 的 變 數 中. 除 了 整 數 之 外, 還 有 一 些 用 來 存 放 其 他 的 資 料 型 態 變 數, 如 浮 點 數 float, 位 元 組 byte 等. 然 而, 指 標 是 一 種 資 料 型 態, 與 其 他 型 態 不 同 的 是 指 標 變 數 型 態, 指 明 了 該 變 數 是 專 門 用 來 存 放 記 憶 體 的 位 址. 我 們 都 知 道 在 電 腦 中, 任 何 資 料 全 部 都 是 存 在 記 憶 體 中. 而 電 腦 中 的 每 個 記 憶 體 空 間 都 有 一 個 唯 一 的 序 號 對 應. 這 就 好 像 每 個 房 屋 都 有 一 個 地 址 一 樣, 可 供 我 們 經 由 地 址 找 到 朋 友. 這 意 味 著 我 們 可 以 有 兩 種 方 式 來 讀 取 或 寫 入 資 料 到 記 憶 體 中. 第 一 : 直 接 使 用 變 數 名 稱 明 確 的 讀 取 存 在 記 憶 體 中 的 資 料 進 行 運 算, 例 如 : int y=x+1; 就 是 把 x 變 數 中 的 資 料 讀 取 出 來 加 上 1 然 後 存 放 到 y 變 數 中. 而 變 數 各 自 代 表 者 不 同 的 記 憶 體 空 間. 另 外, 我 們 也 可 以 利 用 指 定 記 憶 體 位 址 的 方 式 來 讀 取 寫 入 資 料. 例 如 ; unsafe{ int x=1234; int* px=&x; // 取 得 x 變 數 的 記 憶 體 位 址, 存 放 到 px 指 標 變 數 中. int y=*px+1; // 取 得 指 定 記 憶 體 位 址 中 的 資 料 (1234) 加 1 放 到 y 變 數 中. 現 在 我 們 來 討 論 為 什 麼 要 這 麼 麻 煩, 引 入 指 標 這 樣 的 觀 念. 其 實 一 點 都 不 麻 煩!! 為 什 麼 用 了 以 後 會 提 升 存 取 效 率? 電 腦 記 憶 體 是 一 塊 連 續 的 空 間. 所 謂 的 連 續 空 間 指 的 是 下 一 筆 資 料 的 位 址 與 目 前 資 料 的 位 址 是 一 種 遞 增 的 數 值. 這 個 觀 念 很 重 要. 這 意 味 著 當 我 們 存 放 的 資 料 是 大 筆 資 料 的 時 候, 使 用 指 標 就 特 別 的 有 用. 例 如 : 圖 片 的 資 料, 就 是 大 筆 資 料 的 一 個 例 子 它 就 佔 據 了 大 片 連 續 的 記 憶 體. 所 以 假 設 我 們 的 指 標 變 數 px 內 存 放 的 是 第 一 個 pixel 的 記 憶 體 位 址, 如 果 想 要 存 取 其 中 下 一 個 pixel 的 內 容, 很 簡 單, 只 要 將 px 遞 增 就 可 以 存 取 下 一 筆 資 料 的 內 容.

利 用 指 標 存 取 圖 形 資 料 比 起 呼 叫 GetPixel/SetPixel 方 法, 還 要 直 覺 與 有 效 率. 使 用 Image 類 別 的 方 法 進 行 影 像 處 理, 就 好 像 是 每 次 要 取 得 圖 形 資 料 時 都 要 經 過 一 個 代 理 人 幫 我 們 處 理. 如 下 圖 : @ 圖 GetPixel.png @ 圖 說 : 使 用 GetPixel 的 方 法, 讀 取 兩 筆 影 像 資 料 的 示 意 圖 然 而 使 用 指 標 呢, 則 是 不 需 要 代 理 人 Image 物 件, 我 們 長 驅 直 入 影 像 資 料 的 儲 存 地 點, 把 資 料 讀 出 來. 下 面 為 指 標 連 續 存 取 三 個 影 像 點 的 示 意 圖. 是 不 是 比 較 快 比 較 直 接? 圖 說 : 使 用 指 標 直 接 讀 取 三 筆 影 像 資 料 @ 圖 : pointer.png 為 什 麼 指 標 的 程 式 段 落 會 是 危 險 的 呢? 指 標 是 個 既 危 險 又 吸 引 人 的 工 具, 在 電 腦 的 世 界 裡, 只 要 使 用 指 標 變 數, 你 就 可 以 對 該 記 憶 體 位 址 的 內 容 進 行 寫 入 或 讀 取 的 行 為. 這 對 系 統 來 說 是 非 常 危 險 的, 一 個 錯 誤 的 存 取 可 能 覆 寫 了 底 層 正 在 執 行 的 共 通 語 言 執 行 環 境 CLR (Common Language Runtime) 的 狀 態, 進 而 導 致 程 式 以 不 預 期 的 方 式 執 行, 可 能 意 外 當 掉 或 者 發 生 不 可 預 期 的 問 題, 這 種 錯 誤 無 法

預 防. 所 以 這 也 就 是 為 甚 麼 使 用 指 標 的 程 式 難 以 除 錯 的 主 要 原 因. 新 一 代 的 程 式 語 言 C# 強 迫 使 用 者 加 入 unsafe 關 鍵 字 標 明 他 知 道 這 段 程 式 碼 是 不 安 全 的, 並 且 在 整 合 環 境 中 加 入 致 能 使 用 unsafe 的 選 項. 何 謂 自 動 記 憶 體 管 理? 在 C# 程 式 中, 為 何 要 將 記 憶 體 鎖 住 呢? 一 般 來 說, 任 何 使 用 C# 所 寫 的 程 式 碼, 我 們 稱 之 為 列 管 的 程 式 碼 (Managed Code), 這 些 列 管 的 程 式 受 到 底 層 的 共 通 語 言 執 行 環 境 (CLR) 所 控 管. CLR 讓 程 式 設 計 師 不 用 再 去 管 理 記 憶 體 以 及 配 置 與 刪 除 記 憶 體 等 工 作. 取 而 代 之 的 是 由 一 個 自 動 記 憶 體 管 理 員 幫 你 作 這 些 事 情. 由 程 式 接 手 管 理 記 憶 體 的 好 處 是 我 們 完 全 可 以 避 開 因 為 記 憶 體 管 理 所 造 成 的 種 種 問 題. 例 如 : 程 式 員 配 置 記 憶 體 後, 忘 記 執 行 釋 放 記 憶 體 的 指 令, 這 種 問 題 稱 為 記 憶 體 漏 失 (Memory Leak) 問 題. 陣 列 溢 出 存 取 (Array access overflows) 問 題, 也 因 為 CLR 的 自 動 記 憶 體 管 理 機 制 而 獲 得 完 全 的 預 防. 這 些 問 題 在 C/C++ 所 寫 的 程 式 中 特 別 常 見, 只 要 一 出 錯, 往 往 不 是 當 機 收 場, 就 是 程 式 突 然 間 掛 掉. 最 近 的 程 式 語 言 與 軟 體 系 統 架 構 的 發 展 趨 勢, 已 經 傾 向 以 自 動 化 的 方 式 管 理 記 憶 體, 畢 竟 以 手 動 方 式 管 理 記 憶 體 的 代 價 實 在 太 高 了..Net Framework 是 一 個 新 的 架 構, 包 含 了 CLR 與 一 個 名 為.Net Framework 的 類 別 程 式 庫. 所 有 在 CLR 上 面 接 受 管 理 的 程 式, 記 憶 體 全 部 交 給 自 動 管 理 員 幫 你 管 理. 這 意 味 著 你 的 資 料 可 能 不 會 永 遠 固 定 存 在 於 某 個 地 方. 只 要 在 Managed 堆 積 中 有 位 址 空 間 可 用,CLR 就 會 繼 續 為 新 物 件 配 置 記 憶 體. 但 是, 記 憶 體 是 有 限 的 最 後,CLR 中 記 憶 體 回 收 行 程 還 是 必 頇 進 行 回 收 以 釋 放 某 些 記 憶 體 我 們 在 使 用 指 標 的 時 候, 就 必 頇 確 保 這 個 位 址 所 參 考 到 的 資 料, 不 會 受 制 於 記 憶 體 回 收 行 程 的 重 新 配 置 或 配 置 所 以 在 這 篇 文 章 中, 我 們 利 用 指 標 直 接 存 取 圖 檔 資 料, 必 頇 要 先 用 Bmpimage LockBits 把 圖 檔 資 料 鎖 定 在 系 統 記 憶 體 中, 等 到 存 取 完 畢 後, 才 進 行 解 鎖 的 動 作. 井 民 全 國 立 交 通 大 學 自 動 化 資 訊 處 理 實 驗 室 專 長 : 數 位 影 像 處 理 Email:mqJing@msn.com 個 人 網 頁 :http://debut.cis.nctu.edu.tw/~ching ------------------------------------- 附 錄 一 : 圖 片 及 圖 片 圈 示 點 陣 列 影 像 示 意 圖.png

Visual Studio 2005 建 構 專 案 步 驟 _1.png Visual Studio 2005 建 構 專 案 步 驟 _2.png ImageForm 秀 圖 結 果.png 使 用 OpenFileDialog 選 擇 檔 案.png 放 大 兩 倍.png 形 變 的 示 意 圖.png unsafe 關 鍵 字.png BitmapData 的 資 料 說 明.png 去 除 彩 色 以 及 顏 色 反 轉 的 結 果.png 高 效 能 影 像 處 理 彩 色 濾 鏡.png 增 加 亮 度.png 影 像 處 理 存 檔 的 結 果.png 參 考 資 料 : [1] JPEG (Joint Photographic Experts Group) 官 方 網 站 http://www.jpeg.org/ [2] 有 關 BMP (Windows bitmap) 的 詳 細 資 訊, 請 參 閱 http://en.wikipedia.org/wiki/windows_bitmap [3] GIF(Graphics Interchange Format), 可 參 閱 GIF89a 的 規 格 書 http://www.w3.org/graphics/gif/spec-gif89a.txt [4] PNG (Portable Network Graphics) 的 相 關 資 訊, 官 方 網 站 : http://www.libpng.org/pub/png/ [5] LEADTOOLS 公 司 的 網 址 http://www.leadtools.com/ [6] Christian Graus 先 生 的 文 章 http://www.codeproject.com/cs/media/csharpgraphicfilters11.asp