Python-opencv 实现photoshop中的一些基本操作

Python-opencv 实现PS操作

0. 罗里吧嗦的开头

最近有个项目要做颜色变换,而对于我这种对色彩不敏感的人可是相当难受。调半天opencv色调,都不知道自己调的是啥。
于是乎。。。。有了个idea!!!

我找了个做美工设计的朋友帮我做了效果图。(photoshop)然后把操作思路和过程都给到我,这下子就只差复现photoshop的操作了。

所以就有了我在网上寻找总结的这一系列,用python-opencv实现的photoshop操作。(网上基本上都是C++ - opencv实现的)

后面如果有需要其他操作我会持续更新。如果没有找到python代码,我也会想办法自己复现一下的。😀(好像后面还要实现一下Photoshop的图层融合操作。😂)

1. 调整色阶操作 - levels-adjust

根据https://blog.csdn.net/maozefa/article/details/5643459这个链接里面给的公式自己写的python代码:
输入的参数如下图所示,dim代表要修改的分量。(dim单分量:0,1,2;RGB全分量的话:3)
参数

def levels_adjust(img, Shadow, Midtones, Highlight, OutShadow, OutHighlight, Dim):
    # print(img)
    # dim = 3的时候调节RGB三个分量, 0调节B,1调节G,2调节R
    if Dim == 3:
        mask_shadow = img < Shadow
        img[mask_shadow] = Shadow
        mask_Highlight = img > Highlight
        img[mask_Highlight] = Highlight
    else:
        mask_shadow = img[...,Dim]<Shadow
        img[mask_shadow] = Shadow
        mask_Highlight = img[...,Dim]>Highlight
        img[mask_Highlight] = Highlight

    if Dim == 3:
        Diff = Highlight - Shadow
        rgbDiff = img - Shadow
        clRgb = np.power(rgbDiff / Diff, 1 / Midtones)
        outClRgb = clRgb * (OutHighlight - OutShadow) / 255 + OutShadow
        data = np.array(outClRgb*255, dtype='uint8')
        img = data
    else:
        Diff = Highlight - Shadow
        rgbDiff = img[...,Dim] - Shadow
        clRgb = np.power(rgbDiff/Diff, 1/Midtones)
        outClRgb = clRgb*(OutHighlight - OutShadow)/255 + OutShadow
        data = np.array(outClRgb * 255, dtype='uint8')
        img[..., Dim] = data
    return img

2. 调节饱和度 - Saturation

代码我使用了这个博主写的:https://blog.csdn.net/benja_k/article/details/95478100 他里面也有具体的代码原理解读,有兴趣可以直接跳转过去看。

他的输入是一个比例,范围是(-1,1)

def Saturation(rgb_img, increment):
    img = rgb_img * 1.0
    img_min = img.min(axis=2)
    img_max = img.max(axis=2)
    img_out = img

    # 获取HSL空间的饱和度和亮度
    delta = (img_max - img_min) / 255.0
    value = (img_max + img_min) / 255.0
    L = value / 2.0

    # s = L<0.5 ? s1 : s2
    mask_1 = L < 0.5
    s1 = delta / (value)
    s2 = delta / (2 - value)
    s = s1 * mask_1 + s2 * (1 - mask_1)

    # 增量大于0,饱和度指数增强
    if increment >= 0:
        # alpha = increment+s > 1 ? alpha_1 : alpha_2
        temp = increment + s
        mask_2 = temp > 1
        alpha_1 = s
        alpha_2 = s * 0 + 1 - increment
        alpha = alpha_1 * mask_2 + alpha_2 * (1 - mask_2)

        alpha = 1 / alpha - 1
        img_out[:, :, 0] = img[:, :, 0] + (img[:, :, 0] - L * 255.0) * alpha
        img_out[:, :, 1] = img[:, :, 1] + (img[:, :, 1] - L * 255.0) * alpha
        img_out[:, :, 2] = img[:, :, 2] + (img[:, :, 2] - L * 255.0) * alpha

    # 增量小于0,饱和度线性衰减
    else:
        alpha = increment
        img_out[:, :, 0] = img[:, :, 0] + (img[:, :, 0] - L * 255.0) * alpha
        img_out[:, :, 1] = img[:, :, 1] + (img[:, :, 1] - L * 255.0) * alpha
        img_out[:, :, 2] = img[:, :, 2] + (img[:, :, 2] - L * 255.0) * alpha

    # img_out = img_out / 255.0

    # RGB颜色上下限处理(小于0取0,大于1取1)
    # mask_3 = img_out < 0
    # mask_4 = img_out > 255
    # img_out = img_out * (1 - mask_3)
    # img_out = img_out * (1 - mask_4) + mask_4

    return np.array(img_out,dtype='uint8')

3. 调节亮度 - Illumi-adjust

代码我使用了这个博主写的:https://blog.csdn.net/weixin_30896511/article/details/95614626 比较简单哈,同样他里面也有具体的代码原理解读,有兴趣可以直接跳转过去看。

def Illumi_adjust(alpha, img):
    if alpha > 0 :
        img_out = img * (1 - alpha) + alpha * 255.0
    else:
        img_out = img * (1 + alpha)

    return np.array(img_out,dtype='uint8')

4. 调节颜色曲线 - curves-filter

也就是RGB曲线,这个在PS里面调节有特别多的使用。但是由于曲线都是手动调整的,所以一开始我不觉得能用代码实现。但是后面发现可以把PS中的曲线导出文件,然后再通过Python进行曲线的加载,最后实现RGB曲线的调节。nice!!!😀

具体的代码我是找的这个源码:https://github.com/pfdevilliers/python-image-filters

操作的话,只要通过PS导出.acv曲线文件(如下):
在这里插入图片描述
然后通过源码中的filter.py代码,输入图片以及曲线文件。就可以实现对图片RGB曲线的调节。

class Filter:

    def __init__(self, acv_file_path, name):
        self.name = name
        with open(acv_file_path, 'rb') as acv_file:
            self.curves = self._read_curves(acv_file)
        self.polynomials = self._find_coefficients()

    def _read_curves(self, acv_file):
        _, nr_curves = unpack('!hh', acv_file.read(4))
        curves = []
        for i in range(0, nr_curves):
            curve = []
            num_curve_points, = unpack('!h', acv_file.read(2))
            for j in range(0, num_curve_points):
                y, x = unpack('!hh', acv_file.read(4))
                curve.append((x, y))
            curves.append(curve)

        return curves

    def _find_coefficients(self):
        polynomials = []
        for curve in self.curves:
            xdata = [x[0] for x in curve]
            ydata = [x[1] for x in curve]
            p = interpolate.lagrange(xdata, ydata)
            polynomials.append(p)
        return polynomials

    def get_r(self):
        return self.polynomials[1]

    def get_g(self):
        return self.polynomials[2]

    def get_b(self):
        return self.polynomials[3]

    def get_c(self):
        return self.polynomials[0]


class FilterManager:

    def __init__(self):
        self.filters = {}
        # add some stuff here

    def add_filter(self, filter_obj):
        # Overwrites if such a filter already exists
        # NOTE: Fix or not to fix?
        self.filters[filter_obj.name] = filter_obj

    def apply_filter(self, filter_name, image_array):

        if image_array.ndim < 3:
            raise Exception('Photos must be in color, meaning at least 3 channels')
        else:
            def interpolate(i_arr, f_arr, p, p_c):
                p_arr = p_c(f_arr)
                return p_arr

                # NOTE: Assumes that image_array is a numpy array

            image_filter = self.filters[filter_name]
            # NOTE: What happens if filter does not exist?
            width, height, channels = image_array.shape
            filter_array = numpy.zeros((width, height, 3), dtype=float)

            p_r = image_filter.get_r()
            p_g = image_filter.get_g()
            p_b = image_filter.get_b()
            p_c = image_filter.get_c()

            filter_array[:, :, 0] = p_r(image_array[:, :, 0])
            filter_array[:, :, 1] = p_g(image_array[:, :, 1])
            filter_array[:, :, 2] = p_b(image_array[:, :, 2])
            filter_array = filter_array.clip(0, 255)
            filter_array = p_c(filter_array)

            filter_array = numpy.ceil(filter_array).clip(0, 255)

            return filter_array.astype(numpy.uint8)

if __name__ == '__main__':

    img_filter = Filter('curves/curves.acv', 'rgb')

    im = cv2.imread('tmp/test.png')

    im.show()

    image_array = im

    filter_manager = FilterManager()
    filter_manager.add_filter(img_filter)

    filter_array = filter_manager.apply_filter('rgb', image_array)
    cv2.imshow('',filter_array)
    cv2.waitKey(0)

5. 图层混合方式 - layerBlending

公式大部分参考https://jingyan.baidu.com/article/36d6ed1f7c04801bcf4883c2.html
这里也有一部分,原理的讲解,和公式。以及博主用c语言实现的操作。https://www.cnblogs.com/jsxyhelu/p/12934978.html

有一些没有实现,实际上是我参照ps的效果和代码实现的效果进行对比的时候,前面几种混合方式都挺好的。但是从亮光开始,对比效果看差别挺大的,而且不同网上的公式都有些不同,但是也没有和ps一样的效果,所以做到后面就终止了,因为我自己只需要正片叠加这种😂。(这里我反复校对了我的代码,应该没有什么问题,也有可能是代码实现的有问题,有发现哪里有问题的欢迎大家指出。)

import numpy as np
import cv2

def Darken(target, blend):
    # 变暗
    return np.minimum(target, blend)

def Multiply(target, blend):
    # 正片叠底
    return np.array(np.multiply(target/256,blend/256)*256, dtype=np.uint8)

def ClolorBurn(target, blend):
    # 颜色加深
    # return np.array((1-(1-target/256)/(blend/256))*256, dtype=np.uint8)
    return (1-(1-target/256)/(blend/256))

def LinearBurn(target, blend):
    # 线性加深
    return target/256+blend/256-1

def Lighten(target, blend):
    # 变亮
    return np.maximum(target, blend)

def Screen(target, blend):
    # 滤色
    return 1-(1-target/256)*(1-blend/256)

def ColorDodge(target, blend):
    # 颜色减淡
    target = target / 256
    blend = blend / 256
    return target+(target*blend)/(1-blend)

def LinearDodge(target, blend):
    # 线性减淡
    return cv2.add(target, blend)

def Overlay(target, blend):
    # 叠加
    mask1 = target<=128
    mask2 = target>128
    img = np.zeros(target.shape,dtype=np.float64)
    img[mask1] = np.multiply(target[mask1]/256,blend[mask1]/256) #(暗部正片叠底,更暗)
    img[mask2] = 1-(1-target[mask2]/256)*(1-blend[mask2]/256) #(亮部滤色,更亮)
    return img

def SoftLight(target, blend):
    # 柔光
    mask1 = blend<=128
    mask2 = blend>128
    target = target/256
    blend = blend/256
    img = np.zeros(target.shape, dtype=np.float64)
    img[mask1] = (target[mask1]*blend[mask1])/0.5 + (target[mask1]*target[mask1])*(1-2*blend[mask1])
    img[mask2] = (target[mask2]*(1-blend[mask2]))/0.5 + np.sqrt(target[mask2]) * (2*blend[mask2] - 1)
    return img

def HardLight(target, blend):
    # 强光
    mask1 = blend <= 128
    mask2 = blend > 128
    target = target / 256
    blend = blend / 256
    img = np.zeros(target.shape, dtype=np.float64)
    img[mask1] = target[mask1]*blend[mask1]/0.5
    img[mask2] = 1 - (1-target[mask2])*(1-blend[mask2])/0.5
    return img

def VividLight(target, blend):
    # 亮光
    mask1 = blend <= 128
    mask2 = blend > 128
    target = target / 256
    blend = blend / 256
    img = np.zeros(target.shape, dtype=np.float64)
    img[mask1] = target[mask1] - (1-target[mask1])*(1-2*blend[mask1])/(2*blend[mask1])
    img[mask2] = target[mask2] + target[mask2]*(2*blend[mask2]-1)/(2*(1-blend[mask2]))
    return img

def LinearLight(target, blend):
    #线性光
    return target/256 + 2*blend/256 -1
  • 5
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值