Open Pin-Jiun opened 1 year ago
功能列表
# ------ My photoshop Release notes ------ # # ver2.0 (2020.10.11) 1. 新增視窗 `My histogram`,可以隨時查看現在圖片的 RGB直方圖 2. 新增視窗 `My color panel`,可以自己選顏色、自己畫點 # ver1.0 (2020.10.10) 1. 新增儲存檔案功能 2. 離開應用程式的穩定優化 3. 新增關閉程式的文字提示 4. 新增調整光線、對比度功能 5. 新增調整明度、飽和度功能 6. 新增旋轉圖片功能 7. 新增可增加圖片噪點的功能,相當於增加圖片顆粒感
1-1. 建立新視窗 “My histogram”
# 直方圖視窗 cv2.namedWindow("My histogram", window_flags) # cv2.WINDOW_NORMAL)
1-2. 使用之前教學的函數,畫出圖片 RGB直方圖
plot_histogram = show_histogram(img_copy) # 顯示調整後的效果 cv2.imshow("My histogram", plot_histogram)
1-3. 最大難題:如何把 plot 變成 圖片? 這問題才是這次更新功能最大的難關啊, 我們之前絕大部分的plot都是直接顯示在 jupyter notebook 中, 現在要把它變成圖片移出來,該怎麼辦呢?
1-3-1. (failed) 嘗試使用 plt.show() plt.show() 我記得可以直接另外開啟視窗產生出圖片繪製結果, 但在 jupyter notebook 中,似乎沒辦法這樣做。
1-3-2. (passed?) 儲存 plt 為 jpg 檔後,再用 OpenCV 讀取 看標題就知道一定會成功了, 總之這也是很直覺的方法,直接儲存圖片後再讀取圖片, 但這會有一個很大的問題,我們的硬碟無時無刻都會一直在讀寫…
長久下來絕對不是一個好的方案 (除非想更快換硬碟)
1-3-3. (最佳化) 儲存 plt 進 memory buffer 後,再讀取出來 這個方法的精隨在於,他並沒有實際上做儲存檔案的動作, 而只是把這些資訊暫時放在記憶體的某個位置上, 而我們能用如同 1-3-2. 的方法把圖片讀取出來。
def show_histogram(img): fig, ax = plt.subplots() # 畫出 RGB 三種顏色的分佈圖 color = ('b','g','r') plt.style.use('dark_background') for idx, color in enumerate(color): histogram = cv2.calcHist([img],[idx],None,[256],[0, 256]) ax.plot(histogram, color = color) s, (width, height) = fig.canvas.print_to_buffer() plot_histogram = np.frombuffer(s, np.uint8).reshape((height, width, 4)) return plot_histogram
這個不論是 photoshop 或 小畫家 應該都有的功能吧! 我們先新增一個能夠顯示顏色的視窗:
2-1. 建立新視窗 “My color panel”
# 控制顏色 cv2.namedWindow("My color panel", window_flags) # cv2.WINDOW_NORMAL)
2-2. 建立 RGB滑動條,並顯示對應顏色
# create trackbars for color change cv2.createTrackbar('R','My color panel', 0, 255, nothing) cv2.createTrackbar('G','My color panel', 0, 255, nothing) cv2.createTrackbar('B','My color panel', 0, 255, nothing) def color_panel(img_copy): data = {} data['r'] = cv2.getTrackbarPos('R','My color panel') data['g'] = cv2.getTrackbarPos('G','My color panel') data['b'] = cv2.getTrackbarPos('B','My color panel') color_rgb = np.zeros((300,400,3), np.uint8) color_rgb[:] = [data['b'], data['g'], data['r']] # 改變顯示 window 的內容 cv2.imshow('My color panel', color_rgb) cv2.resizeWindow("My color panel", 400, 300) return data['img']
使用 cv2.getTrackbarPos 得到顏色的回傳值,並使用 color_rgb[:] = [data['b'], data['g'], data['r']],組合顏色並顯示顏色
2-3. 建立打點用的筆,並對圖片作畫
這邊我特別說明我們需要製作一個開關,來偵測現在是否開啟打點模式。
製作開關:
cv2.createTrackbar('Points','My color panel', 0, 1, nothing)
偵測開關
data['points'] = cv2.getTrackbarPos('Points','My color panel')
如果開關打開,才是開啟打點模式:
if data['points'] == 1: # 標記點位置 cv2.circle(data['img'], (x,y), 3, (data['b'], data['g'], data['r']), 5, 16) # 顯示修改的 (x,y) 位置 print("change points: (x, y) = ({}, {})".format(x, y))
2-4. 最後,注意 imshow 圖片更新邏輯 我們需要注意一件事情, 昨天我們的內容大多數都是「從原圖」出發,依據對應值開始調整畫面,
但這次的打點內容,是需要被保存在圖片上的。 因此我們必須要修改這部分的程式邏輯,
img = color_panel(img) img_copy = np.copy(img)
我們每次進行打點時,更新原來的圖片, 而做其他變化時,不更新原來的圖片,從原圖片出發開始計算。
code如下
import cv2 import glob import time import math import io import PIL from IPython.display import clear_output import matplotlib import matplotlib.pyplot as plt import numpy as np def resize_img(img, scale_percent): # percent of original size width = int(img.shape[1] * scale_percent / 100) height = int(img.shape[0] * scale_percent / 100) dim = (width, height) resize_img = cv2.resize(img, dim, interpolation = cv2.INTER_AREA) return resize_img def rotate_img(img, rotation=0): (h, w, d) = img.shape center = (w // 2, h // 2) # 第一個參數旋轉中心,第二個參數旋轉角度(-順時針/+逆時針),第三個參數縮放比例 M = cv2.getRotationMatrix2D(center, rotation, 1.0) # 第三個參數變化後的圖片大小 rotate_img = cv2.warpAffine(img, M, (w, h)) return rotate_img def gaussian_noise(img, mean=0, sigma=0.1): # int -> float (標準化) img = img / 255.0 # 隨機生成高斯 noise (float + float) noise = np.random.normal(mean, sigma, img.shape) # noise + 原圖 gaussian_out = img + noise # 所有值必須介於 0~1 之間,超過1 = 1,小於0 = 0 gaussian_out = np.clip(gaussian_out, 0, 1) # 原圖: float -> int (0~1 -> 0~255) gaussian_out = np.uint8(gaussian_out*255) # noise: float -> int (0~1 -> 0~255) noise = np.uint8(noise*255) return gaussian_out def modify_contrast_and_brightness(img, brightness=0 , contrast=0): B = brightness / 255.0 c = contrast / 255.0 k = math.tan((45 + 44 * c) / 180 * math.pi) img = (img - 127.5 * (1 - B)) * k + 127.5 * (1 + B) # 所有值必須介於 0~255 之間,超過255 = 255,小於 0 = 0 img = np.clip(img, 0, 255).astype(np.uint8) return img def modify_lightness_saturation(img, lightness = 0, saturation = 0): # lightness 調整為 "1 +/- 幾 %" # saturation 調整為 "1 +/- 幾 %" origin_img = img # 圖像歸一化,且轉換為浮點型 fImg = img.astype(np.float32) fImg = fImg / 255.0 # 顏色空間轉換 BGR -> HLS hlsImg = cv2.cvtColor(fImg, cv2.COLOR_BGR2HLS) hlsCopy = np.copy(hlsImg) # 亮度調整 hlsCopy[:, :, 1] = (1 + lightness / 100.0) * hlsCopy[:, :, 1] hlsCopy[:, :, 1][hlsCopy[:, :, 1] > 1] = 1 # 應該要介於 0~1,計算出來超過1 = 1 # 飽和度調整 hlsCopy[:, :, 2] = (1 + saturation / 100.0) * hlsCopy[:, :, 2] hlsCopy[:, :, 2][hlsCopy[:, :, 2] > 1] = 1 # 應該要介於 0~1,計算出來超過1 = 1 # 顏色空間反轉換 HLS -> BGR result_img = cv2.cvtColor(hlsCopy, cv2.COLOR_HLS2BGR) result_img = ((result_img * 255).astype(np.uint8)) return result_img def show_histogram(img): fig, ax = plt.subplots() # 畫出 RGB 三種顏色的分佈圖 color = ('b','g','r') # plt.ion() plt.style.use('dark_background') for idx, color in enumerate(color): histogram = cv2.calcHist([img],[idx],None,[256],[0, 256]) ax.plot(histogram, color = color) # ax.xlim([0, 256]) # fig.canvas.tostring_rgb() # plt.savefig("plot_histogram.jpg") # plot_histogram = cv2.imread('plot_histogram.jpg') s, (width, height) = fig.canvas.print_to_buffer() # Option 2a: Convert to a NumPy array. plot_histogram = np.frombuffer(s, np.uint8).reshape((height, width, 4)) # print(plot_histogram.shape) return plot_histogram def color_panel(img_copy): def mouse_handler(event, x, y, flags, data): if event == cv2.EVENT_LBUTTONDOWN: if data['points'] == 1: # 標記點位置 cv2.circle(data['img'], (x,y), 3, (data['b'], data['g'], data['r']), 5, 16) # 顯示修改的 (x,y) 位置 print("change points: (x, y) = ({}, {})".format(x, y)) else: pass # do nothing data = {} data['img'] = img_copy.copy() data['r'] = cv2.getTrackbarPos('R','My color panel') data['g'] = cv2.getTrackbarPos('G','My color panel') data['b'] = cv2.getTrackbarPos('B','My color panel') data['points'] = cv2.getTrackbarPos('Points','My color panel') color_rgb = np.zeros((300,400,3), np.uint8) color_rgb[:] = [data['b'], data['g'], data['r']] # 利用滑鼠回傳值,資料皆保存於 data dict中 cv2.setMouseCallback("My photoshop", mouse_handler, data) # 改變顯示 window 的內容 # cv2.imshow("My photoshop", data['img']) cv2.imshow('My color panel', color_rgb) cv2.resizeWindow("My color panel", 400, 300) return data['img'] # 讀取彩色圖像 img = cv2.imread('./testdata/cat.jpg', cv2.IMREAD_COLOR) h, w, dim = img.shape # control analysis size if h>=1000 or w>=1000: img = resize_img(img, scale_percent=30) half_MAX_VALUE = 100 half_BIG_MAX_VALUE = 255 window_flags = cv2.WINDOW_AUTOSIZE| cv2.WINDOW_KEEPRATIO # 修圖主視窗 cv2.namedWindow("My photoshop", window_flags) # cv2.WINDOW_NORMAL) # 直方圖視窗 cv2.namedWindow("My histogram", window_flags) # cv2.WINDOW_NORMAL) # 控制顏色 cv2.namedWindow("My color panel", window_flags) # cv2.WINDOW_NORMAL) def nothing(*arg): pass # create trackbars for color change cv2.createTrackbar('R','My color panel', 0, 255, nothing) cv2.createTrackbar('G','My color panel', 0, 255, nothing) cv2.createTrackbar('B','My color panel', 0, 255, nothing) cv2.createTrackbar('Points','My color panel', 0, 1, nothing) # 滑動塊 cv2.createTrackbar("rotation", "My photoshop", 180, 2*180, nothing) cv2.createTrackbar("brightness", "My photoshop", half_MAX_VALUE, 2*half_MAX_VALUE, nothing) # 亮度 cv2.createTrackbar("contrast", "My photoshop", half_BIG_MAX_VALUE, 2*half_BIG_MAX_VALUE, nothing) # 對比 cv2.createTrackbar("lightness", "My photoshop", half_MAX_VALUE, 2*half_MAX_VALUE, nothing) # 亮度 cv2.createTrackbar("saturation", "My photoshop", half_BIG_MAX_VALUE, 2*half_BIG_MAX_VALUE, nothing) # 飽和度 # 陰影 # 色溫 # 銳化 cv2.createTrackbar("noise", "My photoshop", 0, half_MAX_VALUE, nothing) # 顆粒點 # 調整飽和度和亮度 while True: time.sleep(0.1) # for better memory use # My color panel (some will change and no reverse) img = color_panel(img) img_copy = np.copy(img) rotation = cv2.getTrackbarPos('rotation', 'My photoshop') - 180 lightness = cv2.getTrackbarPos('lightness', 'My photoshop') - half_MAX_VALUE saturation = cv2.getTrackbarPos('saturation', 'My photoshop') - half_BIG_MAX_VALUE brightness = cv2.getTrackbarPos('brightness', 'My photoshop') - half_MAX_VALUE contrast = cv2.getTrackbarPos('contrast', 'My photoshop') - half_BIG_MAX_VALUE noise = cv2.getTrackbarPos('noise', 'My photoshop') img_copy = rotate_img(img_copy, rotation) img_copy = modify_lightness_saturation(img_copy, lightness, saturation) img_copy = modify_contrast_and_brightness(img_copy, brightness , contrast) img_copy = gaussian_noise(img_copy, mean=0, sigma=noise/100) plot_histogram = show_histogram(img_copy) # 顯示調整後的效果 cv2.imshow("My histogram", plot_histogram) msg = "Press ESC to exit, or press S to save and exit." img_copy = cv2.putText(img_copy, msg, (10, h-10), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 1, cv2.LINE_AA) # 顯示調整後的效果 cv2.imshow("My photoshop", img_copy) ch = cv2.waitKey(5) if ch == 27: # 按 ESC 鍵退出 break elif ch == ord('s'): # 按 s 鍵保存結果並退出 cv2.imwrite("result.jpg", img_copy) break # 關閉所有的窗口 cv2.destroyAllWindows()
https://www.wongwonggoods.com/all-posts/python/python_opencv/opencv-my-photoshop-v2/
功能列表
1.新增視窗 My histogram,可以隨時查看現在圖片的 RGB直方圖
1-1. 建立新視窗 “My histogram”
1-2. 使用之前教學的函數,畫出圖片 RGB直方圖
1-3. 最大難題:如何把 plot 變成 圖片? 這問題才是這次更新功能最大的難關啊, 我們之前絕大部分的plot都是直接顯示在 jupyter notebook 中, 現在要把它變成圖片移出來,該怎麼辦呢?
1-3-1. (failed) 嘗試使用 plt.show() plt.show() 我記得可以直接另外開啟視窗產生出圖片繪製結果, 但在 jupyter notebook 中,似乎沒辦法這樣做。
1-3-2. (passed?) 儲存 plt 為 jpg 檔後,再用 OpenCV 讀取 看標題就知道一定會成功了, 總之這也是很直覺的方法,直接儲存圖片後再讀取圖片, 但這會有一個很大的問題,我們的硬碟無時無刻都會一直在讀寫…
長久下來絕對不是一個好的方案 (除非想更快換硬碟)
1-3-3. (最佳化) 儲存 plt 進 memory buffer 後,再讀取出來 這個方法的精隨在於,他並沒有實際上做儲存檔案的動作, 而只是把這些資訊暫時放在記憶體的某個位置上, 而我們能用如同 1-3-2. 的方法把圖片讀取出來。
2. 新增視窗 My color panel,可以自己選色並作畫
這個不論是 photoshop 或 小畫家 應該都有的功能吧! 我們先新增一個能夠顯示顏色的視窗:
2-1. 建立新視窗 “My color panel”
2-2. 建立 RGB滑動條,並顯示對應顏色
使用 cv2.getTrackbarPos 得到顏色的回傳值,並使用 color_rgb[:] = [data['b'], data['g'], data['r']],組合顏色並顯示顏色
2-3. 建立打點用的筆,並對圖片作畫
這邊我特別說明我們需要製作一個開關,來偵測現在是否開啟打點模式。
製作開關:
偵測開關
如果開關打開,才是開啟打點模式:
2-4. 最後,注意 imshow 圖片更新邏輯 我們需要注意一件事情, 昨天我們的內容大多數都是「從原圖」出發,依據對應值開始調整畫面,
但這次的打點內容,是需要被保存在圖片上的。 因此我們必須要修改這部分的程式邏輯,
我們每次進行打點時,更新原來的圖片, 而做其他變化時,不更新原來的圖片,從原圖片出發開始計算。
code如下
https://www.wongwonggoods.com/all-posts/python/python_opencv/opencv-my-photoshop-v2/