BrightnessContrast Operator Example¶
This example demonstrates usage of BrightnessContrast
oprator.
Brighness and contrast adjustment¶
Brithness and contrast can be adjusted using three parameters: contrast
- makes the dark parts darker and bright parts brighter while keeping the gray (at half dynamic range of the input type) constant,
brightness
- multiplies the intensities,
brightness_shift
- shifts the pixel intensities.
The formula is given as:
where \({contrast\_center}\) denotes a value that is unaffected by contrast changes; by default, it’s at half of the input’s dynamic range (e.g. 128 for uint8
) and \({Max}_{out}\) is the maximum positive value of the output type, for integer types, or 1 for floating point) - but it can also be specified as an argument for the operator. The operator can be used to adjust contrast and brighness as well as to compute negative images (brightness = -1
, brighness_shift = 1
).
Step-by-step guide¶
Let’s start from importing necessary modules (and DALI itself).
[1]:
from nvidia.dali.pipeline import Pipeline
import nvidia.dali.ops as ops
import nvidia.dali.types as types
import matplotlib.pyplot as plt
batch_size = 10
image_filename = "images"
The batch_size
is bigger than one to make it easier to switch images at the end of the example.
Next, let’s implement the pipelines. We’ve presented 2 versions of a pipeline. The CPU one does all the processing (i.e. reading file, decoding it and brightness/contrast manipulation) on the CPU, while the other pipeline conducts these operations on the GPU.
[2]:
class BCCpuPipeline(Pipeline):
def __init__(self, batch_size, num_threads, device_id):
super(BCCpuPipeline, self).__init__(batch_size, num_threads, device_id, seed=42)
self.input = ops.FileReader(device="cpu", file_root=image_filename)
self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB)
self.bc = ops.BrightnessContrast(device="cpu", brightness_shift=0.3, contrast=0.4, contrast_center=100)
def define_graph(self):
read, _ = self.input()
image = self.decode(read)
converted = self.bc(image)
return image, converted
class BCGpuPipeline(Pipeline):
def __init__(self, batch_size, num_threads, device_id):
super(BCGpuPipeline, self).__init__(batch_size, num_threads, device_id, seed=42)
self.input = ops.FileReader(device="cpu", file_root=image_filename)
self.decode = ops.ImageDecoder(device="mixed", output_type=types.RGB)
self.bc = ops.BrightnessContrast(
device="gpu",
contrast=1.5, # increase contrast
brightness_shift = 1, # invert...
brightness=-1, # ...colors
)
def define_graph(self):
read, _ = self.input()
image = self.decode(read)
converted = self.bc(image.gpu())
return image, converted
The function below is used to actually display result of manipulation. Since the pipelines we set up return 2 outputs: modified image and original image, the function aquires both of them from the output and displays them. Additional flag (cpu
) is specified, to determine, whether the pipeline output comes from CPU or GPU. In the latter case, we have to tell the output, to return a CPU-accessible copy of the data.
[3]:
def display(output, cpu = True):
i = 2 # Tweak that to have various images from batch
img1 = output[0].at(i) if cpu else output[0].as_cpu().at(i)
img2 = output[1].at(i) if cpu else output[1].as_cpu().at(i)
fig, ax = plt.subplots(1,2)
ax[0].imshow(img1);
ax[1].imshow(img2);
Now let’s just build the pipelines, run them and display the results. First the GPU one:
[4]:
pipegpu = BCGpuPipeline(batch_size=batch_size, num_threads=1, device_id=0)
pipegpu.build()
[5]:
gpu_output = pipegpu.run()
[6]:
display(gpu_output, cpu=False)
And the CPU:
[7]:
pipecpu = BCCpuPipeline(batch_size=batch_size, num_threads=1, device_id=0)
pipecpu.build()
cpu_output = pipecpu.run()
[8]:
display(cpu_output)