How to remove background gridlines from scanned images with opencv - python-3.x

Input Image I am trying to remove background gridlines from scanned images using OpenCV, till now I have used HoughLine methods to detect lines and fill it with white color.
By this method I'm able to detect horizonal lines but not vertical lines.
Here is my code
'''
import cv2
import numpy as np
def rmv_lines(Image_Path):
img = cv2.imread(Image_Path)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,50,150,apertureSize = 3)
minLineLength, maxLineGap = 100, 15
lines = cv2.HoughLinesP(edges,1,np.pi/180,100,minLineLength,maxLineGap)
for x in range(0, len(lines)):
for x1,y1,x2,y2 in lines[x]:
#if x1 != x2 and y1 != y2:
cv2.line(img,(x1,y1),(x2,y2),(255,255,255),4)
return cv2.imwrite('removed.jpg',img)
'''
Any help or suggestion...

Related

Adding a border around Region of Interest using python

I have a code which takes images from a folder, crops the region of interest around it using the ROI function, and then removes the background using the rembg library. But I want a border around that image, around that specific object itself, like the one we get in segmentation, except keeping the colours and the object intact. [NOT A RECTANGULAR BORDER]. Can anyone help and tell me how to do it?
Here is my code for reference:
import cv2
import numpy as np
import os
from os.path import join
from os import listdir
from PIL import Image
from rembg import remove
path = 'Some path'
folder = 'Some other path'
count = 1
def image_resize(image, width = None, height = None, inter = cv2.INTER_AREA):
dim = None
(h, w) = image.shape[:2]
if width is None and height is None:
return image
if width is None:
r = height / float(h)
dim = (int(w * r), height)
else:
r = width / float(w)
dim = (width, int(h * r))
resized = cv2.resize(image, dim, interpolation = inter)
return resized
for filename in os.listdir(folder):
img = cv2.imread(os.path.join(folder,filename))
if img is not None:
img = image_resize(img, height = 600)
roi = cv2.selectROI(img)
print(roi)
im_cropped = img[int(roi[1]):int(roi[1]+roi[3]),int(roi[0]):int(roi[0]+roi[2])]
rs = str(count)
rem = remove(im_cropped)
cv2.imshow("Removed Image", rem)
cv2.imwrite(os.path.join(path, rs + '.jpg'), rem)
count = count + 1
cv2.waitKey(0)
As i assume you mean a simple outline, i would suggest the following:
import cv2
import numpy as np
# Threshold the image
thresh = img > threshold
# Use binary dilation to widen the area
thresh_dil = cv2.dilate(thresh, np.ones((3, 3)) , iterations=1)
# Get the outlines by substracting the dilated image with the original area
outlines = thresh_dil - thresh
# Superimpose the outlines on your original image
img_with_outlines = img.copy()
img_with_outlines[outlines > 0] = 255
This should draw a white line around your detected object.
Note: This approach works grayscale images. For full colour images you can apply it for each channel individually.

Is there a way to tell if pyautogui is detecting the color/clicking?

import cv2
import numpy as np
import pyautogui
SCREEN_SIZE = (1920, 1080)
#define the codec
fourcc = cv2.VideoWriter_fourcc(*"XVID")
#create the video write object
out = cv2.VideoWriter("output.avi", fourcc, 20.0, (SCREEN_SIZE))
while True:
#make a screenshot
img = pyautogui.screenshot(region=(680, 785, 560, 20))
#convert these pixels to a proper numpy array to work with OpenCV
frame = np.array(img)
#convert colors from BGR to RGB
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
black = [0,0,0]
for x in range(img.width):
for y in range(img.height):
if img.getpixel((x, y)) == black:
print(x, y)
pyautogui.click(x, y)
#write the frame
out.write(frame)
#show the frame
cv2.imshow("screenshot", frame)
# if the user clicks q, it exits
if cv2.waitKey(1) == ord("q"):
break
# make sure everything is closed when exited
cv2.destroyAllWindows()
out.release()
I am creating a script to detect black squares, and click them. For some reason when using this, there is no error, but it is not clicking. Is there a way to tell whether it is detecting the color/clicking?
Edit: It does not output the coordinates, and when changing it to print "black" once finding the color, there is still no output.

How to remove shadows from a video that has static background?

I am trying to detect moving object and remove shadow from a video that has a static background. I am using Mixture of Gaussians(MOG) method to detect moving objects. I am using opencv3 and python 3.5. How can I remove shadows from the video and foreground mask both? I have used erosion and dilation for reducing noise. But it doesn't remove the shadows.
import cv2
import numpy as np
cap = cv2.VideoCapture('TownCentreXVID.avi')
fgbg = cv2.createBackgroundSubtractorMOG2()
while(1):
_, frame = cap.read()
mask = fgbg.apply(frame)
kernel = np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
closing = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
window = cv2.namedWindow('Original', cv2.WINDOW_NORMAL| cv2.WINDOW_KEEPRATIO )
window = cv2.namedWindow('Mask', cv2.WINDOW_NORMAL| cv2.WINDOW_KEEPRATIO)
window = cv2.namedWindow('Opening', cv2.WINDOW_NORMAL| cv2.WINDOW_KEEPRATIO )
#window = cv2.namedWindow('Closing', cv2.WINDOW_NORMAL| cv2.WINDOW_KEEPRATIO)
cv2.imshow('Original',frame)
cv2.imshow('Mask',thresh)
cv2.imshow('Opening',opening)
#cv2.imshow('Closing',closing)
k = cv2.waitKey(5) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
cap.release()
The backgroundsubtractor returns a mask where foreground object are white and shadows are gray.
You can use thresholding to create a new mask without shadow, or with only the shadow.
Use the mask without the shadows to get only the foreground.
Use the mask with only shadow to replace the shadow on the background (with a reference background image).
Result:
Code:
import cv2
import numpy as np
# load image / mask
mask = cv2.imread("mask.png",0)
#threshold mask
ret, foreground = cv2.threshold(mask, 200, 255, cv2.THRESH_BINARY)
ret, shadow = cv2.threshold(mask, 200, 255, cv2.THRESH_TOZERO_INV)
# stack images vertically
res = np.concatenate((mask,foreground,shadow),axis=0)
#show image
cv2.imshow("Result",res)
cv2.waitKey(0)
cv2.destroyAllWindows()

Plot mouse clicks over an image

I writing a code in Python 3 to plot some markers over a DICOM image. for this, I wrote a very short program:
In the main program, I read the DICOM filename from the terminal and plot the image.
main_prog.py:
import sys
import dicom as dcm
import numpy as np
from matplotlib import pyplot as plt
from dicomplot import dicomplot as dcmplot
filename = sys.argv[1]
dicomfile = dcm.read_file(filename)
dicomimg = dicomfile.pixel_array
fig = plt.figure(dpi = 300)
ax = fig.add_subplot(1, 1, 1)
plt.set_cmap(plt.gray())
plt.pcolormesh(np.flipud(dicomimg))
dcm = dcmplot(ax)
plt.show()
Then, I define a class to store the coordinates clicked by the user and plot each of them at a time over the image:
dicomplot.py
from matplotlib import pyplot as plt
class dicomplot():
def __init__(self, img):
self.img = img
self.fig = plt.figure(dpi = 300)
self.xcoord = list()
self.ycoord = list()
self.cid = img.figure.canvas.mpl_connect('button_press_event', self)
def __call__(self, event):
if event.button == 1:
self.xcoord.append(event.x)
self.ycoord.append(event.y)
self.img.plot(self.ycoord, self.xcoord, 'r*')
self.img.figure.canvas.draw()
elif event.button == 2:
self.img.figure.canvas.mpl_disconnect(self.cid)
elif event.button == 3:
self.xcoord.append(-1)
self.ycoord.append(-1)
The problem is that when I click over the image, the markers appear in a different scale, and not over the image as they are supposed to.
How can I modify my code so when I click on the image, all the mouse clicks are stored and ploted in the desired position?
The MouseEvent objects carry both a x/y andxdata/ydata attributes (docs). The first set is in screen coordinates (ex pixels from the lower left) and the second set (*data) are in the data coordinates.
You might also be interested in mpldatacursor.

How do I get my program to update this tkinter label in my nested loop so that it shows the grayscale process?

So I made a program that converts an image to grayscale. I first used graphics.py from a Zelle's Python Programming An Introduction To Computer Science book. I believe this is based on tkinter. I finished the program using a nested loop to iterate through a .gif picture and grab each individual pixel, replacing it with a grayscale algorithm. I had the change happen in a loop and it gave it an effect that shows the grayscale happening across the window. This was slow but it looked neat, but I realized I wanted to be able to use more file types. I found PIL for Python 3.3 and tkinter, using PIL to open the images, turning them into a tkinter PhotoImage, than displaying them in a tkinter window. Now my program will show the image before being processed and after, I would simply like to see the program update the image in the loop so that it shows the grayscale process. Any help would be great appreciated.
Here is my code:
from PIL import Image, ImageTk
from graphics import GraphWin
import tkinter
window = tkinter.Tk()
window.title('# Grayscale')
def GrayScaleConvertor():
#Opens image, creates window and draws image
picToConvert = 'sea.jpg'
pic = Image.open(picToConvert)
picWidth, picHeight = pic.size
# Treats the image as a 2d array, iterates through changing the
#values of each pixel with the algorithm for gray
tkPic = ImageTk.PhotoImage(pic, master = window)
label1 = tkinter.Label(window, image = tkPic)
rgbList = pic.load() #Get a 2d array of the pixels
for row in range(picWidth):
for column in range(picHeight):
rgb = rgbList[row,column]
r,g,b = rgb # Unpacks the RGB value tuple per pixel
grayAlgorithm1 = (r+g+b) // 3
rgbList[row,column] = (grayAlgorithm1, grayAlgorithm1, grayAlgorithm1)
# Converting to a tkinter PhotoImage
tkPic1 = ImageTk.PhotoImage(pic, master = window)
label2 = tkinter.Label(window, image = tkPic1)
# Draws the images to the window
label1.pack() # The image before grayscale
label2.pack() # The image after being grayscaled
window.mainloop()
GrayScaleConvertor()
You could use after(time, function_name) to call function (for example) every 10ms and change one (or more) pixel.
pseudocode:
tkinter.after(10, change_one_pixel)
def change_one_pixel():
change_next_pixel() # if you change more pixels you get faster animation
create_new_image()
update_label()
if any_pixel_left:
tkinter.after(10, change_one_pixel)
EDIT:
Full working code
#import Tkinter as tk # Python 2.7.x
import tkinter as tk # Python 3.x
from PIL import Image, ImageTk
class GrayScaleConvertor():
def __init__(self):
self.window = tk.Tk()
self.window.title('# Grayscale')
#Opens image, creates window and draws image
picToConvert = 'sea.jpg'
#picToConvert = 'background.jpg'
self.pic = Image.open(picToConvert)
# Treats the image as a 2d array, iterates through changing the
#values of each pixel with the algorithm for gray
self.tkPic1 = ImageTk.PhotoImage(self.pic, master = self.window)
self.label1 = tk.Label(self.window, image = self.tkPic1)
# Converting to a tkinter PhotoImage
self.tkPic2 = ImageTk.PhotoImage(self.pic, master = self.window)
self.label2 = tk.Label(self.window, image = self.tkPic2)
# Draws the images to the window
self.label1.pack() # The image before grayscale
self.label2.pack() # The image after being grayscaled
self.column = 0 # start column
self.step = 10 # number columns in one step
self.window.after(1, self.change_pixel) # 1ms
def run(self):
self.window.mainloop()
def change_pixel(self):
rgbList = self.pic.load() #Get a 2d array of the pixels
picWidth, picHeight = self.pic.size
# not to leave image
if self.column + self.step > picWidth:
self.step = picWidth - self.column
# change columns
for column in range(self.column, self.column+self.step):
for row in range(picHeight):
rgb = rgbList[column,row]
r,g,b = rgb # Unpacks the RGB value tuple per pixel
grayAlgorithm1 = (r+g+b) // 3
rgbList[column,row] = (grayAlgorithm1, grayAlgorithm1, grayAlgorithm1)
# change image in label
if self.tkPic2:
del self.tkPic2
self.tkPic2 = ImageTk.PhotoImage(self.pic, master = self.window)
self.label2.config(image = self.tkPic2)
# move start column
self.column += self.step
# if still are columns - call again
if self.column < picWidth:
print "change image"
self.window.after(1, self.change_pixel)
else:
print "the end"
GrayScaleConvertor().run()

Resources