How To Brighten A OpenCV Image - python

I have managed to get a saliency map of an image using the following code below. Here it obtains the saliency map as well as the inverse saliency map and then applies the results to the image in order to get the foreground and background of the image.
import cv2
import numpy as np
def SalienceMaps(image_path):
image = cv2.imread(image_path)
dimensions = (350, 450)
resized = cv2.resize(image, dimensions, interpolation = cv2.INTER_AREA)
saliency = cv2.saliency.StaticSaliencyFineGrained_create()
(success, saliencyMap) = saliency.computeSaliency(resized)
(B, G, R) = cv2.split(resized)
salBlue = B * saliencyMap.astype(saliencyMap.dtype)
salGreen = G * saliencyMap.astype(saliencyMap.dtype)
salRed= R * saliencyMap.astype(saliencyMap.dtype)
salBlue = salBlue.astype("uint8")
salGreen = salGreen.astype("uint8")
salRed = salRed.astype("uint8")
reduction = np.ones((450,350))
inverse = reduction - saliencyMap
inverseBlue = B * inverse.astype(inverse.dtype)
inverseGreen = G * inverse.astype(inverse.dtype)
inverseRed = R * inverse.astype(inverse.dtype)
inverseBlue = inverseBlue.astype("uint8")
inverseGreen = inverseGreen.astype("uint8")
inverseRed = inverseRed.astype("uint8")
main = cv2.merge((salBlue, salGreen, salRed))
inverse = cv2.merge((inverseBlue, inverseGreen, inverseRed))
return (main, inverse)
(main, inverse) = SalienceMaps("Content Image.jpg")
cv2.imshow("Image", main)
cv2.imshow("Image 2", inverse)
cv2.waitKey(0)
I get the following output for the foreground and background respectively when this image has been put in:
I was wondering, how to improve the brightness or features of the foreground so the features are a lot more prominant and the image is brighter if that makes sense? Any idea what could be done? Cheers

You can use cv2.normalize() in Python/OpenCV to increase brightness.
Input:
import cv2
import numpy as np
# Read images
img = cv2.imread('img.png')
# stretch mask to full dynamic range
result = cv2.normalize(img, None, alpha=0, beta=350, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)
# write results
cv2.imwrite("img_enhanced.png", result)
# show results
cv2.imshow("img", img)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:

Related

Subtract vignetting template from image in OpenCV Python

I have 750+ images, like this 'test.png', that I need to subtract the vignetting in 'vig-raw.png' from. I just started using opencv-python, so "I don't even know what I don't know".
Using GIMP, I desaturated 'vig-raw.png' to create 'vig-desat.png', which I then converted with Color to Alpha to create 'vig-alpha.png'.
This is my attempt to subtract 'vig-alpha.png' from 'test.png'.
import cv2 as cv
import numpy as np
img1 = cv.imread('test.png',0)
img1 = cv.cvtColor(img1, cv.COLOR_BGR2BGRA) # add alpha channel to RGB image
print(img1[0][0]) # show alpha
img2 = cv.imread('vig-alpha.png',flags=cv.IMREAD_UNCHANGED) # read RGBA image
print(img2[0][0]) #show alpha
img3 = cv.subtract(img1, img2)
img3 = cv.resize(img3, (500,250))
print(img3[0][0]) # show alpha
cv.imshow('result',img3)
cv.waitKey()
cv.destroyAllWindows()
However, this is the 'result'. I need to produce a uniform shading throughout the image while leaving the original colors intact. I don't know the correct terminology for this sort of thing, and it's hard to search for a solution with what I do know. Thanks in advance.
EDIT: As per Rotem's answer, image file format matters. StackOverflow converted the PNG files I posted to JPEG, which did effect results while checking their answer. See the comment I left on Rotem's answer below for more information.
Vignette template is not supposed to be subtracted, it supposed to be scaled.
The vignette correction process is known as Flat-field correction applies:
G = m / (F - D)
C = (R - D) * G
When D is dark field or dark frame.
We don't have dark frame sample - we may assume that the dark frame is all zeros.
Assuming D=zeros, the correction formula is:
G = m / F
C = R * G
m = mean(F), and F applies vig-alpha.
R is test.png.
For computing G (name it inv_vig_norm, we may use the following stages):
Read vig-alpha.png as grayscale, and convert it to float in range [0, 1] (vig_norm applies F):
vig = cv2.imread('vig-alpha.png', cv2.IMREAD_GRAYSCALE)
vig_norm = vig.astype(np.float32) / 255
Divide m by F:
vig_mean_val = cv2.mean(vig_norm)[0]
inv_vig_norm = vig_mean_val / vig_norm # Compute G = m/F
Compute C = R * G - scale img1 by inv_vig_norm:
inv_vig_norm = cv2.cvtColor(inv_vig_norm, cv2.COLOR_GRAY2BGR)
img2 = cv2.multiply(img1, inv_vig_norm, dtype=cv2.CV_8U) # Compute: C = R * G
For removing noise and artifacts, we may apply Median Blur and Gaussian Blur over vig (it may be required because the site converted vig-alpha.png to JPEG format).
Code sample:
import cv2
import numpy as np
img1 = cv2.imread('test.png')
vig = cv2.imread('vig-alpha.png', cv2.IMREAD_GRAYSCALE) # Read vignette template as grayscale
vig = cv2.medianBlur(vig, 15) # Apply median filter for removing artifacts and extreem pixels.
vig_norm = vig.astype(np.float32) / 255 # Convert vig to float32 in range [0, 1]
vig_norm = cv2.GaussianBlur(vig_norm, (51, 51), 30) # Blur the vignette template (because there are still artifacts, maybe because SO convered the image to JPEG).
#vig_max_val = vig_norm.max() # For avoiding "false colors" we may use the maximum instead of the mean.
vig_mean_val = cv2.mean(vig_norm)[0]
# vig_max_val / vig_norm
inv_vig_norm = vig_mean_val / vig_norm # Compute G = m/F
inv_vig_norm = cv2.cvtColor(inv_vig_norm, cv2.COLOR_GRAY2BGR) # Convert inv_vig_norm to 3 channels before using cv2.multiply. https://stackoverflow.com/a/48338932/4926757
img2 = cv2.multiply(img1, inv_vig_norm, dtype=cv2.CV_8U) # Compute: C = R * G
cv2.imshow('inv_vig_norm', cv2.resize(inv_vig_norm / inv_vig_norm.max(), (500, 250))) # Show inv_vig_norm for testing
cv2.imshow('img1', cv2.resize(img1, (500, 250)))
cv2.imshow('result', cv2.resize(img2, (500, 250)))
cv2.waitKey()
cv2.destroyAllWindows()
Results:
img1:
inv_vig_norm:
img2:

How do I crop an image based on custom mask in python?

Below I have attached two images. I want the first image to be cropped in a heart shape according to the mask image (2nd image).
I searched for solutions but I was not able to get the simple and easier way to do this. Kindly help me with the solution.
2 images:
Image to be cropped:
Mask image:
Let's start by loading the temple image from sklearn:
from sklearn.datasets import load_sample_images
dataset = load_sample_images()
temple = dataset.images[0]
plt.imshow(temple)
Since, we need to use the second image as mask, we must do a binary thresholding operation. This will create a black and white masked image, which we can then use to mask the former image.
from matplotlib.pyplot import imread
heart = imread(r'path_to_im\heart.jpg', cv2.IMREAD_GRAYSCALE)
_, mask = cv2.threshold(heart, thresh=180, maxval=255, type=cv2.THRESH_BINARY)
We can now trim the image so its dimensions are compatible with the temple image:
temple_x, temple_y, _ = temple.shape
heart_x, heart_y = mask.shape
x_heart = min(temple_x, heart_x)
x_half_heart = mask.shape[0]//2
heart_mask = mask[x_half_heart-x_heart//2 : x_half_heart+x_heart//2+1, :temple_y]
plt.imshow(heart_mask, cmap='Greys_r')
Now we have to slice the image that we want to mask, to fit the dimensions of the actual mask. Another shape would have been to resize the mask, which is doable, but we'd then end up with a distorted heart image. To apply the mask, we have cv2.bitwise_and:
temple_width_half = temple.shape[1]//2
temple_to_mask = temple[:,temple_width_half-x_half_heart:temple_width_half+x_half_heart]
masked = cv2.bitwise_and(temple_to_mask,temple_to_mask,mask = heart_mask)
plt.imshow(masked)
If you want to instead make the masked (black) region transparent:
tmp = cv2.cvtColor(masked, cv2.COLOR_BGR2GRAY)
_,alpha = cv2.threshold(tmp,0,255,cv2.THRESH_BINARY)
b, g, r = cv2.split(masked)
rgba = [b,g,r, alpha]
masked_tr = cv2.merge(rgba,4)
plt.axis('off')
plt.imshow(dst)
Since I am on a remote server, cv2.imshow doesnt work for me. I imported plt.
This code does what you are looking for:
import cv2
import matplotlib.pyplot as plt
img_org = cv2.imread('~/temple.jpg')
img_mask = cv2.imread('~/heart.jpg')
##Resizing images
img_org = cv2.resize(img_org, (400,400), interpolation = cv2.INTER_AREA)
img_mask = cv2.resize(img_mask, (400,400), interpolation = cv2.INTER_AREA)
for h in range(len(img_mask)):
for w in range(len(img_mask)):
if img_mask[h][w][0] == 0:
for i in range(3):
img_org[h][w][i] = 0
else:
continue
plt.imshow(img_org)

Is there a better way to separate the writing from the background?

I am working on a project where I should apply and OCR on some documents.
The first step is to threshold the image and let only the writing (whiten the background).
Example of an input image: (For the GDPR and privacy reasons, this image is from the Internet)
Here is my code:
import cv2
import numpy as np
image = cv2.imread('b.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
h = image.shape[0]
w = image.shape[1]
for y in range(0, h):
for x in range(0, w):
if image[y, x] >= 120:
image[y, x] = 255
else:
image[y, x] = 0
cv2.imwrite('output.jpg', image)
Here is the result that I got:
When I applied pytesseract to the output image, the results were not satisfying (I know that an OCR is not perfect). Although I tried to adjust the threshold value (in this code it is equal to 120), the result was not as clear as I wanted.
Is there a way to make a better threshold in order to only keep the writing in black and whiten the rest?
After digging deep in StackOverflow questions, I found this answer which is about removing watermark using opencv.
I adapted the code to my needs and this is what I got:
import numpy as np
import cv2
image = cv2.imread('a.png')
img = image.copy()
alpha =2.75
beta = -160.0
denoised = alpha * img + beta
denoised = np.clip(denoised, 0, 255).astype(np.uint8)
#denoised = cv2.fastNlMeansDenoising(denoised, None, 31, 7, 21)
img = cv2.cvtColor(denoised, cv2.COLOR_BGR2GRAY)
h = img.shape[0]
w = img.shape[1]
for y in range(0, h):
for x in range(0, w):
if img[y, x] >= 220:
img[y, x] = 255
else:
img[y, x] = 0
cv2.imwrite('outpu.jpg', img)
Here is the output image:
The good thing about this code is that it gives good results not only with this image, but also with all the images that I tested.
I hope it helps anyone who had the same problem.
You can use adaptive thresholding. From documentation :
In this, the algorithm calculate the threshold for a small regions of the image. So we get different thresholds for different regions of the same image and it gives us better results for images with varying illumination.
import numpy as np
import cv2
image = cv2.imread('b.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
image = cv2.medianBlur(image ,5)
th1 = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,\
cv2.THRESH_BINARY,11,2)
th2 = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv2.THRESH_BINARY,11,2)
cv2.imwrite('output1.jpg', th1 )
cv2.imwrite('output2.jpg', th2 )

opencv python connectedComponents select component per label

I want to select each component of this image :
In practice, each and every triangle, by its labels. I don't figure out how.
I have this code:
#!/usr/bin/python
import cv2
import numpy as np
img = cv2.imread('invMehs.png', -1)
imGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, imBw = cv2.threshold(imGray, 250, 255, cv2.THRESH_BINARY)
invBwMesh = cv2.bitwise_not(imBw)
Mask = np.ones(imBw.shape, dtype="uint8") * 255
connectivity = 4
output = cv2.connectedComponentsWithStats(imBw, connectivity, cv2.CV_32S)
num_labels = output[0]
labels = output[1]
stats = output[2]
centroids = output[3]
labels = labels + 1
b = ( labels == 1)
cv2.imwrite('tst.jpg',labels[b])
But the image is complety black :S
Thank you very much.
The image you want save (labels[b]) only contains the thin lines (greylevel 1). When saving image using JPEG format, the compression algorithm smooths them, but since they have only 1 greylevel with the background, they are erased. That's why you get a black image
Saving in PNG format do not alter the image labels.
In order to keep all labels for each connected component (0 for the background), the code to write should be :
cv2.imwrite('labels.png',output[1])

Overlay images using python library

I wanted to overlay one image over another dynamically using a Python library, and was wondering which one would be easy to use.
Thanks for your help!
If you want to overlay two images, simply use the OpenCV libraries.
[Sample]
Here is the sample python code using OpenCV to overlay image1 and image2
import cv2
import numpy as np
def overlay(image1, image2, x, y):
image1_gray = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
_, contours, _ = cv2.findContours(image1_gray, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
image1_mask = np.zeros_like(image1)
cv2.drawContours(image1_mask, contours, -1, (255,255,255), -1)
idx = np.where(image1_mask == 255)
image2[y+idx[0], x+idx[1], idx[2]] = image1[idx[0], idx[1], idx[2]]
return image2
if __name__ == '__main__':
grass_img = cv2.imread('grassland.jpg')
horse_img = cv2.imread('horse.png')
overlayed = overlay(horse_img, grass_img, 300, 300)
cv2.imwrite('overlayed.png', overlayed)
The result image was resized to reduce its volume, but in the above code, the resize code was omitted.
Result:
Update!
Here is the code using image's alpha value, and the output is better than before.
The idea is from overlay a smaller image on a larger image python OpenCv
def better_overlay(image1, image2, x, y):
image1_alpha = image1[:, :, 3] / 255.0
height, width = image1.shape[0], image1.shape[1]
for c in range(3):
image2[y:y+height, x:x+width, c] = image1_alpha * image1[:, :, c] + (1.0 - image1_alpha)* image2[y:y+height, x:x+width, c]
return image2
Result:
Usually PIL(Python Imaging Library) is for image processing stuff.

Categories