Created
January 9, 2023 01:57
-
-
Save rhee-elten/541852e0743d6daa4caf0a7f250d1dcf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cv2 | |
import pandas as pd | |
import numpy as np | |
""" | |
HoughCircles(image, method, dp, minDist[, circles[, param1[, param2[, minRadius[, maxRadius]]]]]) -> circles | |
. @brief Finds circles in a grayscale image using the Hough transform. | |
. | |
. The function finds circles in a grayscale image using a modification of the Hough transform. | |
. | |
. @note Usually the function detects the centers of circles well. However, it may fail to find correct | |
. radii. You can assist to the function by specifying the radius range ( minRadius and maxRadius ) if | |
. you know it. Or, you may set maxRadius to a negative number to return centers only without radius | |
. search, and find the correct radius using an additional procedure. | |
. | |
. @param image 8-bit, single-channel, grayscale input image. | |
. @param method Detection method, see #HoughModes. Currently, the only implemented method is #HOUGH_GRADIENT | |
. @param dp Inverse ratio of the accumulator resolution to the image resolution. For example, if | |
. dp=1 , the accumulator has the same resolution as the input image. If dp=2 , the accumulator has | |
. half as big width and height. | |
. @param minDist Minimum distance between the centers of the detected circles. If the parameter is | |
. too small, multiple neighbor circles may be falsely detected in addition to a true one. If it is | |
. too large, some circles may be missed. | |
. @param param1 First method-specific parameter. In case of #HOUGH_GRADIENT , it is the higher | |
. threshold of the two passed to the Canny edge detector (the lower one is twice smaller). | |
. @param param2 Second method-specific parameter. In case of #HOUGH_GRADIENT , it is the | |
. accumulator threshold for the circle centers at the detection stage. The smaller it is, the more | |
. false circles may be detected. Circles, corresponding to the larger accumulator values, will be | |
. returned first. | |
. @param minRadius Minimum circle radius. | |
. @param maxRadius Maximum circle radius. If <= 0, uses the maximum image dimension. If < 0, returns | |
. centers without finding the radius. | |
linearPolar(src, center, maxRadius, flags[, dst]) -> dst | |
. @brief Remaps an image to polar coordinates space. | |
. | |
. @deprecated This function produces same result as cv::warpPolar(src, dst, src.size(), center, maxRadius, flags) | |
. | |
. @param src Source image | |
. @param dst Destination image. It will have same size and type as src. | |
. @param center The transformation center; | |
. @param maxRadius The radius of the bounding circle to transform. It determines the inverse magnitude scale parameter too. | |
. @param flags A combination of interpolation methods, see #InterpolationFlags | |
""" | |
def auto_polar_to_linear(im, | |
dsize=None, | |
thumb_scale=4, # 원본: 2448x2048 ==> 612x512 | |
blur_ksize=5, | |
hc_dp=1, | |
hc_min_dist_fac=0.2, | |
hc_param1=250, | |
hc_param2=30, | |
hc_minradius_fac=0.6, | |
hc_maxradius_fac=0.99, | |
cb_fac=0.15, | |
_debug=None): | |
if isinstance(_debug, dict): | |
_debug.clear() | |
### 빠른 속도록 HoughCircles 를 구하기 위해서 흑백이미지를 원본 크기의 1/4 로 줄인 썸네일 생성 | |
thumb = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY) | |
thumb = cv2.resize(thumb, None, fx=1.0/thumb_scale, | |
fy=1.0/thumb_scale) | |
### 노이즈 억제를 위해서 medianBlur 적용 | |
thumb = cv2.medianBlur(thumb, blur_ksize) | |
### hc_min_dist_fac, hc_minradius_fac, hc_maxradius_fac 파라메터의 설정값은 | |
### 아래에서 구하는 thumb_radius 값에 대한 상대값으로 지정되어 있음 | |
thumb_radius = min(*thumb.shape[:2]) / 2 | |
hc_min_dist = int(thumb_radius * hc_min_dist_fac) | |
hc_minradius = int(thumb_radius * hc_minradius_fac) | |
hc_maxradius = int(thumb_radius * hc_maxradius_fac) | |
### HoughCircle 계산 | |
circles = cv2.HoughCircles(thumb, cv2.HOUGH_GRADIENT, hc_dp, hc_min_dist, | |
param1=hc_param1, param2=hc_param2, | |
minRadius=hc_minradius, maxRadius=hc_maxradius) | |
assert circles.shape[0] == 1, ('invalid detection:', circles) | |
if isinstance(_debug, dict): | |
### HoughCircle 결과 확인 함수 | |
def debug_preview(thickness=5, | |
cb_color=(0, 222, 0), | |
center_color=(222, 222, 0), | |
circle_color=(222, 0, 0)): | |
canvas = cv2.cvtColor(thumb, cv2.COLOR_GRAY2RGB) | |
hh, ww = canvas.shape[:2] | |
tx_min, tx_max = int(ww * (0.5 - cb_fac)), int(ww * (0.5 + cb_fac)) | |
ty_min, ty_max = int(hh * (0.5 - cb_fac)), int(hh * (0.5 + cb_fac)) | |
cv2.rectangle(canvas, (tx_min, ty_min), | |
(tx_max, ty_max), cb_color, thickness) | |
for cc in np.int0(circles[0]): | |
center = tuple(cc[:2]) | |
radius = cc[2] | |
cv2.circle(canvas, center, thickness, center_color, thickness) | |
cv2.circle(canvas, center, radius, circle_color, thickness) | |
return canvas | |
#_debug['debug_preview'] = debug_preview | |
_debug.update(locals()) | |
### circles 결과값을 원본 이미지 크기로 변환 | |
cands = circles[0] * thumb_scale | |
#print('cands:', len(cands), cands[:5]) | |
### 탐지된 Circle 이 적절한지 확인하기 위해 중심점의 범위를 설정 | |
### 중심점이 설정된 범위 내부가 아니면 잘못 탐지한 것으로 간주함. | |
### 영상 취득시, 실린더의 중심이 이미지의 가운데 근처에 위치하도록 | |
### 잘 촬영되어 있다고 간주함. | |
hh, ww = im.shape[:2] | |
cx_min, cx_max = int(ww * (0.5 - cb_fac)), int(ww * (0.5 + cb_fac)) | |
cy_min, cy_max = int(hh * (0.5 - cb_fac)), int(hh * (0.5 + cb_fac)) | |
### 중심점을 벗어나지 않은 circle 을 검색 | |
for c in cands: | |
cx, cy = c[:2] | |
if (cx_min <= cx <= cx_max and cy_min <= cy <= cy_max): | |
break | |
else: | |
return None, None, None | |
### 중심점에서 가장 가까운 경계까지의 거리가 max_radius 가 됨 | |
### 중심점에서 max_radius 보다 멀리 떨어진 점은 최종 변환 이미지에 포함되지 않음 | |
max_radius = min(cx, cy, abs(hh-cy), abs(ww-cx)) | |
### linearPolar 변환 적용 (deprecated) | |
# warped = cv2.linearPolar(im, (cx, cy), max_radius, cv2.INTER_LINEAR) | |
### warpPolar 적용 | |
""". @param flags A combination of interpolation methods, #InterpolationFlags + #WarpPolarMode. | |
. - Add #WARP_POLAR_LINEAR to select linear polar mapping (default) | |
. - Add #WARP_POLAR_LOG to select semilog polar mapping | |
. - Add #WARP_INVERSE_MAP for reverse mapping. | |
. @note | |
. - The function can not operate in-place. | |
. - To calculate magnitude and angle in degrees #cartToPolar is used internally thus angles are measured from 0 to 360 with accuracy about 0.3 degrees. | |
. - This function uses #remap. Due to current implementation limitations the size of an input and output images should be less than 32767x32767. | |
""" | |
if dsize is None: | |
#dsize = (int(max_radius), int(2 * np.pi * max_radius)) | |
#dsize = (int(max_radius), 1080) # 360 * 1/0.3 | |
dsize = im.shape[1], im.shape[0] | |
warped = cv2.warpPolar(im, dsize, (cx, cy), max_radius, cv2.WARP_POLAR_LINEAR) | |
return warped, (cx, cy), max_radius | |
def transform_points(pts, dsize, center, max_radius): | |
""" | |
pts = list of (x, y) | |
dsize = (height, width) | |
center = (cx, cy) | |
""" | |
w, h = dsize | |
Kmag = float(w)/max_radius | |
Kangle = float(h)/(2 * np.pi) | |
pts = np.asarray(pts, dtype=float) | |
I = pts - center | |
x, y = I.transpose() | |
mag, angle = cv2.cartToPolar(x, y) | |
mag = mag.squeeze(axis=1) | |
angle = angle.squeeze(axis=1) | |
rho = (Kmag * mag).astype(int) | |
phi = (Kangle * angle).astype(int) | |
phi = (phi + h) % h | |
tr_pts = np.transpose([rho, phi]) | |
return tr_pts | |
def inverse_transform_points(tr_pts, dsize, center, max_radius): | |
w, h = dsize | |
Kmag = float(w)/max_radius | |
Kangle = float(h)/(2 * np.pi) | |
tr_pts = np.asarray(tr_pts, dtype=float) | |
rho, phi = tr_pts.transpose() | |
mag = rho / Kmag | |
angle = phi / Kangle | |
ux, uy = cv2.polarToCart(mag, angle) | |
pts = np.squeeze([ux, uy], axis=2).transpose().astype(int) + center | |
return pts | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment