图像处理--自动亮度对比度

git地址:https://github.com/oshawkat/Auto_Brightness_Correction

效果图:
在这里插入图片描述
代码:
example.py

import os
import sys
import cv2
import numpy as np
import numpy.ma as ma
from matplotlib import pyplot as plt

import BrightnessCorrection as BC


SOURCE_DIR = './input'
OUTPUT_DIR = './output'

if __name__ == "__main__":

	if not os.path.exists(OUTPUT_DIR):
		os.makedirs(OUTPUT_DIR)
    # 遍历指定目录(SOURCE_DIR)下的所有文件
	for root, subdirectories, files in os.walk(SOURCE_DIR):
		for file in files:
			# 读取一个文件
			image = cv2.imread(os.path.join(root, file))
			
			# BGR Histogram
			mask = np.full((image.shape[0], image.shape[1], 3), 255, dtype=np.uint8)
			cv2.imwrite(os.path.join(OUTPUT_DIR, '%s_color_mask.jpg' % file), mask)
			histogram = BC.createHistogram(image, mask)
			graph = BC.drawHistogram(histogram)
			graph.title("%s Histogram" % file)
			graph.savefig(os.path.join(OUTPUT_DIR, '%s_Hist.jpg' % file))

			# Combine image layers
			joinedlayers = BC.joinLayers(image)
			print ("Joined layers shape: {}".format(joinedlayers.shape))

			# Joined Histogram
			mask = np.full(joinedlayers.shape, 255, dtype=np.uint8)
			im_mask =  joinedlayers >= 700
			mask[im_mask] = 0
			light_mask = mask
			im_mask = joinedlayers <= 30
			mask[im_mask] = 0
			dark_mask = mask
			cv2.imwrite(os.path.join(OUTPUT_DIR, '%s_joined_mask.jpg' % file), mask)
			joined_hist = BC.createJoinedHistogram(joinedlayers, mask)
			joinedhistgraph = BC.drawHistogram([joined_hist])
			joinedhistgraph.savefig(os.path.join(OUTPUT_DIR, '%s_Comb_Hist.jpg' % file))

			# Find target range
			start, end = BC.findRange(joined_hist, .00, .1)
			print ("Target range is between %d and %d" % (start, end))

			# Rescale brightness
			print ("Scaling images to between {} and {}".format(start, end))
			rescaled = BC.nonlinearRescale(image, joinedlayers, start, end, light_mask, dark_mask)
			cv2.imwrite(os.path.join(OUTPUT_DIR, '%s_Output.jpg' % file), rescaled)
			print ("Min val: {} \t Max val: {}".format(np.min(rescaled), np.max(rescaled)))

			# Create a Histogram for the output image
			output_hist = BC.createHistogram(rescaled, np.full(rescaled.shape, 255, dtype=np.uint8))
			draw_output_hist = BC.drawHistogram(output_hist)
			draw_output_hist.savefig(os.path.join(OUTPUT_DIR, '%s_Output_Hist.jpg' % file))

			print ("\n")

	print ("Processing Complete!")

BrightnessCorrection.py

import cv2
import numpy as np
import numpy.ma as ma
from matplotlib import pyplot as plt



# Construct a histogram for a given image 计算单个通道直方图
# Returns a list of 256 x 1 arrays, with one array for each color channel
# B/W pictures have a single array, color is ordered as BGR
def createHistogram(image, mask):
	histogram = []
	for channel in range(image.shape[2]):
		hist = cv2.calcHist([image.astype(np.uint8)], [channel], mask[:,:,channel], [256], [0, 256])
		histogram.append(hist)
		
	return histogram


# Similar to createHistogram but can process the summation of up to 3 joined layers
# Outputs a single histogram of with 3x the range 
def createJoinedHistogram(image, mask):
	histogram = cv2.calcHist([image.astype(np.uint16)], [0], mask, [768], [0, 768])
	return histogram


# Given a list of histograms, one for each color channel, return a plot of the histograms, all on one chart
# 绘制直方图
def drawHistogram(histograms):
	colors = ('b', 'g', 'r')
	# Clear current plot before making any new ones
	plt.cla()
	plt.clf()

	# B&W vs color images 
	if len(histograms) == 1:
		plt.plot(histograms[0], color='k')	# plot the single graph with a black line
	else:
		for index in range(len(histograms)):
			plt.plot(histograms[index], color=colors[index])

	plt.axis('tight')
	plt.title("Histogram")
	return plt


# Finds the appropriate upper and lower pixel value bounds that excludes the threshold percentage of pixels on both sides of the histogram
# 找到合适的上下像素值边界,排除直方图两侧像素的阈值百分比
def findRange(histogram, lower_threshold, upper_threshold):

	# Calculate total number of pixels in the histogram (if used 
	# with joinHistLayers, will count total for each channel)
	total_pixels = np.sum(histogram)

	# Starting from the bottom of the range, 0, find the intensity 
	# value for which a threshold percent of pixels are excluded
	total = 0
	i = 0
	while np.sum(histogram[:i]) <= total_pixels * lower_threshold:
		i += 1
	start = i - 1

	# Also find upper bound
	total = 0
	i = histogram.shape[0]
	while np.sum(histogram[i:]) <= total_pixels * upper_threshold:
		i -= 1
	end = i + 1

	return start, end


# Produces a numpy array of the same shape as the input image but the range is the sum of each  color channel.  
# Returns a numpy array
# 生成与输入图像形状相同的numpy数组,但范围是每个颜色通道的和。
def joinLayers(image):
	print(image.shape[2])
	output = np.zeros((image.shape[0], image.shape[1]), dtype=np.float)
	for channel in range(image.shape[2]):
		output += image[:, :, channel]
		print(output)
	return output


# Given an image, it's joined values, and a target range, scale the original image to fit the desired histogram
# 给定一张图像,它的连接值和目标范围,缩放原始图像以适应所需的直方图
# Outputs a float numpy array of the same size and shape as image
def linearRescale(image, start, end):

	output = np.zeros(image.shape, dtype=np.float)

	scale = 255 / (end - start)

	for index in range(image.shape[2]):
		output[:,:,index] = (image[:,:,index] - start ) * scale

	lower_bound_mask = output < 0
	output[lower_bound_mask] = 0
	upper_bound_mask = output > 255
	output[upper_bound_mask] = 255
	return output.astype(np.uint8)

# Given an image, it's joined values, and a target range, scale the original image to fit the desired histogram
# Outputs a float numpy array of the same size and shape as image
def nonlinearRescale(image, joinedimage, start, end, light_mask, dark_mask):

	output = np.zeros(image.shape, dtype=np.float)
	scale = 255.0 / (end - start)
	print ("Range is {} and scale is {:.2f}".format(end - start, scale))

	joined_rescaled = (joinedimage - start) * scale 
	for i in range(image.shape[2]):
		print ("Color {}, at avg val of {:5.1f}, represents {:.1%} of the joined image, with avg of {:5.1f}".format(i, np.average(image[:,:,i]), np.average(image[:,:,i]) / np.average(joinedimage), np.average(joinedimage)))
		output[:,:,i] = joined_rescaled * (image[:,:,i] / joinedimage) / (1.0/3.0)
		print ("Average output for this color is {:.1f}".format(np.average(output[:,:,i])))


	adark_mask = output < 15
	output[adark_mask] = output[adark_mask] * 2
	bright_mask = output > 245
	output[bright_mask] = output[bright_mask] * .95

	# Only scale unmasked regions by copying over original image data that was masked
	mask_3Dl = np.full(image.shape, False, dtype=np.uint8)
	mask_3Dd = np.full(image.shape, False, dtype=np.uint8)
	for i in range(image.shape[2]):
		mask_3Dl[:,:, i] = light_mask
		mask_3Dd[:,:, i] = dark_mask
	almask = mask_3Dl == 0
	admask = mask_3Dd == 0
	output[almask] = image[almask] * .98
	output[admask] = image[admask] * 2
	print ("Average output post mask {:.1f} \t Max: {}".format(np.average(output), np.max(output)))

	lower_bound_mask = output < 0
	output[lower_bound_mask] = 0
	upper_bound_mask = output > 255
	output[upper_bound_mask] = 255
	
	# Adjust the brightest and darkest areas with a non-linear scaling
	# dark_boost_max = 2		# Max scaling to increase darkest areas
	# dark_boost_range = 15
	# for i in range(0, dark_boost_range):
	# 	adark_mask = output == i
	# 	output[adark_mask] = output[adark_mask] * dark_boost_max * (dark_boost_range - i) / dark_boost_range

	# light_boost_max = .1		# Max scaling to increase darkest areas
	# light_boost_range = 230
	# for i in range(dark_boost_range, 256):
	# 	alight_mask = output == i
	# 	output[alight_mask] = output[alight_mask] * (1 - (light_boost_max * (i - light_boost_range) / (255 - light_boost_range)))

	return output
	# return output.astype(np.uint8)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力减肥的小胖子5

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值