From 9cef78b685c5f82462edc9c98805437d8258afd9 Mon Sep 17 00:00:00 2001 From: Pedro Ferreira da Costa Date: Thu, 21 Mar 2019 19:53:12 +0000 Subject: [PATCH] Merge pull request #14109 from PedroFerreiradaCosta:adding_python_version_to_anisotropic_tutorial * Created python version of the code for the anisotropic image segmentation tutorial. Created python/cpp toggles for the markdown file. * fix doxygen warnings --- .../anisotropic_image_segmentation.markdown | 49 ++++++++-- .../anisotropic_image_segmentation.py | 92 +++++++++++++++++++ 2 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 samples/python/tutorial_code/imgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.py diff --git a/doc/tutorials/imgproc/anisotropic_image_segmentation/anisotropic_image_segmentation.markdown b/doc/tutorials/imgproc/anisotropic_image_segmentation/anisotropic_image_segmentation.markdown index c05cb63548..489cd6bf3a 100755 --- a/doc/tutorials/imgproc/anisotropic_image_segmentation/anisotropic_image_segmentation.markdown +++ b/doc/tutorials/imgproc/anisotropic_image_segmentation/anisotropic_image_segmentation.markdown @@ -48,28 +48,65 @@ The orientation of an anisotropic image: Coherency: \f[C = \frac{\lambda_1 - \lambda_2}{\lambda_1 + \lambda_2}\f] -The coherency ranges from 0 to 1. For ideal local orientation (\f$\lambda_2\f$ = 0, \f$\lambda_1\f$ > 0) it is one, for an isotropic gray value structure (\f$\lambda_1\f$ = \f$\lambda_2\f$ > 0) it is zero. +The coherency ranges from 0 to 1. For ideal local orientation (\f$\lambda_2\f$ = 0, \f$\lambda_1\f$ > 0) it is one, for an isotropic gray value structure (\f$\lambda_1\f$ = \f$\lambda_2\f$ \> 0) it is zero. Source code ----------- You can find source code in the `samples/cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp` of the OpenCV source code library. -@include cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp +@add_toggle_cpp + @include cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp +@end_toggle + +@add_toggle_python + @include samples/python/tutorial_code/imgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.py +@end_toggle Explanation ----------- An anisotropic image segmentation algorithm consists of a gradient structure tensor calculation, an orientation calculation, a coherency calculation and an orientation and coherency thresholding: -@snippet samples/cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp main + +@add_toggle_cpp + @snippet samples/cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp main +@end_toggle + +@add_toggle_python + @snippet samples/python/tutorial_code/imgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.py main +@end_toggle A function calcGST() calculates orientation and coherency by using a gradient structure tensor. An input parameter w defines a window size: -@snippet samples/cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp calcGST + +@add_toggle_cpp + @snippet samples/cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp calcGST +@end_toggle + +@add_toggle_python + @snippet samples/python/tutorial_code/imgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.py calcGST +@end_toggle + The below code applies a thresholds LowThr and HighThr to image orientation and a threshold C_Thr to image coherency calculated by the previous function. LowThr and HighThr define orientation range: -@snippet samples/cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp thresholding + +@add_toggle_cpp + @snippet samples/cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp thresholding +@end_toggle + +@add_toggle_python + @snippet samples/python/tutorial_code/imgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.py thresholding +@end_toggle + And finally we combine thresholding results: -@snippet samples/cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp combining + +@add_toggle_cpp + @snippet samples/cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp combining +@end_toggle + +@add_toggle_python + @snippet samples/python/tutorial_code/imgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.py combining +@end_toggle + Result ------ diff --git a/samples/python/tutorial_code/imgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.py b/samples/python/tutorial_code/imgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.py new file mode 100644 index 0000000000..5e2385a1ce --- /dev/null +++ b/samples/python/tutorial_code/imgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.py @@ -0,0 +1,92 @@ + +import cv2 as cv +import numpy as np +import argparse + +W = 52 # window size is WxW +C_Thr = 0.43 # threshold for coherency +LowThr = 35 # threshold1 for orientation, it ranges from 0 to 180 +HighThr = 57 # threshold2 for orientation, it ranges from 0 to 180 + +## [calcGST] +## [calcJ_header] +## [calcGST_proto] +def calcGST(inputIMG, w): +## [calcGST_proto] + img = inputIMG.astype(np.float32) + + # GST components calculation (start) + # J = (J11 J12; J12 J22) - GST + imgDiffX = cv.Sobel(img, cv.CV_32F, 1, 0, 3) + imgDiffY = cv.Sobel(img, cv.CV_32F, 0, 1, 3) + imgDiffXY = cv.multiply(imgDiffX, imgDiffY) + ## [calcJ_header] + + imgDiffXX = cv.multiply(imgDiffX, imgDiffX) + imgDiffYY = cv.multiply(imgDiffY, imgDiffY) + + J11 = cv.boxFilter(imgDiffXX, cv.CV_32F, (w,w)) + J22 = cv.boxFilter(imgDiffYY, cv.CV_32F, (w,w)) + J12 = cv.boxFilter(imgDiffXY, cv.CV_32F, (w,w)) + # GST components calculations (stop) + + # eigenvalue calculation (start) + # lambda1 = J11 + J22 + sqrt((J11-J22)^2 + 4*J12^2) + # lambda2 = J11 + J22 - sqrt((J11-J22)^2 + 4*J12^2) + tmp1 = J11 + J22 + tmp2 = J11 - J22 + tmp2 = cv.multiply(tmp2, tmp2) + tmp3 = cv.multiply(J12, J12) + tmp4 = np.sqrt(tmp2 + 4.0 * tmp3) + + lambda1 = tmp1 + tmp4 # biggest eigenvalue + lambda2 = tmp1 - tmp4 # smallest eigenvalue + # eigenvalue calculation (stop) + + # Coherency calculation (start) + # Coherency = (lambda1 - lambda2)/(lambda1 + lambda2)) - measure of anisotropism + # Coherency is anisotropy degree (consistency of local orientation) + imgCoherencyOut = cv.divide(lambda1 - lambda2, lambda1 + lambda2) + # Coherency calculation (stop) + + # orientation angle calculation (start) + # tan(2*Alpha) = 2*J12/(J22 - J11) + # Alpha = 0.5 atan2(2*J12/(J22 - J11)) + imgOrientationOut = cv.phase(J22 - J11, 2.0 * J12, angleInDegrees = True) + imgOrientationOut = 0.5 * imgOrientationOut + # orientation angle calculation (stop) + + return imgCoherencyOut, imgOrientationOut +## [calcGST] + +parser = argparse.ArgumentParser(description='Code for Anisotropic image segmentation tutorial.') +parser.add_argument('-i', '--input', help='Path to input image.', required=True) +args = parser.parse_args() + +imgIn = cv.imread(args.input, cv.IMREAD_GRAYSCALE) +if imgIn is None: + print('Could not open or find the image: {}'.format(args.input)) + exit(0) + +## [main_extra] +## [main] +imgCoherency, imgOrientation = calcGST(imgIn, W) + +## [thresholding] +_, imgCoherencyBin = cv.threshold(imgCoherency, C_Thr, 255, cv.THRESH_BINARY) +_, imgOrientationBin = cv.threshold(imgOrientation, LowThr, HighThr, cv.THRESH_BINARY) +## [thresholding] + +## [combining] +imgBin = cv.bitwise_and(imgCoherencyBin, imgOrientationBin) +## [combining] +## [main] + +imgCoherency = cv.normalize(imgCoherency, None, alpha=0, beta=1, norm_type=cv.NORM_MINMAX, dtype=cv.CV_32F) +imgOrientation = cv.normalize(imgOrientation, None, alpha=0, beta=1, norm_type=cv.NORM_MINMAX, dtype=cv.CV_32F) + +cv.imshow('result.jpg', np.uint8(0.5*(imgIn + imgBin))) +cv.imshow('Coherency.jpg', imgCoherency) +cv.imshow('Orientation.jpg', imgOrientation) +cv.waitKey(0) +## [main_extra]