How to fill OpenCV image with one solid color? - image-processing

How to fill OpenCV image with one solid color?

Using the OpenCV C API with IplImage* img:
Use cvSet(): cvSet(img, CV_RGB(redVal,greenVal,blueVal));
Using the OpenCV C++ API with cv::Mat img, then use either:
cv::Mat::operator=(const Scalar& s) as in:
img = cv::Scalar(redVal,greenVal,blueVal);
or the more general, mask supporting, cv::Mat::setTo():
img.setTo(cv::Scalar(redVal,greenVal,blueVal));

Here's how to do with cv2 in Python:
# Create a blank 300x300 black image
image = np.zeros((300, 300, 3), np.uint8)
# Fill image with red color(set each pixel to red)
image[:] = (0, 0, 255)
Here's more complete example how to create new blank image filled with a certain RGB color
import cv2
import numpy as np
def create_blank(width, height, rgb_color=(0, 0, 0)):
"""Create new image(numpy array) filled with certain color in RGB"""
# Create black blank image
image = np.zeros((height, width, 3), np.uint8)
# Since OpenCV uses BGR, convert the color first
color = tuple(reversed(rgb_color))
# Fill image with color
image[:] = color
return image
# Create new blank 300x300 red image
width, height = 300, 300
red = (255, 0, 0)
image = create_blank(width, height, rgb_color=red)
cv2.imwrite('red.jpg', image)

Create a new 640x480 image and fill it with purple (red+blue):
cv::Mat mat(480, 640, CV_8UC3, cv::Scalar(255,0,255));
Note:
height before width
type CV_8UC3 means 8-bit unsigned int, 3 channels
colour format is BGR

The simplest is using the OpenCV Mat class:
img=cv::Scalar(blue_value, green_value, red_value);
where img was defined as a cv::Mat.

Use numpy.full. Here's a Python that creates a gray, blue, green and red image and shows in a 2x2 grid.
import cv2
import numpy as np
gray_img = np.full((100, 100, 3), 127, np.uint8)
blue_img = np.full((100, 100, 3), 0, np.uint8)
green_img = np.full((100, 100, 3), 0, np.uint8)
red_img = np.full((100, 100, 3), 0, np.uint8)
full_layer = np.full((100, 100), 255, np.uint8)
# OpenCV goes in blue, green, red order
blue_img[:, :, 0] = full_layer
green_img[:, :, 1] = full_layer
red_img[:, :, 2] = full_layer
cv2.imshow('2x2_grid', np.vstack([
np.hstack([gray_img, blue_img]),
np.hstack([green_img, red_img])
]))
cv2.waitKey(0)
cv2.destroyWindow('2x2_grid')

For an 8-bit (CV_8U) OpenCV image, the syntax is:
Mat img(Mat(nHeight, nWidth, CV_8U);
img = cv::Scalar(50); // or the desired uint8_t value from 0-255

color=(200, 100, 255) # sample of a color
img = np.full((100, 100, 3), color, np.uint8)

If you are using Java for OpenCV, then you can use the following code.
Mat img = src.clone(); //Clone from the original image
img.setTo(new Scalar(255,255,255)); //This sets the whole image to white, it is R,G,B value

I personally made this python code to change the color of a whole image opened or created with openCV . I am sorry if it's not good enough , I am beginner 😚😚 .
def OpenCvImgColorChanger(img,blue = 0,green = 0,red = 0):
line = 1
ImgColumn = int(img.shape[0])-2
ImgRaw = int(img.shape[1])-2
for j in range(ImgColumn):
for i in range(ImgRaw):
if i == ImgRaw-1:
line +=1
img[line][i][2] = int(red)
img[line][i][1] = int(green)
img[line][i][0] = int(blue)

Related

Remove Yellow rectangle from image

I am using this code to remove this yellow stamp from an image :
import cv2
import numpy as np
# read image
img = cv2.imread('input.jpg')
# threshold on yellow
lower = (0, 200, 200)
upper = (100, 255, 255)
thresh = cv2.inRange(img, lower, upper)
# apply dilate morphology
kernel = np.ones((9, 9), np.uint8)
mask = cv2.morphologyEx(thresh, cv2.MORPH_DILATE, kernel)
# get largest contour
contours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)
x, y, w, h = cv2.boundingRect(big_contour)
# draw filled white contour on input
result = img.copy()
cv2.drawContours(result, [big_contour], 0, (255, 255, 255), -1)
cv2.imwrite('yellow_removed.png', result)
# show the images
cv2.imshow("RESULT", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
I get the following error:
big_contour = max(contours, key=cv2.contourArea) ValueError: max() arg
is an empty sequence
Obviously, it is not detecting any contours, and the contours array is empty, but I could not figure out why that is or how to fix it.
Help is appreciated!
Check your lower thresholds. It worked for me for both images when I changed the lower threshold to lower = (0, 120, 120).
The thresholds is the reason due to the second image being darker. Lowering these thresholds captures more of the yellow area, but will still leave some holes when drawing the contour.
lower = (0, 130, 130)
You can fix this by drawing the bounding rectangle instead.
cv2.rectangle(result,(x,y),(x+w,y+h),(255,255,255),-1)
Using HSV color space is great for figuring out a particular shade/tone of color. When you have dominant colors to isolate, you can opt for the LAB color space. I have explained as to why this is better in this answer.
Code:
img = cv2.imread('bill.jpg')
# create another copy for the result
img2 = img.copy()
# convert to LAB space and store b-channel
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
b_channel = lab[:,:,-1]
Notice how bright the yellow region is above.
# Perform Otsu threshold
th = cv2.threshold(b_channel, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# Find the contour with largest area
contours, hierarchy = cv2.findContours(th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
c = max(contours, key = cv2.contourArea)
# draw the contour on plain black image of same shape as original
mask = np.zeros((img.shape[0], img.shape[1]), np.uint8)
mask = cv2.drawContours(mask,[c],0,255, -1)
# dilation to avoid border effects
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
dilate = cv2.dilate(mask, kernel, iterations=1)
img2[dilate == 255] = (255, 255, 255)
Another example:
Input:
Result:

Determine flow meter reading from image

currently I'm working on a project to identify the rotameter's reading. My main task is to located the rotameter from the input image, then identify the line mark from the rotameter as well as the floater. Then, I need to find out the reading of the meter based on the position of the floater. There are a few challenges faced, the glare on the meter due to illumination and the angle of the image taken. Currently, I tried with CLAHE followed by Canny edge detection. However, I was only able to get the line mark at the area without light reflection. I had also tried Inpaint + CLAHE to remove the glare part and Gaussian blurring to reduce the image noises. But most on the line marks are missing. I'm still new in computer vision and currently using Python + OpenCV. Any good suggestions?
Source image
Here's the code I still trying on for Inpaint + CLAHE, and the outcome is attached.
Outcome: Inpaint + Clahe
img = cv2.imread('IMG-20210630-WA0016.jpeg')
resized = cv2.resize(img, (540, 960))
crp = resized[0:960, 150:360]
# convert image from RGB to HSV
img_hsv = cv2.cvtColor(crp, cv2.COLOR_RGB2HSV)
# Histogram equalisation on the V-channel
img_hsv[:, :, 2] = cv2.equalizeHist(img_hsv[:, :, 2])
# convert image back from HSV to RGB
image = cv2.cvtColor(img_hsv, cv2.COLOR_HSV2RGB)
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
# Create a CLAHE object (Arguments are optional).
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(4,4))
cl1 = clahe.apply(gray)
cv2.imshow('Clahe', cl1)
# mask = cv2.threshold(cl1, 180, 185, cv2.THRESH_BINARY)[1]
mask = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
# use mask with input to do inpainting
inpaint = cv2.inpaint(cl1, mask, 21, cv2.INPAINT_NS)
# display it
cv2.imshow("IMAGE", crp)
cv2.imshow("GRAY", gray)
result1 = cv2.GaussianBlur(inpaint, (1, 1), 1, cv2.BORDER_DEFAULT)
canny = cv2.Canny(result1, 100, 255)
cv2.imshow("Canny", canny)
key = cv2.waitKey(0)
if key == 27:
cv2.destroyAllWindows()
For this, I just tried with CLAHE, the line marks look clearer but the position of the floater are missing.
Outcome 2: Clahe
img = cv2.imread('IMG-20210630-WA0016.jpeg')
resized = cv2.resize(img, (540, 960))
crp = resized[0:960, 150:360]
gray = cv2.cvtColor(crp,cv2.COLOR_BGR2GRAY)
# Create a CLAHE object (Arguments are optional).
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(4,4))
cl1 = clahe.apply(gray)
result1 = cv2.GaussianBlur(cl1, (3, 3), 1, cv2.BORDER_DEFAULT)
canny = cv2.Canny(result1, 150, 255)
cv2.imshow("Canny", canny)
cv2.imshow("Source", crp)
cv2.imshow('Clahe', cl1)
key = cv2.waitKey(0)
if key == 27:
cv2.destroyAllWindows()

How can i remove background noise from a handwritten text image?

I tried these approaches but didn't get any real changes. actually, I am trying to build a handwritten OCR using Google cloud vision API. please suggest to me what can I do for preprocessing steps.
1.
image = cv2.fastNlMeansDenoisingColored(image, None, 10, 10, 7, 15)
kernel = np.ones((5, 5), np.uint8)
image = cv2.dilate(image, kernel, iterations = 1)
kernel = np.ones((5, 5), np.uint8)
image = cv2.erode(image, kernel, iterations = 1)
Another way is HSV color filter. Because you are using blue pen, so we can choice the color that we want. Sample code:
import cv2
import numpy as np
image = cv2.imread('9rS31.jpg')
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower_green = np.array([100, 43, 20])
upper_green = np.array([130, 255, 255])
mask = cv2.inRange(hsv, lower_green, upper_green)
res = cv2.bitwise_and(image, image, mask=mask)
gray = cv2.cvtColor(res,cv2.COLOR_BGR2GRAY)
ret, generator = cv2.threshold(gray, 1,255,cv2.THRESH_BINARY)
cv2.imwrite("img.jpg",generator)
Generated image:
The noise is including horizontal line in your text book. So one method is using
cv2.getStructuringElement
You can find more information on the internet. Sample code:
import cv2
# Load image
image = cv2.imread('9rS31.jpg')
img=image.copy()
# Remove border
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (50,1))
temp2 = 255 - cv2.morphologyEx(image, cv2.MORPH_CLOSE, horizontal_kernel)
result = cv2.add(temp2, image)
# Convert to grayscale and Otsu's threshold
gray = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray,(5,5),0)
_,thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY_INV)
cv2.imwrite('img.jpg',thresh)
cv2.imshow('img', thresh)
cv2.waitKey()
Generated image:

I want to detect all the underlined words in a paragraph

Original Image
Click here for the image
For this, I am trying to detect the underlines first. But as the underlines might be tilted, this code:
import time
from google.colab.patches import cv2_imshow
from collections import OrderedDict
# Let's load a simple image with 3 black squares
image = cv2.imread("line_detected.png")
cv2.waitKey(0)
# Grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Find Canny edges
font = cv2.FONT_HERSHEY_COMPLEX
edged = cv2.Canny(gray, 30, 200)
cv2.waitKey(0)
# Finding Contours
# Use a copy of the image e.g. edged.copy()
# since findContours alters the image
contours, hierarchy = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2_imshow(edged)
cv2.waitKey(0)
print("Number of Contours found = " + str(len(contours)))
# Draw all contours
# -1 signifies drawing all contours
# cv2.drawContours(image, contours, -1, (0, 255, 0), 3)
mask = np.ones(image.shape[:2], dtype="uint8") * 255
d=OrderedDict()
coords=[]
nuclei = []
l=[]
heading=[]
images=[]
lvalue=0
line=[]
h=[]
contours = contours[::-1]
for cnt in (contours):
peri = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, 0.04 * peri, True)
if (len(approx==2)):
x, y, w, h = cv2.boundingRect(cnt)
# print(h)
cv2.rectangle(img,(x, y), (x+w, y+h),(0, 0, 255), 2)
cv2_imshow(img)
is not able to detect the slanting underlines very properly. Also, I want this code to extend to detecting only the gray underlines. "minor differences" has a single underline as it is slanted/tilted, it reads it as two straight lines. Also, it is reading the images in the left which it should not read(tesseract giving weird outputs).
For the gray shade only I found this mask thing online:
lower_range = np.array([110,50,50])
upper_range = np.array([130,255,255])
mask = cv2.inRange(hsv, lower_range, upper_range)
But Don't know how to incorporate in code... I'm a beginner, any help is much appreciated!

How to whiten background and blaken grid in a same image

I have an image like this. I wan to use HoughLine detection but the image is too dark to recognize the line. Is there a way that can whiten the background and blacken the grid? Is there any algorithms in openCV or python that I can apply? Thank you
I try to dilate the image first, then medianBlur it, so I get the background. Use the original gray image to sub the background, I get the frontground ( that is the grids). Then do some other steps, I get the result like this.
The code is as follow:
#!/usr/bin/python3
# 2017.10.04 19:37:43 CST
filename = "data/paper.png"
img = cv2.imread(filename)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
## do morph-dilate-op
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
dilated = cv2.morphologyEx(gray, cv2.MORPH_DILATE, kernel)
diff1 = 255 - cv2.subtract(dilated, gray)
## do medianBlur
median = cv2.medianBlur(dilated, 15)
diff2 = 255 - cv2.subtract(median, gray)
## do normalize
normed = cv2.normalize(diff2,None, 0, 255, cv2.NORM_MINMAX )
## save the result
dst = np.hstack((gray, normed))
cv2.imwrite("result_paper1.png", dst)
res = np.hstack((gray,dilated, diff1, median, diff2, normed))
cv2.imwrite("result_paper2.png", res)
You should try a form of localized adaptive thresholding.
In OpenCV this is called cv2.adaptiveThreshold.
See here: http://docs.opencv.org/master/d7/d4d/tutorial_py_thresholding.html for a python example.
The code (from the source above):
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('sudoku.png',0)
img = cv2.medianBlur(img,5)
ret,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
th2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,\
cv2.THRESH_BINARY,11,2)
th3 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv2.THRESH_BINARY,11,2)
titles = ['Original Image', 'Global Thresholding (v = 127)',
'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]
for i in range(4):
plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
Your image has poor contrast and inconsistent light. You have to make some preprocessing (c++ code here):
cv::Mat img = cv::imread("E:\\Workspace\\KS\\excercise\\oBwBH.jpg", 0);
cv::Mat workingMat;
cv::GaussianBlur(img, workingMat, cv::Size(101, 101), 31, 31); //high blur to extract background light
img = img - 0.7*work; //adjust light level
cv::normalize(img, img, 0, 255, cv::NORM_MINMAX); \\use whole range
cv::medianBlur(img, img, 5); \\remove noise
cv::Canny(img, work, 100, 200); \\extract lines; you could do hough lines instead since it has canny inside.
Results (from left to right, canny has all lines, in preview there is some compression issue):

Resources