Added abd replaced some filters, added docstrings.

master
Rostislav Lán 2 years ago
parent cc5e39e53c
commit 3b5040d6ba

@ -1,17 +1,18 @@
"""! @file filters.py
@brief Filter library for the application
@author xlanro00
@brief Filter library for the application
@author xlanro00
"""
import numpy as np
#import matplotlib.pyplot as plt
import cv2 as cv
from skimage import filters as skiflt
from skimage import restoration as skirest
# Parent class for all the filters
class filter:
'''
Parent class for all the filters
''' Parent class for all the filters.
'''
def __init__(self, img):
@ -19,8 +20,9 @@ class filter:
class convolve(filter):
''' Convolve using custom kernel,
if no kernel is given, use default 3x3 kernel for averaging
''' Convolve with custom kernel using opencv.
If no kernel is given, use default 3x3 kernel for averaging.
Possibly useful for custom filters.
'''
def __init__(self, img):
@ -31,15 +33,20 @@ class convolve(filter):
kernel = np.array(params["kernel"]) if params["kernel"] else np.ones(
(ksize, ksize), np.float32) / np.sqrt(ksize)
#print("with params: " + " ksize: " + str(ksize) + " kernel: \n" + str(kernel))
print("with params: ksize: " +
str(ksize) + " kernel: \n" + str(kernel))
self.img = cv.filter2D(self.img, -1, kernel)
class blur(filter):
''' Blur filter from OpenCV.
Performs averaging of the image.
'''
def __init__(self, img):
super().__init__(img)
def apply(self, params):
# TODO remove try-except
if(params["anchor"]):
try:
anchor = tuple(map(int, params["anchor"].split(',')))
@ -49,11 +56,14 @@ class blur(filter):
anchor = (-1, -1)
ksize = int(params["ksize"]) if params["ksize"] else 3
#print("with params: " + " ksize: " + str(ksize) + " anchor: " + str(anchor))
print("with params: ksize: " +
str(ksize) + " anchor: " + str(anchor))
self.img = cv.blur(self.img, ksize=(ksize, ksize), anchor=anchor)
class gaussian(filter):
''' Gaussian blur filter from OpenCV.
'''
def __init__(self, img):
super().__init__(img)
@ -62,22 +72,27 @@ class gaussian(filter):
sigmaX = float(params["sigmaX"]) if params["sigmaX"] else 0
sigmaY = float(params["sigmaY"]) if params["sigmaY"] else 0
#print("with params: " + " ksize: " + str(ksize) + " sigmaX: " + str(sigmaX) + " sigmaY: " + str(sigmaY))
print("with params: ksize: " + str(ksize) +
" sigmaX: " + str(sigmaX) + " sigmaY: " + str(sigmaY))
self.img = cv.GaussianBlur(self.img, (ksize, ksize), sigmaX, sigmaY)
class median(filter):
''' Median blur filter from OpenCV.
'''
def __init__(self, img):
super().__init__(img)
def apply(self, params):
ksize = int(params["ksize"]) if params["ksize"] else 3
#print("with params: " + " ksize: " + str(ksize))
self.img = cv.medianBlur(np.uint8(self.img), ksize)
print("with params: ksize: " + str(ksize))
self.img = cv.medianBlur(np.float32(self.img), ksize)
class bilateral(filter):
''' Bilateral filter from OpenCV.
'''
def __init__(self, img):
super().__init__(img)
@ -87,27 +102,87 @@ class bilateral(filter):
sigmaColor = int(params["sigmaColor"]) if params["sigmaColor"] else 75
sigmaSpace = int(params["sigmaSpace"]) if params["sigmaSpace"] else 75
#print("with params: " + " d: " + str(d) + " sigmaColor: " + str(sigmaColor) + " sigmaSpace: " + str(sigmaSpace))
print("with params: d: " + str(d) + " sigmaColor: " +
str(sigmaColor) + " sigmaSpace: " + str(sigmaSpace))
self.img = cv.bilateralFilter(self.img, d, sigmaColor, sigmaSpace)
class denoise(filter):
# TODO possibly not necessary
def __init__(self, img):
super().__init__(img)
def apply(self, params):
h = int(params["h"]) if params["h"] else 20
h = int(params["h"]) if params["h"] else 10
tWS = int(params["templateWindowSize"]
) if params["templateWindowSize"] else 7
sWS = int(params["searchWindowSize"]
) if params["searchWindowSize"] else 21
#print("with params: " + " h: " + str(h) + " tWS: " + str(tWS) + " sWS: " + str(sWS))
print("with params: h: " + str(h) +
" tWS: " + str(tWS) + " sWS: " + str(sWS))
self.img = np.uint8(self.img)
self.img = cv.fastNlMeansDenoising(self.img, h, tWS, sWS)
self.img = cv.fastNlMeansDenoising(
self.img, h, tWS, sWS)
class denoise_bilateral(filter):
''' Scikit image denoise_bilateral filter.
Performs bilateral denoising technique on the image.
Averages pixels based on their distance and color similarity.
Preserves edges while removing unwanted noise.
'''
def __init__(self, img):
super().__init__(img)
def apply(self, params):
sigmaColor = float(params["sigmaColor"]
) if params["sigmaColor"] else 0.1
sigmaSpace = float(params["sigmaSpace"]
) if params["sigmaSpace"] else 15.0
channelAxis = int(params["channelAxis"]
) if params["channelAxis"] else None
iterations = int(params["iterations"]) if params["iterations"] else 1
print("with params: sigma_color: " + str(sigmaColor) +
" sigma_spatial: " + str(sigmaSpace) + " channel_axis: " +
str(channelAxis) + " iterations: " + str(iterations))
for i in range(iterations):
self.img = skirest.denoise_bilateral(
self.img, sigma_color=sigmaColor,
sigma_spatial=sigmaSpace, channel_axis=channelAxis)
class denoise_tv_chambolle(filter):
''' Scikit image denoise_tv_chambolle filter from scikit-image.
Performs total variation denoising technique on the image.
This filter removes fine detail, but preserves edges.
'''
def __init__(self, img):
super().__init__(img)
def apply(self, params):
weight = float(params["weight"]) if params["weight"] else 0.1
channelAxis = int(params["channelAxis"]
) if params["channelAxis"] else None
iterations = int(params["iterations"]) if params["iterations"] else 1
print("with params: weight: " + str(weight) +
" channel_axis: " + str(channelAxis) + " iterations: " + str(iterations))
for i in range(iterations):
self.img = skirest.denoise_tv_chambolle(
self.img, weight=weight, channel_axis=channelAxis)
class sharpen(filter):
''' Convolution with a sharpening kernel using opencv.
'''
# TODO possibly unnecessary, because unsharp masking is working better
def __init__(self, img):
super().__init__(img)
@ -115,13 +190,13 @@ class sharpen(filter):
kernel = np.matrix(params["kernel"]) if params["kernel"] else np.array(
[[0, -1, 0], [-1, 5, -1], [0, -1, 0]])
#print("with params: " + " kernel: \n" + str(kernel))
print("with params: kernel: \n" + str(kernel))
self.img = cv.filter2D(self.img, ddepth=-1, kernel=kernel)
class unsharp_mask(filter):
''' Unsharp mask filter.
''' Unsharp mask filter from opencv.
First blur the image a little bit, then calculate Laplacian of the image to get the edges.
Scale the Laplacian and subtract it from the original image.
'''
@ -132,13 +207,43 @@ class unsharp_mask(filter):
def apply(self, params):
strength = float(params["strength"]) if params["strength"] else 1.0
ksize = int(params["ksize"]) if params["ksize"] else 3
blurred = cv.medianBlur(np.uint8(self.img), ksize)
blurred = cv.medianBlur(self.img, ksize)
lap = cv.Laplacian(blurred, cv.CV_32F)
print("with params: strength: " +
str(strength) + " ksize: " + str(ksize))
self.img = blurred - strength*lap
class unsharp_mask_scikit(filter):
''' Unsharp mask filter from scikit.
Apply blurring using gaussian filter, then subtract the blurred image from the original image.
Radius parameter is the sigma parameter of the gaussian filter.
Amount parameter regulates the strength of the unsharp mask.
Better results than using opencv module.
'''
def __init__(self, img):
super().__init__(img)
def apply(self, params):
radius = int(params["radius"]) if params["radius"] else 3
amount = float(params["amount"]) if params["amount"] else 1
channelAxis = int(params["channelAxis"]
) if params["channelAxis"] else None
#self.img = cv.cvtColor(self.img, cv.COLOR_GRAY2RGB)
print("with params: radius: " +
str(radius) + " amount: " + str(amount))
self.img = skiflt.unsharp_mask(
self.img, radius=radius, amount=amount, channel_axis=channelAxis)
#self.img = cv.cvtColor(self.img, cv.COLOR_RGB2GRAY)
class morph(filter):
''' General morphological operations.
''' General morphological operations from OpenCV.
Can be used with MORPH_OPEN, MORPH_CLOSE, MORPH_DILATE, MORPH_ERODE and more as 'op'.
'''
@ -152,6 +257,7 @@ class morph(filter):
iterations = int(params["iterations"]) if params["iterations"] else 1
op = getattr(cv, params["op"]) if params["op"] else cv.MORPH_OPEN
if(params["anchor"]):
# TODO remove try-except
try:
anchor = tuple(map(int, params["anchor"].split(',')))
except AttributeError:
@ -159,6 +265,8 @@ class morph(filter):
else:
anchor = (-1, -1)
#print("with params: " + " kernel: \n" + str(kernel) + " anchor: " + str(anchor) + " iterations: " + str(iterations) + " op: " + str(op))
print("with params: kernel: \n" + str(kernel) + " anchor: " +
str(anchor) + " iterations: " + str(iterations) + " op: " + str(op))
self.img = cv.morphologyEx(
self.img, op=op, kernel=kernel, anchor=anchor, iterations=iterations)
np.uint8(self.img), op=op, kernel=kernel,
anchor=anchor, iterations=iterations)

@ -1,13 +1,12 @@
"""! @file main.py
@brief Main file for the application
@author xlanro00
@brief Main file for the application
@author xlanro00
"""
# Import basic libraries
import argparse as ap
import sys
import json
#from datetime import datetime
# Libraries for image processing
import numpy as np
@ -33,7 +32,6 @@ class apply_filters:
self.config_file = self.args.config[0]
self.preset_name = self.args.config[1]
self.config = json.load(open(self.config_file))
print("Config loaded")
self.parse_conf()
# If no config file given, expect filters in command line
@ -63,11 +61,12 @@ class apply_filters:
def run(self):
# read as numpy.array
self.img = cv.imread(self.input_file, cv.IMREAD_GRAYSCALE)
self.img = cv.imread(
self.input_file, cv.IMREAD_GRAYSCALE).astype(np.uint8)
self.width = self.img.shape[1]
self.height = self.img.shape[0]
print(self.width, self.height)
self.print_size(self.img.shape)
fig = plt.figure(figsize=(self.width, self.height),
frameon=False, dpi=self.dpi / 100) # dpi is in cm
@ -86,21 +85,27 @@ class apply_filters:
if self.args.stl:
self.make_lithophane()
def parse_params(self, params):
''' Parse parameters of filters.
Set to None if not given.
They are later set in the filter method.
'''
possible_params = {"h", "searchWindowSize", "templateWindowSize",
"ksize", "kernel", "sigmaX", "sigmaY",
"sigmaColor", "sigmaSpace", "d", "anchor", "iterations",
"op", "strength"}
"sigmaColor", "sigmaSpace", "d", "anchor", "iterations",
"op", "strength", "amount", "radius", "weight", "channelAxis"}
for key in possible_params:
try:
params[key] = params[key]
except KeyError:
if params.get(key) is None:
params[key] = None
else:
params[key] = params[key]
def parse_conf(self):
# Parse configuration file if given.
try:
''' Parse configuration file if one was given and store filters with their parameters
'''
if self.preset_name in self.config:
filter_array = self.config[self.preset_name]
for i, filter in enumerate(range(len(filter_array)), start=1):
self.filters.append(filter_array[filter]["name"])
@ -109,13 +114,15 @@ class apply_filters:
if attribute != "name":
self.params[i][attribute] = value
self.parse_params(self.params[i])
except(KeyError):
print("Loaded preset: " + self.preset_name +
" from file: " + self.config_file)
else:
print("Preset not found", file=sys.stderr)
def parse_arguments(self):
''' Parse arguments from command line
'''
# Parse arguments
parser = ap.ArgumentParser(prog='main.py',
description='Program for processing a 2D image into 3D fingerprint.',
usage='%(prog)s [-h] [-m | --mirror | --no-mirror] input_file output_file dpi ([-c config_file preset | --config config_file preset] | [filters ...])')
@ -127,10 +134,11 @@ class apply_filters:
help="output file location")
parser.add_argument("dpi", type=int, help="scanner dpi")
# boolean switch
# boolean switch argument
parser.add_argument('-m', "--mirror", help="mirror input image",
type=bool, action=ap.BooleanOptionalAction)
# another boolean switch argument
parser.add_argument('-s', '--stl', help="make stl model from processed image",
type=bool, action=ap.BooleanOptionalAction)
@ -149,7 +157,7 @@ class apply_filters:
''' Selects filter method of filters library.
'''
print("Applying " + filter_name + " filter", file=sys.stderr)
print("Applying " + filter_name + " filter ", end='')
return getattr(flt, filter_name)
def resize_image(self):
@ -163,16 +171,16 @@ class apply_filters:
should be used only if we want a positive model
'''
#TODO make this automatic for positive STL
# TODO make this automatic for positive STL
print("Mirroring image", file=sys.stderr)
self.img = cv.flip(self.img, 1) # 1 for vertical mirror
def apply_filter(self):
''' Apply filters to image.
Applies the filters one by one, if no filters were given, just save original image output.
'''
if len(self.filters) == 0:
# No filter given, just save the image
pass
@ -180,11 +188,13 @@ class apply_filters:
# Apply all filters
for i, filter_name in enumerate(self.filters):
filter = self.filter_factory(filter_name)
# print(self.img.dtype)
filter.apply(self, self.params[i+1])
# print(self.img.dtype)
def print_size(self, size):
print("Width: " + str(size[0]), file=sys.stderr)
print("Height: " + str(size[1]), file=sys.stderr)
print("Height: " + str(size[0]), file=sys.stderr)
print("Width: " + str(size[1]), file=sys.stderr)
def save_image(self, fig, ax):
''' Save processed image.
@ -196,7 +206,6 @@ class apply_filters:
fig.savefig(fname=self.output_file)
def make_lithophane(self):
pass
'''After processing image, make a lithophane from it.
'''
@ -208,15 +217,21 @@ class apply_filters:
self.save_model()
def make_meshgrid(self):
''' Create numpy meshgrid.
Modify image values to get more usable depth values.
Add zero padding to image to make sides of the plate.
'''
# Modify image to make it more suitable depth
# values1 = (1 + (1 - self.img/255)/6) * 255/10 # this works
# values2 = (1 - (1 - self.img/255)/6) * 255/10 # TODO: i dont know how to make white surrounding be extruded
# values2 = (1 - (1 - self.img/255)/6) * 255/10 #
# TODO: i dont know how to make white surrounding be extruded
values1better = 28.05 - 0.01*self.img
#values2better = 22.95 - 0.01*self.img
# (np.around(values2[::300],3))
# Add zero padding to image to make sides of the plate
# Add zero padding to image
# TODO this better be done in the next function to keep dimensions intact
self.height = self.img.shape[0] + 2
self.width = self.img.shape[1] + 2
self.img = np.zeros([self.height, self.width])
@ -228,6 +243,11 @@ class apply_filters:
self.meshgrid = np.meshgrid(verticesX, verticesY)
def make_mesh(self):
''' Create mesh from image.
Create vertices from meshgrid, add depth values from image.
Create faces from vertices.
'''
# Convert meshgrid and image matrix to array of 3D points
vertice_arr = np.vstack(list(map(np.ravel, self.meshgrid))).T
z = (self.img / 10).reshape(-1, 1)
@ -278,7 +298,10 @@ class apply_filters:
self.model.vectors[i][j] = vertices[face[j], :]
def save_model(self):
print("Saving stl model", file=sys.stderr)
''' Save final model to stl file.
'''
print("Saving lithophane to stl file", file=sys.stderr)
self.model.save('res/test.stl')

Loading…
Cancel
Save