Pin-Jiun / ComputerVision

0 stars 0 forks source link

23-Photoshop ver3.0 #23

Open Pin-Jiun opened 1 year ago

Pin-Jiun commented 1 year ago

功能列表

# ------  My photoshop Release notes ------ #
# ver3.0 (2020.10.12)
1. 優化整體程式穩定度
2. 新增油漆工具 (位於 "My color panel" 視窗,可自選色)
3. 新增新視窗 "My control panel",為所有調整的主控版 (之前的調整功能也移至這裡)
4. 同上,同時移除主畫面 "My photoshop" 的所有調整功能
5. 新增可調整RGB分量功能
6. 新增可調整色溫功能 (冷色系、暖色系)
7. 新增銳化功能
8. 新增4種不同模糊功能
9. 新增22種不同 colormap
10. 新增視窗自動調整大小的功能
11. 新增自動將視窗移動至固定位置的功能,以後不用自己移動視窗了!
# 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. 優化整體程式穩定度 這次的改版主要做的事情是將 無限while迴圈 的部分, 加了個 time.sleep(0.001), 這樣做可以大幅降低 memory 的使用率, 而且 time.sleep(0.001) 對於人是幾乎不會有感覺的, 加了這行程式碼能讓我們的程式更好的使用電腦資源。

https://www.wongwonggoods.com/all-posts/python/python_opencv/opencv-my-photoshop-v3/

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 modify_colormap(img, case = 0):
    if case == 0:
        return img
    img = cv2.applyColorMap(img, case-1)
    return img
def averaging_Blur(image, kernel_size=5):
    if kernel_size <= 0:
        return image
    try:
        blurred = cv2.blur(image, (kernel_size, kernel_size))
        return blurred
    except:
        return image

def gaussian_Blur(image, kernel_size=5):
    if kernel_size <= 0:
        return image
    try:
        blurred = cv2.GaussianBlur(image, (kernel_size, kernel_size), 0)
        return blurred
    except:
        return image

def median_Blur(image, kernel_size=5):
    if kernel_size <= 0:
        return image
    try:
        blurred = cv2.medianBlur(image, kernel_size)
        return blurred
    except:
        return image

def Bilateral_Filter(image, kernel_size=5):
    if kernel_size <= 0:
        return image
    try:
        blurred = cv2.bilateralFilter(image, kernel_size, 75, 75)
        return blurred
    except:
        return image
def modify_color_temperature(img, Bvalue=0, Gvalue=0, Rvalue=0):
    if Bvalue == 0 and Gvalue == 0 and Rvalue == 0:
        return img

    # 1.計算三個通道的平均值,並依照平均值調整色調
    imgB = img[:, :, 0] 
    imgG = img[:, :, 1]
    imgR = img[:, :, 2] 

    # 調整色調請調整這邊~~ 
    # 白平衡 -> 三個值變化相同
    # 冷色調(增加b分量) -> 除了b之外都增加
    # 暖色調(增加r分量) -> 除了r之外都增加
    bAve = cv2.mean(imgB)[0] + Bvalue
    gAve = cv2.mean(imgG)[0] + Gvalue
    rAve = cv2.mean(imgR)[0] + Rvalue
    aveGray = (int)(bAve + gAve + rAve) / 3

    # 2. 計算各通道增益係數,並使用此係數計算結果
    bCoef = aveGray / bAve
    gCoef = aveGray / gAve
    rCoef = aveGray / rAve
    imgB = np.floor((imgB * bCoef))  # 向下取整
    imgG = np.floor((imgG * gCoef))
    imgR = np.floor((imgR * rCoef))

    # 3. 變換後處理
    # 將原文第3部分的演算法做修改版,加快速度
    imgb = imgB
    imgb[imgb > 255] = 255

    imgg = imgG
    imgg[imgg > 255] = 255

    imgr = imgR
    imgr[imgr > 255] = 255

    final_rgb = np.dstack((imgb, imgg, imgr)).astype(np.uint8) 

    return final_rgb

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):
    if sigma == 0:
        return img
    # 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):  
    if brightness == 0 and contrast == 0:
        return img
    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):
    if lightness == 0 and saturation == 0:
        return img
    # 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)

    # 顯示調整後的效果
    cv2.imshow("My histogram", plot_histogram)
    cv2.resizeWindow("My histogram", 400, 300)
    cv2.moveWindow("My histogram", 1000, 600)

def modify_sharpen(img, sigma=1):
    if sigma == 0:
        return img
    # sigma = 5、15、25
    blur_img = cv2.GaussianBlur(img, (0, 0), sigma)
    usm = cv2.addWeighted(img, 1.5, blur_img, -0.5, 0)

    return usm

def color_panel(img_copy):
    def mouse_handler(event, x, y, flags, data):                  
        if event == cv2.EVENT_LBUTTONDOWN:
            if data['painter'] == 1:
                loDiff=40
                upDiff=30
                seed = (x,y)
                h, w = data['img'].shape[:2]
                mask = np.zeros([h+2, w+2], np.uint8) 
                cv2.floodFill(data['img'], mask, seed, (data['b'], data['g'], data['r']), (loDiff, loDiff, loDiff), (upDiff, upDiff, upDiff), cv2.FLOODFILL_FIXED_RANGE)  
                print("FloodFill at (x, y) = ({}, {})".format(x, y))
#             elif data['line'] == 1 and data['line_drawing'] == False: 
#                 data['line_drawing'] = True
#                 data['line_x'] = x
#                 data['line_y'] = y
#                 print("Start draw line at (x, y) = ({}, {})".format(x, y))
#                 for i in range(1000000):
#                     print("\r{}".format(i), end="")
#                     if event == cv2.EVENT_LBUTTONUP:
#                         break

#                 if data['line'] == 1 and data['line_drawing'] == True: 
#                     data['line_drawing'] = False
#                     cv2.line(data['img'], (data['line_x'], data['line_y']), (x,y), (data['b'], data['g'], data['r']), 16)
#                     print("End draw line at (x, y) = ({}, {})".format(x, y))

            elif 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')
#     data['line']  = cv2.getTrackbarPos('Line','My color panel')
#     data['line_drawing']  = False
#     data['line_x']  = 0
#     data['line_y']  = 0
    data['painter']  = cv2.getTrackbarPos('Painter','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']
def control_panel(img_copy):

    rotation = cv2.getTrackbarPos('rotation', 'My control panel') - 180
    lightness = cv2.getTrackbarPos('lightness', 'My control panel') - half_MAX_VALUE 
    saturation = cv2.getTrackbarPos('saturation', 'My control panel') - half_BIG_MAX_VALUE 
    brightness = cv2.getTrackbarPos('brightness', 'My control panel') - half_MAX_VALUE
    contrast = cv2.getTrackbarPos('contrast', 'My control panel') - half_BIG_MAX_VALUE 
    coldvalue = cv2.getTrackbarPos('cold temp', 'My control panel') 
    warmvalue = cv2.getTrackbarPos('warm temp', 'My control panel') 
    Rvalue = cv2.getTrackbarPos('R value', 'My control panel') + coldvalue
    Gvalue = cv2.getTrackbarPos('G value', 'My control panel') + coldvalue + warmvalue
    Bvalue = cv2.getTrackbarPos('B value', 'My control panel') + warmvalue
    sharpen = cv2.getTrackbarPos('sharpen', 'My control panel')
    noise = cv2.getTrackbarPos('noise', 'My control panel')

    # every kinds of blur
    avg_blur = cv2.getTrackbarPos('avg Blur', 'My control panel')
    gau_blur = cv2.getTrackbarPos('gaussian', 'My control panel')
    med_blur = cv2.getTrackbarPos('medianBlur', 'My control panel')
    bil_blur = cv2.getTrackbarPos('Bilateral', 'My control panel')

    colormap_mode = cv2.getTrackbarPos('Colormap', 'My control panel')

    # ---------- modify the image ---------- #

    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 = modify_color_temperature(img_copy, Bvalue, Gvalue, Rvalue)
    img_copy = modify_sharpen(img_copy, sigma=sharpen/10)    
    img_copy = gaussian_noise(img_copy, mean=0, sigma=noise/100)
    img_copy = averaging_Blur(img_copy, avg_blur)
    img_copy = gaussian_Blur(img_copy, gau_blur)
    img_copy = median_Blur(img_copy, med_blur)
    img_copy = Bilateral_Filter(img_copy, bil_blur)
    img_copy = modify_colormap(img_copy, colormap_mode)

    return img_copy

# 讀取彩色圖像
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)

h, w, dim = img.shape
half_MAX_VALUE = 100
half_BIG_MAX_VALUE = 255

window_flags = cv2.WINDOW_AUTOSIZE # WINDOW_AUTOSIZE

# 修圖主視窗
cv2.namedWindow("My photoshop", window_flags) # cv2.WINDOW_NORMAL)
cv2.resizeWindow("My photoshop", 1280, 720)
cv2.moveWindow("My photoshop", 0, 0)

# 直方圖視窗
cv2.namedWindow("My histogram", window_flags) # cv2.WINDOW_NORMAL)
cv2.resizeWindow("My histogram", 400, 300)
cv2.moveWindow("My histogram", 1000, 600)

# 控制顏色
cv2.namedWindow("My color panel", window_flags) # cv2.WINDOW_NORMAL)
cv2.resizeWindow("My color panel", 400, 300)
cv2.moveWindow("My color panel", 1000, 0)

# 主控台視窗
cv2.namedWindow("My control panel", window_flags) # cv2.WINDOW_NORMAL)
cv2.resizeWindow("My control panel", 400, 800)
cv2.moveWindow("My control panel", 1500, 0)

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('Line','My color panel', 0, 1, nothing)
cv2.createTrackbar('Painter','My color panel', 0, 1, nothing)

# 滑動塊
cv2.createTrackbar("rotation", "My control panel", 180, 2*180, nothing)
cv2.createTrackbar("brightness", "My control panel", half_MAX_VALUE, 2*half_MAX_VALUE, nothing) # 亮度
cv2.createTrackbar("contrast", "My control panel", half_BIG_MAX_VALUE, 2*half_BIG_MAX_VALUE, nothing) # 對比
cv2.createTrackbar("lightness", "My control panel", half_MAX_VALUE, 2*half_MAX_VALUE, nothing) # 亮度
cv2.createTrackbar("saturation", "My control panel", half_BIG_MAX_VALUE, 2*half_BIG_MAX_VALUE, nothing) # 飽和度

cv2.createTrackbar("R value", "My control panel", 0, half_MAX_VALUE, nothing) # 色溫 R
cv2.createTrackbar("G value", "My control panel", 0, half_MAX_VALUE, nothing) # 色溫 G
cv2.createTrackbar("B value", "My control panel", 0, half_MAX_VALUE, nothing) # 色溫 B
cv2.createTrackbar("cold temp", "My control panel", 0, half_MAX_VALUE, nothing) # 色溫 cold
cv2.createTrackbar("warm temp", "My control panel", 0, half_MAX_VALUE, nothing) # 色溫 warm

cv2.createTrackbar("sharpen", "My control panel", 0, half_MAX_VALUE, nothing) # 銳化
cv2.createTrackbar("noise", "My control panel", 0, half_MAX_VALUE, nothing) # 顆粒點

# every kinds of blur
cv2.createTrackbar("avg Blur", "My control panel", 0, half_MAX_VALUE, nothing) 
cv2.createTrackbar("gaussian", "My control panel", 0, half_MAX_VALUE, nothing) 
cv2.createTrackbar("medianBlur", "My control panel", 0, half_MAX_VALUE, nothing) 
cv2.createTrackbar("Bilateral", "My control panel", 0, half_MAX_VALUE, nothing) 

cv2.createTrackbar("Colormap", "My control panel", 0, 22, nothing) # colormap

# 調整飽和度和亮度
while True:
#     time.sleep(0.001) # for better memory use
#     h, w, dim = img_copy.shape

    # My color panel (some will change and no reverse)
    img = color_panel(img)
    img_copy = np.copy(img)

    # My color panel (some will change and can reverse)
    img_copy = control_panel(img_copy)
    show_histogram(img_copy)  

    ch = cv2.waitKey(5)
    if ch == 27:
        # 按 ESC 鍵退出
        break
    elif ch == ord('s'):
        # 按 s 鍵保存結果並退出
        cv2.imwrite("result.jpg", img_copy)
        break

    # add message
    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)

# 關閉所有的窗口
cv2.destroyAllWindows()