用ACR调色核心,HSL面板深度解析,让照片色彩随心而变
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用ACR调色核心,HSL面板深度解析,让照片色彩随心而变相关的知识,希望对你有一定的参考价值。
参考技术A 大家好,我是学长,前面和大家分享了 Camera Raw滤镜界面,以及“基本”面板的使用方法。看过以上两期内容的伙伴,相信你已经对 Camera Raw 有了一定的了解,Camera Raw 是 PS 内置的滤镜插件,简称 ACR。
当我们导入一张 Raw 格式文件的时候,会自动加载ACR。
Raw 格式的文件是用专业相机拍摄的原始文件,能够 记录相机在拍摄现场所有的原始信息 ,包括理想的色彩范围,以及现场所有的光线的白平衡信息和影调信息。所以如果你拥有一台相机,最好是 Raw 格式拍摄。
品牌相机拍摄的 Raw 格式的文件扩展名对比如下。
Raw 格式的文件,除能够包含更多的原始拍摄信息,Raw 格式的文件还有更大的位深度,更适合进行后期处理,因此用相机拍照,尽量使用 Raw 格式,以便后期调色时能获得最佳效果。
接下来用一个实例讲解 ACR 在摄影后期调色中的应用。
前两期已经对 ACR“基本”面板做了详细的讲解,还没学习的伙伴,点我查看详细介绍,否则很难理解本期的案例教程。
本期将深度解析 HSL 面板,只有掌握了 HSL 的工作原理,才能熟能生巧,让照片颜色随心而变。
一、 HLS深度解析
1、HSB颜色模式
大家都知道,任何一种颜色或者说图像,都是由是由红(Red)、绿(Green)、蓝(Blue)三种颜色以不同比例相加而成,也就是 RGB。
1、HSB颜色模式
为了更更加直观的表达颜色,就有了 HSB 颜色模式。
色相H( Hue ):在 0~360°的标准色轮上,色相是按位置度量的。在通常的使用中,色相是由颜色名称标识的,比如红、绿或橙色。
饱和度 S( saturation ):是指颜色的强度或纯度。饱和度表示色相中彩色成分所占的比例,用从0(灰色)~100%(完全饱和)的百分比来度量。在标准色轮上饱和度是从中心逐渐向边缘递增的。
亮度 B( brightness ):是颜色的相对明暗程度,通常是从0(黑)~100%(白)的百分比来度量的。
如下图所示,用 PS 打开一张照片,快捷键 i 启用吸管工具,在图像任一位置点一下,可查看 HSB 值。
2、色相环60度理论
在 PS 里面,把色彩分为六个部分,分别是红(0°)、黄(60°)、绿(120°)、青(180°)、蓝(240°)、品(300°)。
它们之间的过度就是加上 60°,红色 0° 加 60° 就是黄色,黄色加 60° 就是绿色,绿色加 60° 就是青色,青色加 60° 就是蓝色,蓝色加 60° 就是品色,品色加 60°,又变成红色。
刚好形成一个闭环,这六个颜色之间又分出六个过渡色,例如 红色和黄色的过度色为橙色(30°) 。 蓝色与品色之间的过度色为紫色(270°) 。
下面这个色相环一定要理解,并熟记,这对于后期调色会有很大帮助。
平时可以用 吸管工具+HSB 面板 取色看数字,得到相应的度数来判断色相,这样反复多加练习,很快就能熟记。
3、HSL面板原理
从 RGB 到 HSB,再到 HSL,讲的都颜色理论,ACR 的第四个调色面板就是 HSL。
虽然与 HSB 只有一个字母的差别,但是它们的含义却不尽相同。
HSL是色相(Hue)、饱和度(Saturation)和亮度(Lightness)三个颜色属性的简称。
色相 (Hue)是色彩的基本属性,这个前面已经讲过,是同一个原理。
饱和度 (Saturation)是指色彩的纯度,饱和度越高色彩越纯越浓,饱和度越低则色彩变灰变淡。
亮度 (Lightness)指的是色彩的明暗程度,亮度值越高,色彩越白,亮度越低,色彩越黑。
4、HSL的使用
在 HLS 面板中,按照色相环的原理,把颜色的色相、饱和度和明度分为 8 个部分,每一部分都有一个小滑块。
这 8 个调色滑块对应了前面讲过的红、橙、黄、绿、青(浅绿)、蓝、紫、品(洋红)8 种自然界中的主要色彩,通过调整滑块,来改变图像的颜色。
色相滑块往右移(加数值),颜色会在色环中顺时针偏色 。
色相滑块往左移(减数值),颜色会在色环中逆时针偏色 。
理解了上面两句话,我们就学会了 HSL 的色相调整。
我给大家准备了一个色相换,大家可以用 PS 打开,载入 ACR,打开 HSL 面板尝试拖动不同颜色的滑块,观察颜色的变化。
也可以按照下面的方法绘制一个色相环。
打开 PS 新建一个正方图层,用参考线绘制中心点,用椭圆选框工具绘制一个圆,使圆的中心点与图层中心点重合。
快捷键 G 调用渐变工具。选择内置的七色渐变器,绘制方式选择“ 角度渐变 "。从中心点往外拉一条直线,就是一个色相环了。
色相调整(红色为例):
向左拖动滑块-100,红色及右边相邻的颜色向品红偏色。
向右拖动滑块+100,红色及右边相邻的颜色向橙色偏色。
也就是说,当拖动一个颜色的滑块时,该颜色会在左右相邻的色带之间偏移,但不会越过相邻的颜色。
大家可以尝试一下其余几个色相,观察并熟悉色彩的变化。
饱和度调整(红色为例):
向左拖动滑块-100,红色区域颜色变淡。
之所以没有完全变成灰色,是因为,这部分颜色也会受橙色的影响,向左拖动橙色滑块-100。整个红色区域全部变成灰色。
亮度调整就容易理解了,向左变暗,向右变亮。
向左拖动滑块,变暗。
向右拖动滑块,变亮。
二、HSL调色案例
1、打开一张照片,从照片中可以看到,背景中有些色彩相对来说比较杂乱,我们可以通过 HSL 面板进行全方位的优化。
2、切换到到 HSL 面板,选中”饱和度“选项卡,在该选项卡中,降低”黄色“”绿色“”浅绿色“的饱和度。
从原图中可以看到,降低黄色的饱和度,可以削弱画面左上角背景当中被光线照射的植物的颜色。降低这几种颜色的饱和度以后,画面的色彩不再过度跳跃,显得协调了很多。
3、切换到”明亮度”,适当降低“黄色”“浅绿色”及“绿色”的明亮度,让这些色彩的亮度下降,变得暗一些,让人物更加突出。
4、适当提高橙色的明亮度,因为无论是白种人、黄种人还是黑种人,人物肤色中的成分都是橙色,因此,提高曾色的明亮度,就可以提亮人物的肤色,让画面中的主题人物肤色更加明亮。
这样就完成了对色彩的优化和调整,前后对比如下图。
俗话说,教程看百遍,不如上手练一遍,看完本期内容的伙伴,记得找一张自己拍的照片,载入ACR,尝试给照片进行调色处理。
好了,本期内容到此结束,关于本期的内容,有任何不理解的地方,都可以在底部留言交流,学长会第一时间为你解答。
树莓派视觉小车 -- OpenCV巡线(HSL色彩空间PID)
目录
试错
试错1:形态学处理
一开始用的形态学处理,自行改变阈值,调试之后,进行处理,发现效果不是太好,于是改成了HSV色彩空间。
试错2:HSV色彩空间
之前没注意到,HSV色彩空间很难识别白色:
HSV:
不难看出,如果寻白色线的话,HSV色彩空间不是一个很好的选择,下面引入HSL色彩空间:
HSL:
所以,如果是巡白色的话,建议用HSL色彩空间。
注意:巡线小车的摄像头不能太低,如果太低了,可能让小车自己的影子会阻碍光线。
hsv中的效果:
hsl中的效果:
可以看出,已经能大致找到白线了。
基础理论
1、HSV与HSL色彩空间
HSV:
不难看出,如果寻白色线的话,HSV色彩空间不是一个很好的选择,下面引入HSL色彩空间:
HSL:
所以,如果是巡白色的话,建议用HSL色彩空间。
2、PID调节
个人理解:
P:拉力
I:推动力
D:阻力
一、OpenCV图像处理
1、在HSL色彩空间下得到二值图
# 在HSV色彩空间下得到二值图
def Get_HSV(image):
# 1 get trackbar's value
hmin = cv2.getTrackbarPos('hmin', 'h_binary')
hmax = cv2.getTrackbarPos('hmax', 'h_binary')
smin = cv2.getTrackbarPos('smin', 's_binary')
smax = cv2.getTrackbarPos('smax', 's_binary')
lmin = cv2.getTrackbarPos('lmin', 'l_binary')
lmax = cv2.getTrackbarPos('lmax', 'l_binary')
# 2 to HSV
hls = cv2.cvtColor(image, cv2.COLOR_BGR2HLS)
cv2.imshow('hls', hls)
h, l, s = cv2.split(hls)
# 3 set threshold (binary image)
# if value in (min, max):white; otherwise:black
h_binary = cv2.inRange(np.array(h), np.array(hmin), np.array(hmax))
s_binary = cv2.inRange(np.array(s), np.array(smin), np.array(smax))
l_binary = cv2.inRange(np.array(l), np.array(lmin), np.array(lmax))
# 4 get binary(对H、S、V三个通道分别与操作)
binary = 255 - cv2.bitwise_and(h_binary, cv2.bitwise_and(s_binary, l_binary))
# 5 Show
cv2.imshow('h_binary', h_binary)
cv2.imshow('s_binary', s_binary)
cv2.imshow('l_binary', l_binary)
cv2.imshow('binary', binary)
return binary
2、 对二值图形态学处理
# 图像处理
def Image_Processing():
global frame, binary
# Capture the frames
ret, frame = camera.read()
# to binary
binary = Get_HSV(frame)
blur = cv2.GaussianBlur(binary, (5, 5), 0)
cv2.imshow('blur', blur)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (35, 35))
Open = cv2.morphologyEx(blur, cv2.MORPH_OPEN, kernel)
cv2.imshow('Open', Open)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (25, 25))
Erode = cv2.morphologyEx(Open, cv2.MORPH_ERODE, kernel)
cv2.imshow('Erode', Erode)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (25, 25))
Dilate = cv2.morphologyEx(Erode, cv2.MORPH_DILATE, kernel)
cv2.imshow('Dilate', Dilate)
binary = Erode#Dilate
3、找出线的轮廓和中心点坐标
# 找线
def Find_Line():
global x, y, image
# 1 找出所有轮廓
bin2, contours, hierarchy = cv2.findContours(binary, 1, cv2.CHAIN_APPROX_NONE)
# 2 找出最大轮廓
if len(contours) > 0:
# 最大轮廓
c = max(contours, key=cv2.contourArea)
M = cv2.moments(c)
# 中心点坐标
x = int(M['m10'] / M['m00'])
y = int(M['m01'] / M['m00'])
#print(x, y)
# 显示
image = frame.copy()
# 标出中心位置
cv2.line(image, (x, 0), (x, 720), (0, 0, 255), 1)
cv2.line(image, (0, y), (1280, y), (0, 0, 255), 1)
# 画出轮廓
cv2.drawContours(image, contours, -1, (128, 0, 128), 2)
cv2.imshow("image", image)
else:
print("not found the line")
(x,y) = (0, 0)
二、PID
比例:获取当前时刻白线中心点与图像中点的误差,作为当前误差。
积分:获取上一时刻的误差。
def Pid():
global turn_speed, x, y, speed
global error, last_error, pre_error, out_pid
error = abs(x - width / 2)
out_pid = int(proportion * error - integral * last_error + derivative * pre_error)
turn_speed = out_pid
# 保存本次误差,以便下一次运算
pre_error = last_error
last_error = error
# 限值
if (turn_speed < 30):
turn_speed = 30
elif (turn_speed > 100):
turn_speed = 100
if (speed < 0):
speed = 0
elif (speed > 100):
speed = 100
print(error, out_pid, turn_speed, (x, y))
三、运动控制
# 巡线
def Follow_Line():
global turn_speed, x, y,speed, back_speed
'''if(x < width / 2 and y>2*height/3):
Left(turn_speed)
elif(x>3*width/2 and y>2*height/3):
Right(turn_speed)'''
if(0<x<width/4):
Left(turn_speed)
print("turn left")
elif(3*width/4<x<width):
Right(turn_speed)
print("turn right")
#直角拐弯
elif(y>3*height/4):
if(x<width/2):
Left(turn_speed*2)
print("turn left")
elif(x>=width/2):
Right(turn_speed*2)
print("turn right")
elif(x>=width/4 and x<=3*width/4):
Forward(speed)
elif(x==0 and y==0):
Back(back_speed)
总代码
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import numpy as np
import cv2
import Adafruit_PCA9685
import RPi.GPIO as GPIO
import time
l_motor = 18
left_Forward = 22
left_back = 27
r_motor = 23
right_Forward = 25
right_back = 24
pwm_servo = Adafruit_PCA9685.PCA9685()
width, height = 160, 120
camera = cv2.VideoCapture(0)
camera.set(3, width)
camera.set(4, height)
# pid
error = 0 # 当前误差e[k]
last_error = 0 # 上一次误差e[k-1]
pre_error = 0 # 上上次误差e[k-2]
proportion = 1 # 比例系数3 0.2
integral = 0.5 # 积分系数1.2
derivative = 0 # 微分系数1.2
stop_flag = 1
control_flag = 1
turn_speed = 30
speed = 30
back_speed = 30
def Motor_Init():
global L_Motor, R_Motor
L_Motor = GPIO.PWM(l_motor, 100)
R_Motor = GPIO.PWM(r_motor, 100)
L_Motor.start(0)
R_Motor.start(0)
def Direction_Init():
GPIO.setup(left_back, GPIO.OUT)
GPIO.setup(left_Forward, GPIO.OUT)
GPIO.setup(l_motor, GPIO.OUT)
GPIO.setup(right_Forward, GPIO.OUT)
GPIO.setup(right_back, GPIO.OUT)
GPIO.setup(r_motor, GPIO.OUT)
def set_servo_angle(channel, angle):
angle = 4096 * ((angle * 11) + 500) / 20000
pwm_servo.set_pwm_freq(50) # frequency==50Hz (servo)
pwm_servo.set_pwm(channel, 0, int(angle))
def TrackBar_Init():
# 1 create windows
cv2.namedWindow('h_binary')
cv2.namedWindow('s_binary')
cv2.namedWindow('l_binary')
# 2 Create Trackbar
cv2.createTrackbar('hmin', 'h_binary', 0, 179, call_back)
cv2.createTrackbar('hmax', 'h_binary', 110, 179, call_back)
cv2.createTrackbar('smin', 's_binary', 0, 255, call_back)
cv2.createTrackbar('smax', 's_binary', 51, 255, call_back) # 51
cv2.createTrackbar('lmin', 'l_binary', 0, 255, call_back)
cv2.createTrackbar('lmax', 'l_binary', 255, 255, call_back)
'''cv2.namedWindow('binary')
cv2.createTrackbar('thresh', 'binary', 154, 255, call_back) '''
# 创建滑动条 滑动条值名称 窗口名称 滑动条值 滑动条阈值 回调函数
def Init():
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
Direction_Init()
Motor_Init()
TrackBar_Init()
def Forward(turn_speed):
L_Motor.ChangeDutyCycle(turn_speed)
GPIO.output(left_Forward, 1) # left_Forward
GPIO.output(left_back, 0) # left_back
R_Motor.ChangeDutyCycle(turn_speed)
GPIO.output(right_Forward, 1) # right_Forward
GPIO.output(right_back, 0) # right_back
def Back(turn_speed):
L_Motor.ChangeDutyCycle(turn_speed)
GPIO.output(left_Forward, 0) # left_Forward
GPIO.output(left_back, 1) # left_back
R_Motor.ChangeDutyCycle(turn_speed)
GPIO.output(right_Forward, 0) # right_Forward
GPIO.output(right_back, 1) # right_back
def Left(turn_speed):
L_Motor.ChangeDutyCycle(turn_speed)
GPIO.output(left_Forward, 0) # left_Forward
GPIO.output(left_back, 1) # left_back
R_Motor.ChangeDutyCycle(turn_speed)
GPIO.output(right_Forward, 1) # right_Forward
GPIO.output(right_back, 0) # right_back
def Right(turn_speed):
L_Motor.ChangeDutyCycle(turn_speed)
GPIO.output(left_Forward, 1) # left_Forward
GPIO.output(left_back, 0) # left_back
R_Motor.ChangeDutyCycle(turn_speed)
GPIO.output(right_Forward, 0) # right_Forward
GPIO.output(right_back, 1) # right_back
def Stop():
L_Motor.ChangeDutyCycle(0)
GPIO.output(left_Forward, 0) # left_Forward
GPIO.output(left_back, 0) # left_back
R_Motor.ChangeDutyCycle(0)
GPIO.output(right_Forward, 0) # right_Forward
GPIO.output(right_back, 0) # right_back
# 回调函数
def call_back(*arg):
pass
# 在HSV色彩空间下得到二值图
def Get_HSV(image):
# 1 get trackbar's value
hmin = cv2.getTrackbarPos('hmin', 'h_binary')
hmax = cv2.getTrackbarPos('hmax', 'h_binary')
smin = cv2.getTrackbarPos('smin', 's_binary')
smax = cv2.getTrackbarPos('smax', 's_binary')
lmin = cv2.getTrackbarPos('lmin', 'l_binary')
lmax = cv2.getTrackbarPos('lmax', 'l_binary')
# 2 to HSV
hls = cv2.cvtColor(image, cv2.COLOR_BGR2HLS)
cv2.imshow('hls', hls)
h, l, s = cv2.split(hls)
# 3 set threshold (binary image)
# if value in (min, max):white; otherwise:black
h_binary = cv2.inRange(np.array(h), np.array(hmin), np.array(hmax))
s_binary = cv2.inRange(np.array(s), np.array(smin), np.array(smax))
l_binary = cv2.inRange(np.array(l), np.array(lmin), np.array(lmax))
# 4 get binary(对H、S、V三个通道分别与操作)
binary = 255 - cv2.bitwise_and(h_binary, cv2.bitwise_and(s_binary, l_binary))
# 5 Show
cv2.imshow('h_binary', h_binary)
cv2.imshow('s_binary', s_binary)
cv2.imshow('l_binary', l_binary)
cv2.imshow('binary', binary)
return binary
# 手动控制小车(上下左右,案件事件判断)
# 控制方式:w、s、a、d分别表示:上、下、左、右
def Key_Control(keyboard):
global stop_flag, control_flag
if keyboard == ord("w"):
Forward(50)
time.sleep(0.1)
Stop()
elif keyboard == ord("s"):
Back(50)
time.sleep(0.1)
Stop()
elif keyboard == ord("a"):
Left(50)
time.sleep(0.1)
Stop()
elif keyboard == ord("d"):
Right(50)
time.sleep(0.1)
Stop()
# 图像处理
def Image_Processing():
global frame, binary
# Capture the frames
ret, frame = camera.read()
# to binary
binary = Get_HSV(frame)
blur = cv2.GaussianBlur(binary, (5, 5), 0)
cv2.imshow('blur', blur)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (35, 35))
Open = cv2.morphologyEx(blur, cv2.MORPH_OPEN, kernel)
cv2.imshow('Open', Open)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (25, 25))
Erode = cv2.morphologyEx(Open, cv2.MORPH_ERODE, kernel)
cv2.imshow('Erode', Erode)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (25, 25))
Dilate = cv2.morphologyEx(Erode, cv2.MORPH_DILATE, kernel)
cv2.imshow('Dilate', Dilate)
binary = Erode # Dilate
# 找线
def Find_Line():
global x, y, image
# 1 找出所有轮廓
bin2, contours, hierarchy = cv2.findContours(binary, 1, cv2.CHAIN_APPROX_NONE)
# 2 找出最大轮廓
if len(contours) > 0:
# 最大轮廓
c = max(contours, key=cv2.contourArea)
M = cv2.moments(c)
# 中心点坐标
x = int(M['m10'] / M['m00'])
y = int(M['m01'] / M['m00'])
# print(x, y)
# 显示
image = frame.copy()
# 标出中心位置
cv2.line(image, (x, 0), (x, 720), (0, 0, 255), 1)
cv2.line(image, (0, y), (1280, y), (0, 0, 255), 1)
# 画出轮廓
cv2.drawContours(image, contours, -1, (128, 0, 128), 2)
cv2.imshow("image", image)
else:
print("not found the line")
(x, y) = (0, 0)
def Pid():
global turn_speed, x, y, speed
global error, last_error, pre_error, out_pid
error = abs(x - width / 2)
out_pid = int(proportion * error - integral * last_error + derivative * pre_error)
turn_speed = out_pid
# 保存本次误差,以便下一次运算
pre_error = last_error
last_error = error
# 限值
if (turn_speed < 30):
turn_speed = 30
elif (turn_speed > 100):
turn_speed = 100
if (speed < 0):
speed = 0
elif (speed > 100):
speed = 100
print(error, out_pid, turn_speed, (x, y))
# 巡线
def Follow_Line():
global turn_speed, x, y, speed, back_speed
'''if(x < width / 2 and y>2*height/3):
Left(turn_speed)
elif(x>3*width/2 and y>2*height/3):
Right(turn_speed)'''
if (0 < x < width / 4):
Left(turn_speed)
print("turn left")
elif (3 * width / 4 < x < width):
Right(turn_speed)
print("turn right")
# 直角拐弯
elif (y > 3 * height / 4):
if (x < width / 2):
Left(turn_speed * 2)
print("turn left")
elif (x >= width / 2):
Right(turn_speed * 2)
print("turn right")
elif (x >= width / 4 and x <= 3 * width / 4):
Forward(speed)
elif (x == 0 and y == 0):
Back(back_speed)
def Control():
global control_flag, speed, proportion, integral
keyboard = cv2.waitKey(1)
# 加速减速
if (keyboard == ord('k')):
speed += 5
elif (keyboard == ord('l')):
speed -= 5
print(speed)
if keyboard == ord("n"):
integral += 0.01
elif keyboard == ord("m"):
integral -= 0.01
print(integral)
if (control_flag == -1):
Follow_Line()
if keyboard == 32:
control_flag *= -1
Stop()
else:
Key_Control(keyboard)
if keyboard == 32:
control_flag *= -1
Stop()
print(control_flag)
if __name__ == '__main__':
Init()
set_servo_angle(4, 140) # top servo lengthwise
# 0:back 180:front
set_servo_angle(5, 90) # bottom servo crosswise
# 0:left 180:right
while True:
Image_Processing()
Find_Line()
Pid()
Control()
if cv2.waitKey(1) == ord('q'):
cv2.destroyAllWindows()
break
其实一开始主要是想玩机器视觉,小车的运动控制研究的不算精细,PID研究的也不深。
有很多是自己的想法,有错误欢迎指正,有建议也欢迎交流,谢谢。
以上是关于用ACR调色核心,HSL面板深度解析,让照片色彩随心而变的主要内容,如果未能解决你的问题,请参考以下文章