I have an image:
Original Image
I want to remove the grey mesh part of the image without affecting the rest of the image i.e., the part inside the black circle.
I have written a code for that
import cv2
import numpy as np
from PIL import Image
imag = Image.open('results.jpg')
imag.show()
pixelMap = imag.load()
img = Image.new( imag.mode, imag.size)
pixelsNew = img.load()
for i in range(img.size[0]):
for j in range(img.size[1]):
if (( pixelMap[i,j]> (200,0,0)) and (pixelMap[i,j]< (240,0,0))):
pixelsNew[i,j] = (255,255,255)
else:
pixelsNew[i,j] = pixelMap[i,j]
img.show()
with this code I have got the following output image:
Output Image
But, Some of the pixels inside the black circle were also changed to white, which is not what I want. I would like to know how can this problem be solved.
You can find the indices of black circle and assign values to the pixels that are either to the left or to the right of black circle. Below is the sample code for this
import cv2
import numpy as np
# read the image
img = cv2.imread('original.png')
cv2.imshow("Image", img)
# convert image to numpy array and also to grayscale
img = np.array(img)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# get height and width of image
[rows, cols] = gray.shape
# now extract one row from image, find indices of black circle
# and make those pixels white which are to the left/right
# of black cirlce
for i in range(rows):
row = gray[i, :] # extract row of image
indices = np.where(row == 0) # find indices of black circle
indices = indices[0]
# if indices are not empty
if len(indices) > 0:
# find starting/ending column index
si = indices[0]
ei = indices[len(indices)-1]
# assign values to the range of pixels
img[i, 0:si-1] = [255, 255, 255]
img[i, ei+1:] = [255, 255, 255]
# if indices is empty then make whole row white
else:
img[i,:] = [255, 255, 255]
cv2.imshow("Modified Image", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Input Image
Output Image
Related
I am trying to detect orange beats in below image
To detect these, I have first cropped the area from original image and then setting high and low hsv values to detect orange. This seems to be working fine. Below is the detected image:
Below is the code:
import cv2
import numpy as np
win_name = "Image"
cv2.namedWindow(win_name)
img = cv2.imread('image.png')
orangeImg = img[420:510, 457:953]
hsv = cv2.cvtColor(orangeImg, cv2.COLOR_BGR2HSV)
lower_bound = np.array([0, 80, 80])
upper_bound = np.array([20, 255, 255])
origMask = cv2.inRange(hsv, lower_bound, upper_bound)
contours, hierarchy = cv2.findContours(origMask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for _, c in enumerate(contours):
areas = [cv2.contourArea(c) for c in contours]
for area in areas:
if area >= 20.0:
boundRect = cv2.boundingRect(c)
rectX = boundRect[0]
rectY = boundRect[1]
rectWidth = boundRect[2]
rectHeight = boundRect[3]
color = (0, 0, 255)
cv2.rectangle(orangeImg, (int(rectX), int(rectY)), (int(rectX + rectWidth), int(rectY + rectHeight)), color, 2)
cv2.imshow(win_name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
In the output image, you can notice that it still has some noise around the bbox created. Is there a better way to reduce the noise in it. Also is there a way to count the detected contours in the image?
I am trying to fetch label 0026 from the attached image:
Initial input image
I tried below code initially to fetch the text:
import pytesseract
BGR = cv2.imread('C:/Users/Choudharyp/CV/11952/01_A_parcel_layer_single_parcel.png')
RGB = cv2.cvtColor(BGR, cv2.COLOR_BGR2RGB)
lower = np.array([175, 125, 45], dtype="uint8")
upper = np.array([255, 255, 255], dtype="uint8")
mask = cv2.inRange(RGB, lower, upper)
img = cv2.bitwise_and(RGB, RGB, mask=mask)
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
gray = 255 - gray
emp = np.full_like(gray, 255)
emp -= gray
emp[emp==0] = 255
emp[emp<100] = 0
gauss = cv2.GaussianBlur(emp, (3,3), 1)
gauss[gauss<220] = 0
text = pytesseract.image_to_string(gauss, config='outputbase digits')
print("Text=>",text)
This did not work possibly because I need to remove green lines from the image.
Hence I first wrote below code to remove green lines from the image and extract only the black colors in the image (this works fine):
# Imports
import cv2
import numpy as np
import pytesseract
# Read image
imagePath = "C:/Users/Choudharyp/CV/11952/" #insert your own loctaion
inputImage = cv2.imread(imagePath + "01_A_parcel_layer_single_parcel.png")
# Conversion to CMYK (just the K channel):
# Convert to float and divide by 255:
imgFloat = inputImage.astype(np.float64) / 255.
# Calculate channel K:
kChannel = 1 - np.max(imgFloat, axis=2)
# Convert back to uint 8:
kChannel = (255 * kChannel).astype(np.uint8)
# Threshold image:
binaryThresh = 190
_, binaryImage = cv2.threshold(kChannel, binaryThresh, 255, cv2.THRESH_BINARY)
cv2.imshow('Black_LettersOnly', binaryImage)
cv2.waitKey(0)
Output looks like below :
Image with only black label
The label 0026 however is too small in the image.
Then, I used the same code as above to fetch the text from the image, however it still doesn't work. Can someone suggest what else I could do to start fetching the labels from the image ?
In binaryImage you get white text on black background. However, Tesseract is optimized to detect dark text on bright background and apparently it works perfectly fine in this case, when you simply invert your image:
...
text = pytesseract.image_to_string(255-binaryImage, config='outputbase digits')
print("Text=>", text.strip())
>>> Text=> 0026
However, if you want it even simpler, I'd prefer HSV color space and threshold the value channel with a one-liner to maintain just the non-colored/black pixels:
...
inputImageHSV = cv2.cvtColor(inputImage, cv2.COLOR_BGR2HSV)
binaryImage = (inputImageHSV[..., 2] > 120).astype(np.uint8) * 255
text = pytesseract.image_to_string(binaryImage, config='outputbase digits')
print("Text=>", text.strip())
>>> Text=> 0026
I am trying to use bitwise operations. In the code below I use 2 images (img1, img2). I create two masks using img2 (gray_inv and gray_test).
img1 = cv2.imread('Computer-Vision-with-Python/DATA/dog_backpack.jpg')
img1 = cv2.cvtColor(img1,cv2.COLOR_BGR2RGB)
img1 = img1[0:600,0:600]
img2 = cv2.imread('Computer-Vision-with-Python/DATA/watermark_no_copy.png')
img2 = cv2.cvtColor(img2,cv2.COLOR_BGR2RGB)
img2 = cv2.resize(img2,(600,600))
gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
gray_inv = cv2.bitwise_not(gray)
gray_test = cv2.bitwise_not(gray_inv)
I use a bitwise_or function merge with img1. The first mask works fine. However the second one does not. Am I missing out on something ? Ideally since the they are inverse gray_inv should have showed the background with the text in black.
plt.imshow(cv2.bitwise_or(img1,img1, mask=gray_inv))
plt.imshow(cv2.bitwise_or(img1,img1, mask=gray_test))
You need to "binarize" the mask.
For your code to work properly, the mask should be a binary image.
In OpenCV, the convention of a binary image is an image with all values 0 or 255 (and type np.uint8).
Using only 0 and 255 is not a "must", but when using cv2.bitwise_not(mask), it's important for the mask to be binary (then all zeros are inverted to 255, and all 255 are inverted to zeros).
The code gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY), converts img2 to grayscale, but not to a binary image (not all values are 0 and 255).
You may apply thresholding for converting gray to binary:
thres_gray = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)[1] # threshold (binarize) the image
Use thres_gray instead of gray:
gray_inv = cv2.bitwise_not(thres_gray)
gray_test = cv2.bitwise_not(gray_inv)
The following code sample demonstrates the solution:
import cv2
import numpy as np
img1 = cv2.imread('img1.png')
img1 = cv2.resize(img1, (100,100))
img2 = cv2.imread('img2.png')
img2 = cv2.resize(img2, (100,100))
gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
thres_gray = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)[1] # threshold (binarize) the image
gray_inv = cv2.bitwise_not(thres_gray)
gray_test = cv2.bitwise_not(gray_inv)
out2 = cv2.bitwise_or(img1, img1, mask=gray_inv)
out3 = cv2.bitwise_or(img1, img1, mask=gray_test)
cv2.imshow('out2', out2)
cv2.imshow('out3', out3)
cv2.waitKey()
cv2.destroyAllWindows()
out2:
out3:
For the effect you’re describing you don’t want a bit-wise OR. You want to multiply the values. So output = input1 * input2 / 255.
I'm trying to remove numbers which are laying inside the circular part of image, numbers are in black in color and background varies between red,yellow, blue and green.
I am using opencv to remove those numbers. I used a mask which extracts numbers from image, with help of cv2.inpaint tried to remove those numbers from images.
For my further analysis I required to have clear image. But my current approach gives distorted image and numbers are not completely removed.
I tried changing the threshold values, lowering will neglect numbers from dark shaded area such as from green and red.
import cv2
img = cv2.imread('scan_1.jpg')
mask = cv2.threshold(img,50,255,cv2.THRESH_BINARY_INV)[1][:,:,0]
cv2.imshow('mask', mask)
cv2.waitKey(0)
cv2.destroyAllWindows()
dst = cv2.inpaint(img, mask, 5, cv2.INPAINT_TELEA)
cv2.imshow('dst',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite('ost_1.jpg',dst)
Input images: a) scan_1.jpg
b) scan_2.jpg
Output images: a) ost_1.jpg
b) ost_2.jpg
Expected Image: Circles can ignored, but something similar to it is required.
Here is my attempt, a better/easier solution might be acquired if you do not care about preserving texts outside of your circle.
import cv2
import numpy as np
# connectivity method used for finding connected components, 4 vs 8
CONNECTIVITY = 4
# HSV threshold for finding black pixels
H_THRESHOLD = 179
S_THRESHOLD = 255
V_THRESHOLD = 150
# read image
img = cv2.imread("a1.jpg")
img_height = img.shape[0]
img_width = img.shape[1]
# save a copy for creating resulting image
result = img.copy()
# convert image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# found the circle in the image
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.7, minDist= 100, param1 = 48, param2 = 100, minRadius=70, maxRadius=100)
# draw found circle, for visual only
circle_output = img.copy()
# check if we found exactly 1 circle
num_circles = len(circles)
print("Number of found circles:{}".format(num_circles))
if (num_circles != 1):
print("invalid number of circles found ({}), should be 1".format(num_circles))
exit(0)
# save center position and radius of found circle
circle_x = 0
circle_y = 0
circle_radius = 0
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
for (x, y, radius) in circles:
circle_x, circle_y, circle_radius = (x, y, radius)
cv2.circle(circle_output, (circle_x, circle_y), circle_radius, (255, 0, 0), 4)
print("circle center:({},{}), radius:{}".format(x,y,radius))
# keep a median filtered version of image, will be used later
median_filtered = cv2.medianBlur(img, 21)
# Convert BGR to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# define range of black color in HSV
lower_val = np.array([0,0,0])
upper_val = np.array([H_THRESHOLD,S_THRESHOLD,V_THRESHOLD])
# Threshold the HSV image to get only black colors
mask = cv2.inRange(hsv, lower_val, upper_val)
# find connected components
components = cv2.connectedComponentsWithStats(mask, CONNECTIVITY, cv2.CV_32S)
# apply median filtering to found components
#centers = components[3]
num_components = components[0]
print("Number of found connected components:{}".format(num_components))
labels = components[1]
stats = components[2]
for i in range(1, num_components):
left = stats[i, cv2.CC_STAT_LEFT] - 10
top = stats[i, cv2.CC_STAT_TOP] - 10
width = stats[i, cv2.CC_STAT_WIDTH] + 10
height = stats[i, cv2.CC_STAT_HEIGHT] + 10
# iterate each pixel and replace them if
#they are inside circle
for row in range(top, top+height+1):
for col in range(left, left+width+1):
dx = col - circle_x
dy = row - circle_y
if (dx*dx + dy*dy <= circle_radius * circle_radius):
result[row, col] = median_filtered[row, col]
# smooth the image, may be necessary?
#result = cv2.blur(result, (3,3))
# display image(s)
cv2.imshow("img", img)
cv2.imshow("gray", gray)
cv2.imshow("found circle:", circle_output)
cv2.imshow("mask", mask)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result for a1:
very new to python. I have used code found in guides etc to try and grab the white pixles in an image but getting stuck. Probably super easy but my if statement to select white is not playing ball. Can anyone help?
#***convert image to no pixels per shade output
import cv2
import numpy as np
from collections import defaultdict
img = cv2.imread('..\\Snapshots\\Me.png')
pixels = img.reshape(-1,3)
counts = defaultdict(int)
for pixel in pixels:
if pixel[0] == pixel[1] == pixel[2]:
counts[pixel[0]] += 1
for pv in sorted(counts.keys()):
print("(%d,%d,%d): %d pixels" % (pv, pv, pv, counts[pv]))
#***count white pixels
from PIL import Image
im = Image.open('..\\snapshots\\Me.png')
white = 0
other = 0
for pixel in im.getdata():
if pixel == (255, 255, 255, 255): # if your image is RGB (if RGBA, (0, 0, 0, 255) or so
white += 1
else:
other += 1
print('white=' + str(white)+', Other='+str(other))
white rgb is (255, 255, 255) not (255, 255, 255, 255)
Also try:
countNonZero(pixel == 255)
(from here: count number of black pixels in an image in Python with OpenCV)
The above code has some formatting issues. Hence, I am posting here the corrected version of that code. It may be helpful for others.
#***convert image to no pixels per shade output
import cv2
import numpy as np
from collections import defaultdict
img = cv2.imread('/Users/monjoysaha/Downloads/generated_images/gen_1.png')
pixels = img.reshape(-1,3)
counts = defaultdict(int)
for pixel in pixels:
if pixel[0] == pixel[1] == pixel[2]:
counts[pixel[0]] += 1
for pv in sorted(counts.keys()):
print("(%d,%d,%d): %d pixels" % (pv, pv, pv, counts[pv]))
#***count white pixels
from PIL import Image
im = Image.open('/Users/monjoysaha/Downloads/generated_images/gen_1.png')
white = 0
other = 0
for pixel in im.getdata():
if pixel == (255, 255, 255, 255): # if your image is RGB (if RGBA, (0, 0, 0, 255) or so
white += 1
else:
other += 1
print('white=' + str(white)+', Other='+str(other))