In [ ]:
#src == 來源圖物件
#dst == 目標/輸出圖物件

#邊緣檢測函數
#cv2.CV_8U == dtype: np.uint8
#cv2.Laplacian(src, dtype, dst, K值)

#cv2.Sobel(src, dtype, dx, dy) 
# x,y兩軸需要分開作, 即dx=1的時候dy=0
#且dtype必須是cv2.CV_16S, 因Sobel計算結果會超出0~255的區間, 不能用cv2.CV_8U
#之後還需要透過cv2.convertScaleAbs()把dtype轉換回cv2.CV_8U, 否則不能正確顯示圖片
#最後用cv2.addWeighted(x, x的weight, y, y的weight, gamma值)將拆開計算的結果組合回去
#x kernel = [[-1, 0, 1], 
#           [-2, 0, 2],
#           [-1, 0, 1]]
#y kernel = [[-1,-2,-1],
#            [ 0, 0, 0],
#            [ 1, 2, 1]]

#cv2.Scharr() #尚未讀過
#以上的邊緣檢測容易被噪點誤導, 需要自己先模糊化/去噪/二值化等過程後才做檢測
#cv2.Canny(src, treshold1, treshold2) #最省事的方法,自帶去噪,使用double treshold剔除false positive,檢測並消除不明顯邊緣

#模糊化函數, K越大越模糊
#cv2.Blur(圖物件, K值) #簡單模糊
#cv2.medianBlur(圖物件, K值) #中位數模糊,對處理影片雜訊點較有效,特別是彩色影像
#cv2.GaussianBlur(圖物件, K值) #高斯模糊

#cv2.filter2D(src, dtype, kernel, dst) #卷積濾波器, 可自訂kernel
#dtype若為-1表示src與dst用相同的dtype
#filter2D對3通道都使用同一個kernel
#若要對不同通道使用不同kernel, 需要先用cv2.split(src)分離通道處理過, 在用cv2.merge(通道,dst)

In [20]:
import cv2
import numpy as np
import utils
import scipy.interpolate

#模糊化>灰階>Laplacian邊緣檢測
#src輸入的圖物件, dst輸出的圖物件
#封裝成filters.py

def strokeEdges(src, dst, blurKsize, edgeKsize):  #邊緣檢測 + 描繪邊緣
    if blurKsize >= 3:
        blurredSrc = cv2.medianBlur(src, blurKsize)
        graySrc = cv2.cvtColor(blurredSrc, cv2.COLOR_BGR2GRAY)
    else:
        graySrc = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
    cv2.Laplacian(graySrc, cv2.CV_8U, graySrc, ksize = edgeKsize) #Laplacian邊緣檢測
    normalizedInverseAlpha = (1.0/255)*(255-graySrc) # 正規化, 修正成0~1之間的float, 當weight使用
    channels = cv2.split(src) #將通道分離
    for channel in channels:
        channel[:] = channel * normalizedInverseAlpha
    output = cv2.merge(channels, dst) #將通道組合回去
    return output

class VConvolutionFilter(object):   #作卷積
    # A filter that applies a convolution to V (or all of BGR).
    def __init__(self, kernel):
        self._kernel = kernel
        
    def apply(self, src, dst):      
        #Apply the filter with a BGR or gray source/destination.
        cv2.filter2D(src, -1, self._kernel, dst)   #指定kernel作卷積

class SharpenFilter(VConvolutionFilter):
    #A sharpen filter with a 1-pixel radius.
    def __init__(self):
        #銳利化的kernel,
        kernel = np.array([
                [-1,-1,-1],
                [-1, 9,-1],
                [-1,-1,-1],
            ])
        VConvolutionFilter.__init__(self, kernel)
        
class FindEdgesFilter(VConvolutionFilter):
    #An edge-finding filter with 1-pixel radius.
    def __init__(self):
        #銳利化的kernel, 但令總和為0, 這會導致像素點與鄰近點通道值相近的變為黑色
        kernel = np.array([
                    [-1,-1,-1],
                    [-1, 8,-1],
                    [-1,-1,-1],
                ])
        VConvolutionFilter.__init__(self, kernel)
        
class BlurFilter(VConvolutionFilter):
    #A blur filter with a 2-pixel radius.
    def __init__(self):
        #模糊化的kernel, 總合為1且全為正數
        kernel = np.array([
                [0.04,0.04,0.04,0.04,0.04],
                [0.04,0.04,0.04,0.04,0.04],
                [0.04,0.04,0.04,0.04,0.04],
                [0.04,0.04,0.04,0.04,0.04],
                [0.04,0.04,0.04,0.04,0.04],
                ])
        VConvolutionFilter.__init__(self, kernel)
        
class EmbossFilter(VConvolutionFilter):
    #An emboss filter with a 1-pixel radius.
     def __init__(self):
            #浮雕化, 左上角銳利化並右下角模糊化
        kernel = np.array([
                    [-2,-1, 0],
                    [-1, 1, 1],
                    [ 0, 1, 2],
                ])
        VConvolutionFilter.__init__(self, kernel)

In [ ]:
def createCurveFunc(points):
    """Return a function derived from control points."""
    if points is None:
        return None
    numPoints = len(points)
    if numPoints < 2:
        return None
    xs, ys = zip(*points)
    if numPoints < 4:
        kind = 'linear'
        # 'quadratic' is not implemented.
    else:
        kind = 'cubic'
    return scipy.interpolate.interp1d(xs, ys, kind, bounds_error = False)

def createLookupArray(func, length = 256):
    """Return a lookup for whole-number inputs to a function.
    
    The lookup values are clamped to [0, length - 1].
    
    """
    if func is None:
        return None
    lookupArray = np.empty(length)
    i = 0
    while i < length:
        func_i = func(i)
        lookupArray[i] = min(max(0, func_i), length - 1)
        i += 1
    return lookupArray

def applyLookupArray(lookupArray, src, dst):
    """Map a source to a destination using a lookup."""
    if lookupArray is None:
        return
    dst[:] = lookupArray[src]

def createCompositeFunc(func0, func1):
    """Return a composite of two functions."""
    if func0 is None:
        return func1
    if func1 is None:
        return func0
    return lambda x: func0(func1(x))
        
class BGRFuncFilter(object):
    """A filter that applies different functions to each of BGR."""
    
    def __init__(self, vFunc = None, bFunc = None, gFunc = None, rFunc = None, dtype = np.uint8):
        length = np.iinfo(np.uint8).max + 1
        self._bLookupArray = createLookupArray(createCompositeFunc(bFunc, vFunc), length)
        self._gLookupArray = createLookupArray(createCompositeFunc(gFunc, vFunc), length)
        self._rLookupArray = createLookupArray(createCompositeFunc(rFunc, vFunc), length)
    
    def apply(self, src, dst):
        """Apply the filter with a BGR source/destination."""
        b, g, r = cv2.split(src)
        utils.applyLookupArray(self._bLookupArray, b, b)
        utils.applyLookupArray(self._gLookupArray, g, g)
        utils.applyLookupArray(self._rLookupArray, r, r)
        cv2.merge([b, g, r], dst)

class BGRCurveFilter(BGRFuncFilter):
    """A filter that applies different curves to each of BGR."""
    
    def __init__(self, vPoints = None, bPoints = None, gPoints = None, rPoints = None, dtype = np.uint8):
        BGRFuncFilter.__init__(self,
                               createCurveFunc(vPoints),
                               createCurveFunc(bPoints),
                               createCurveFunc(gPoints),
                               createCurveFunc(rPoints), dtype = dtype)      
        
class BGRPortraCurveFilter(BGRCurveFilter):
    """A filter that applies Portra-like curves to BGR."""
    
    def __init__(self, dtype = np.uint8):
        BGRCurveFilter.__init__(
            self,
            vPoints = [(0,0),(23,20),(157,173),(255,255)],
            bPoints = [(0,0),(41,46),(231,228),(255,255)],
            gPoints = [(0,0),(52,47),(189,196),(255,255)],
            rPoints = [(0,0),(69,69),(213,218),(255,255)], dtype = dtype)

In [62]:
#strokeEdges的分段測試
from matplotlib import pyplot as plt
%matplotlib inline
path = '7-11-logo-2.png'
src = cv2.imread(path) #原圖
src_RGB = cv2.cvtColor(src,cv2.COLOR_BGR2RGB)
plt.imshow(src_RGB)
plt.show()

blurredSrc = cv2.medianBlur(src, 1) #模糊化+灰階
graySrc = cv2.cvtColor(blurredSrc, cv2.COLOR_BGR2GRAY)
plt.imshow(graySrc, 'gray')
plt.show()

cv2.Laplacian(graySrc, cv2.CV_8U, graySrc, 7) #Laplacian邊緣檢測
plt.imshow(graySrc, 'gray')
plt.show()

normalizedInverseAlpha = (1.0/255)*(255-graySrc)
#print normalizedInverseAlpha

channels = cv2.split(src)
#print channels

for channel in channels:
    channel[:] = channel * normalizedInverseAlpha
#    print channel
dst = cv2.merge(channels, src)

#dst = strokeEdges(src, src, 7, 7)
dst_RGB = cv2.cvtColor(dst,cv2.COLOR_BGR2RGB)
plt.imshow(dst_RGB)
plt.show()



In [83]:
path = '7-11-logo-2.png'
src = cv2.imread(path) #原圖
dst = cv2.Canny(src, 200, 300) #用Canny邊緣檢測
plt.imshow(dst, 'gray')
plt.show()

path = 'face.jpg'
src = cv2.imread(path)
dst = cv2.Canny(src, 100, 200) #用Canny
plt.imshow(dst, 'gray')
plt.show()



In [77]:
from matplotlib import pyplot as plt
%matplotlib inline

path = 'face.jpg'
src = cv2.imread(path) #原圖
plt.imshow(cv2.cvtColor(src, cv2.COLOR_BGR2RGB))
plt.show()

strokeEdges(src, src, 7, 5)
BGRPortraCurveFilter(src)   #卡通化
src_RGB = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)
plt.imshow(src_RGB)
plt.show()

src = cv2.imread(path)
EmbossFilter().apply(src, src)  #浮雕化
plt.imshow(cv2.cvtColor(src,cv2.COLOR_BGR2RGB))
plt.show()

src = cv2.imread(path)
SharpenFilter().apply(src, src) #銳利化
plt.imshow(cv2.cvtColor(src,cv2.COLOR_BGR2RGB))
plt.show()

src = cv2.imread(path)
BlurFilter().apply(src, src)  #模糊化
plt.imshow(cv2.cvtColor(src,cv2.COLOR_BGR2RGB))
plt.show()

src = cv2.imread(path)
FindEdgesFilter().apply(src, src)  #自製的簡易邊緣檢測
plt.imshow(cv2.cvtColor(src,cv2.COLOR_BGR2RGB))
plt.show()



In [98]:
path = 'face.jpg'
src = cv2.imread(path)
x = cv2.Sobel(src,cv2.CV_16S,1,0)  #Sobel邊緣檢測
y = cv2.Sobel(src,cv2.CV_16S,0,1)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
dst = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
plt.imshow(cv2.cvtColor(dst,cv2.COLOR_BGR2RGB), 'gray')
plt.show()

src = cv2.imread(path)
dst = cv2.Canny(src, 100, 200) #Canny邊緣檢測
plt.imshow(dst, 'gray')
plt.show()

src = cv2.imread(path)
FindEdgesFilter().apply(src, src)  #自製的簡易邊緣檢測
plt.imshow(cv2.cvtColor(src,cv2.COLOR_BGR2RGB))
plt.show()



In [106]:
path = 'testimage.jpg'
src = cv2.imread(path)
x = cv2.Sobel(src,cv2.CV_16S,1,0)  #Sobel邊緣檢測
y = cv2.Sobel(src,cv2.CV_16S,0,1)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
dst = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
plt.imshow(cv2.cvtColor(dst,cv2.COLOR_BGR2RGB), 'gray')
plt.show()

src = cv2.imread(path)
dst = cv2.Canny(src, 100, 300) #Canny邊緣檢測
plt.imshow(dst, 'gray')
plt.show()

src = cv2.imread(path)
FindEdgesFilter().apply(src, src)  #自製的簡易邊緣檢測
plt.imshow(cv2.cvtColor(src,cv2.COLOR_BGR2RGB))
plt.show()



In [ ]: