Đối với Python, tôi chưa tìm thấy chức năng OpenCV cung cấp độ tương phản. Như những người khác đã đề xuất, có một số kỹ thuật để tự động tăng độ tương phản bằng cách sử dụng một công thức rất đơn giản.
Trong các tài liệu OpenCV chính thức, có ý kiến cho rằng phương trình này có thể được sử dụng để áp dụng cả độ tương phản và độ sáng cùng một lúc:
new_img = alpha*old_img + beta
trong đó alpha tương ứng với độ tương phản và beta là độ sáng. Trường hợp khác nhau
alpha 1 beta 0 --> no change
0 < alpha < 1 --> lower contrast
alpha > 1 --> higher contrast
-127 < beta < +127 --> good range for brightness values
Trong C/C ++, bạn có thể thực hiện phương trình này bằng CV :: MAT :: Convertto, nhưng chúng tôi không có quyền truy cập vào phần đó của thư viện từ Python. Để thực hiện nó trong Python, tôi khuyên bạn nên sử dụng chức năng CV :: AddWights, bởi vì nó nhanh chóng và nó tự động buộc đầu ra phải nằm trong phạm vi 0 đến 255 [ví dụ: với hình ảnh màu 24 bit, 8 bit mỗi kênh]. Bạn cũng có thể sử dụng
import cv2
img = cv2.imread['input.png']
# call addWeighted function. use beta = 0 to effectively only operate one one image
out = cv2.addWeighted[ img, contrast, img, 0, brightness]
output = cv2.addWeighted
5 như đề xuất của @Nathancy.import cv2
img = cv2.imread['input.png']
# call addWeighted function. use beta = 0 to effectively only operate one one image
out = cv2.addWeighted[ img, contrast, img, 0, brightness]
output = cv2.addWeighted
Công thức và mã trên nhanh chóng để viết và sẽ thay đổi độ sáng và độ tương phản. Nhưng chúng mang lại kết quả khác biệt đáng kể so với các chương trình chỉnh sửa ảnh. Phần còn lại của câu trả lời này sẽ mang lại kết quả sẽ tái tạo hành vi trong GIMP và độ sáng và độ tương phản của libreoffice. Đó là nhiều dòng mã hơn, nhưng nó cho kết quả tốt đẹp.
Tương phản
Trong GIMP, mức độ tương phản đi từ -127 đến +127. Tôi đã điều chỉnh các công thức từ đây để phù hợp với phạm vi đó.
f = 131*[tương phản + 127]/[127*[131-đối độ]] new_image = f*[old_image-127] + 127 = f*[old_image] + 127*[1-f]
new_image = f*[old_image - 127] + 127 = f*[old_image] + 127*[1-f]
Để tìm ra độ sáng, tôi đã tìm ra mối quan hệ giữa độ sáng và mức độ và thông tin được sử dụng trong bài đăng cấp này để đi đến một giải pháp.
#pseudo code
if brightness > 0
shadow = brightness
highlight = 255
else:
shadow = 0
highlight = 255 + brightness
new_img = [[highlight - shadow]/255]*old_img + shadow
độ sáng và độ tương phản trong python và opencv
Đặt tất cả lại với nhau và thêm bằng cách sử dụng hình ảnh "Mandrill" tham chiếu từ USC SIPI:
import cv2
import numpy as np
# Open a typical 24 bit color image. For this kind of image there are
# 8 bits [0 to 255] per color channel
img = cv2.imread['mandrill.png'] # mandrill reference image from USC SIPI
s = 128
img = cv2.resize[img, [s,s], 0, 0, cv2.INTER_AREA]
def apply_brightness_contrast[input_img, brightness = 0, contrast = 0]:
if brightness != 0:
if brightness > 0:
shadow = brightness
highlight = 255
else:
shadow = 0
highlight = 255 + brightness
alpha_b = [highlight - shadow]/255
gamma_b = shadow
buf = cv2.addWeighted[input_img, alpha_b, input_img, 0, gamma_b]
else:
buf = input_img.copy[]
if contrast != 0:
f = 131*[contrast + 127]/[127*[131-contrast]]
alpha_c = f
gamma_c = 127*[1-f]
buf = cv2.addWeighted[buf, alpha_c, buf, 0, gamma_c]
return buf
font = cv2.FONT_HERSHEY_SIMPLEX
fcolor = [0,0,0]
blist = [0, -127, 127, 0, 0, 64] # list of brightness values
clist = [0, 0, 0, -64, 64, 64] # list of contrast values
out = np.zeros[[s*2, s*3, 3], dtype = np.uint8]
for i, b in enumerate[blist]:
c = clist[i]
print['b, c: ', b,', ',c]
row = s*int[i/3]
col = s*[i%3]
print['row, col: ', row, ', ', col]
out[row:row+s, col:col+s] = apply_brightness_contrast[img, b, c]
msg = 'b %d' % b
cv2.putText[out,msg,[col,row+s-22], font, .7, fcolor,1,cv2.LINE_AA]
msg = 'c %d' % c
cv2.putText[out,msg,[col,row+s-4], font, .7, fcolor,1,cv2.LINE_AA]
cv2.putText[out, 'OpenCV',[260,30], font, 1.0, fcolor,2,cv2.LINE_AA]
cv2.imwrite['out.png', out]
Tôi đã xử lý thủ công các hình ảnh trong GIMP và thêm các thẻ văn bản trong Python/OpenCV:
Lưu ý: @utkarshbhardwaj đã đề xuất rằng người dùng Python 2.x phải chuyển mã tính toán hiệu chỉnh tương phản vào phao để nhận kết quả nổi, như vậy: @UtkarshBhardwaj has suggested that Python 2.x users must cast the contrast correction calculation code into float for getting floating result, like so:
...
if contrast != 0:
f = float[131*[contrast + 127]]/[127*[131-contrast]]
...
Một hình ảnh [kỹ thuật số] được tạo thành bởi nhiều phần tử gọi là pixel [hay điểm ảnh]. Thường thì một hình được biểu diễn bằng một mảng hai chiều gồm nhiều pixels. Mỗi pixel [cụ thể là hình RGB] mang trong mình 3 thông số màu: Red, Green, Blue.Red, Green, Blue. Một chút căn bản về ảnh kỹ thuật số [digital image]
Ngoài ra, đối với không gian RGBA thì nó có một kênh thứ 4 là Alpha, trong đó Alpha là một thông số chỉ độ trong suốt của pixel đó.Alpha, trong đó Alpha là một thông số chỉ độ trong suốt của pixel đó.
Như đã nói ở trên, một pixel mang 3 giá trị màu, vì trên thực tế mỗi pixel được cấu thành bao gồm 3 sub-pixels. Và pixel cũng không có kích thước vật lý cụ thể, mà nó phụ thuộc vào pixel density. Mỗi pixel chứa được 256 [0-255] giá trị, cho nên về mặt lý thuyết 3 sub-pixels hiện thị được 256^3 = 16777216 [~16 triệu màu]. Nhưng tất nhiên là mắt người chưa hẳn đã phân biệt được 16 triệu màu này.kích thước vật lý cụ thể, mà nó phụ thuộc vào pixel density. Mỗi pixel chứa được 256 [0-255] giá trị, cho nên về mặt lý thuyết 3 sub-pixels hiện thị được 256^3 = 16777216 [~16 triệu màu]. Nhưng tất nhiên là mắt người chưa hẳn đã phân biệt được 16 triệu màu này.
Xử lý hình ảnh bằng Pillow và OpenCV
Trong toàn bài này mình sẽ sử dụng ảnh này làm ảnh gốc để chỉnh sửa. Phần code có hơi mì ăn liền cho nên bạn nào khó tính vui lòng bỏ qua.
1. Chuyển ảnh màu sang grayscale
Về cơ bản thì grayscale [hình trắng đen] là loại ảnh mà tất cả pixels chỉ mang thông tin về độ sáng, hay nói cách khác chỉ thể hiện các sắc thái của màu xám [Luminance mode]. Trong không gian màu RGB, thì màu xám là màu mà các sắc tố Red, Green, Blue có giá trị bằng nhau. Ví dụ màu đỏ là
import cv2
img = cv2.imread['input.png']
# call addWeighted function. use beta = 0 to effectively only operate one one image
out = cv2.addWeighted[ img, contrast, img, 0, brightness]
output = cv2.addWeighted
6 thì nếu ta muốn chuyển nó sang xám thì lấy giá trị trung bình của 3 sub-pixels và tạo thành 1 pixel mới import cv2
img = cv2.imread['input.png']
# call addWeighted function. use beta = 0 to effectively only operate one one image
out = cv2.addWeighted[ img, contrast, img, 0, brightness]
output = cv2.addWeighted
7Red, Green, Blue có giá trị bằng nhau. Ví dụ màu đỏ là
import cv2
img = cv2.imread['input.png']
# call addWeighted function. use beta = 0 to effectively only operate one one image
out = cv2.addWeighted[ img, contrast, img, 0, brightness]
output = cv2.addWeighted
6 thì nếu ta muốn chuyển nó sang xám thì lấy giá trị trung bình của 3 sub-pixels và tạo thành 1 pixel mới import cv2
img = cv2.imread['input.png']
# call addWeighted function. use beta = 0 to effectively only operate one one image
out = cv2.addWeighted[ img, contrast, img, 0, brightness]
output = cv2.addWeighted
71.1 Code thủ công
Đầu tiên chúng ta áp dụng lý thuyết ở trên để sửa từng pixel cho ảnh gốc để tạo ra ảnh mới:
from PIL import Image
img = Image.open['rick-morty.png']
pixels = img.load[]
new_img = Image.new[img.mode, img.size]
pixels_new = new_img.load[]
for i in range[new_img.size[0]]:
for j in range[new_img.size[1]]:
r, b, g = pixels[i,j]
avg = int[round[[r + b + g] / 3]]
pixels_new[i,j] = [avg, avg, avg, 0]
new_img.show[]
Chúng ta có ảnh gốc được chuyển thành grayscale như sau:
1.2 Sử dụng Pillow
Pillow là một bản fork của PIL [Python Image Library] một thư viện xử lý hình ảnh của Python. Do PIL được phát hành từ 2009 và không có cập nhật thường xuyên, cho nên PIL đã bị thay thế bở Pillow trên các bản phân phối của Debian và Ubuntu luôn.
from PIL import Image
img = Image.open["rick-morty.png"]
# If you want a greyscale image, simply convert it to the L [Luminance] mode:
new_img = img.convert['L']
new_img.show[]
# Or save it to a file
# new_img.save['rick-morty-L.png']
Kết quả hơi khác so code thủ công ở trên một xíu, ví dụ cái màu áo của Morty ở trên là
import cv2
img = cv2.imread['input.png']
# call addWeighted function. use beta = 0 to effectively only operate one one image
out = cv2.addWeighted[ img, contrast, img, 0, brightness]
output = cv2.addWeighted
8 nhưng ở dưới là import cv2
img = cv2.imread['input.png']
# call addWeighted function. use beta = 0 to effectively only operate one one image
out = cv2.addWeighted[ img, contrast, img, 0, brightness]
output = cv2.addWeighted
92. Thay đổi độ tương phản [contrast]
Để thay đổi độ tương phản của pixel bằng
#pseudo code
if brightness > 0
shadow = brightness
highlight = 255
else:
shadow = 0
highlight = 255 + brightness
new_img = [[highlight - shadow]/255]*old_img + shadow
0 khá đơn giảnfrom PIL import Image, ImageEnhance
# PIL accesses images in Cartesian co-ordinates, so it is Image[columns, rows]
img = Image.open["rick-morty.png"]
# Enhance constrast
enhancer = ImageEnhance.Contrast[img]
for i in range[1, 8]:
factor = i / 4.0
new_img = enhancer.enhance[factor]
new_img.show[]
# Or save it to file
# new_img.save['rick-morty-%s.png' % i]
Trong đó thì factor là một giá trị để kiểm soát độ tương phản, 1.0 là tương đương hình gốc, còn thấp hơn 1.0 là giảm độ tương phản vân vân và mây mây.
factor – A floating point value controlling the enhancement. Factor 1.0 always returns a copy of the original image, lower factors mean less color [brightness, contrast, etc], and higher values more. There are no restrictions on this value.
Mình cũng không rõ Pillow thay đổi độ tương phản bằng cách nào, nhưng về lý thuyết chung thì làm tăng/giảm contrast là làm tối [darkening] nhưng pixel có giá trị dưới một mức nào đó, và làm sáng [lightening] những pixels có giá trị trên một mức nào đó. Độ chênh lệch này sẽ làm tăng hay giảm sự tương phản.
Các bạn có thể xem thêm công thức thay đổi độ tương phản của pixel ở đây, nhưng kết quả từ công thức này không ổn lắm nên mình không post kết quả ở đây.
3. Thay đổi độ sáng [brightness]
Để thay đổi độ sáng của ảnh bằng Pillow cũng rất đơn giản, ta làm như sau:
from PIL import Image, ImageEnhance
img = Image.open["rick-morty.png"]
enhancer = ImageEnhance.Brightness[img]
# Lighter
new_img = enhancer.enhance[1.8]
# Darker
# new_img = enhancer.enhance[0.8]
new_img.show[]
Để thay đổi độ sáng của pixels bằng cách thủ công cũng khá đơn giản, ta chỉ cần tăng hay giảm giá trị của từng pixel là được, chỉ cần lưu ý
#pseudo code
if brightness > 0
shadow = brightness
highlight = 255
else:
shadow = 0
highlight = 255 + brightness
new_img = [[highlight - shadow]/255]*old_img + shadow
1 giá trị đó sao cho nó nằm trong khoảng 0-255 là được.from PIL import Image
def truncate[value]:
if [value < 0]:
return 0
if [value > 255]:
return 255
return value
if __name__ == "__main__":
img = Image.open['rick-morty.png']
pixels = img.load[]
img_new = Image.new[img.mode, img.size]
pixels_new = img_new.load[]
brightness = 20
for i in range[img_new.size[0]]:
for j in range[img_new.size[1]]:
r, b, g = pixels[i,j]
_r = truncate[r + brightness]
_b = truncate[b + brightness]
_g = truncate[g + brightness]
pixels_new[i,j] = [_r, _b, _g, 255]
img_new.show[]
4. Làm mờ [Gaussian blur]
Guassian blur Phương pháp làm mờ hình ảnh sử dụng Gussian function để giảm nhiễu và chi tiết trên bức ảnh.
Giải thích ngắn gọn thì cái ma trận 3x3 trong hình được gọi là
#pseudo code
if brightness > 0
shadow = brightness
highlight = 255
else:
shadow = 0
highlight = 255 + brightness
new_img = [[highlight - shadow]/255]*old_img + shadow
2. Chúng ta áp dụng cái filter như hình cho từng pixel thì cuối cùng chúng ta sẽ có một hình đã được làm mờ. Để sẽ giải thích chi tiết ở các bài sau.import cv2
img = cv2.imread['input.png']
# call addWeighted function. use beta = 0 to effectively only operate one one image
out = cv2.addWeighted[ img, contrast, img, 0, brightness]
output = cv2.addWeighted
05. Làm tối 4 góc [vignette] sử dụng OpenCV và numpy
import cv2
img = cv2.imread['input.png']
# call addWeighted function. use beta = 0 to effectively only operate one one image
out = cv2.addWeighted[ img, contrast, img, 0, brightness]
output = cv2.addWeighted
1Cơm thêm
Phần này không liên quan tới xử lý hình ảnh ở trên
Tìm các cạnh bằng phương pháp Canny [Canny edge detection]
import cv2
img = cv2.imread['input.png']
# call addWeighted function. use beta = 0 to effectively only operate one one image
out = cv2.addWeighted[ img, contrast, img, 0, brightness]
output = cv2.addWeighted
2Vẽ countour của ảnh
Contour trong image proccesing là những đường nối những điểm liền nhau tạo thành hình dáng của vật trong ảnh [không phải contour trong trang điểm]. Nó là một công cụ rất hữu ích trong nhận dạng và định danh các vật thể trong hình [object detection and recognition].
import cv2
img = cv2.imread['input.png']
# call addWeighted function. use beta = 0 to effectively only operate one one image
out = cv2.addWeighted[ img, contrast, img, 0, brightness]
output = cv2.addWeighted
3Nhận diện gương mặt bằng OpenCV
Sử dụng Haar Cascade classifier của OpenCV. Tải về file haarcascade_frontalface_default.xml ở đây
import cv2
img = cv2.imread['input.png']
# call addWeighted function. use beta = 0 to effectively only operate one one image
out = cv2.addWeighted[ img, contrast, img, 0, brightness]
output = cv2.addWeighted
4Subscribe to Koodibar
Get the latest posts delivered right to your inbox