105 lines
2.2 KiB
Python
105 lines
2.2 KiB
Python
import cv2
|
|
import numpy as np
|
|
|
|
# ========================
|
|
# 配置
|
|
# ========================
|
|
IMAGE_PATH = "22.png"
|
|
|
|
CANNY_LOW = 60
|
|
CANNY_HIGH = 150
|
|
|
|
# 判定“近似水平”的阈值
|
|
MAX_SLOPE = 0.2 # |dy/dx| < 0.2 认为是水平
|
|
|
|
# ========================
|
|
|
|
|
|
def main():
|
|
img = cv2.imread(IMAGE_PATH)
|
|
if img is None:
|
|
raise RuntimeError("图片读取失败")
|
|
|
|
h, w = img.shape[:2]
|
|
|
|
# 1. 灰度
|
|
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
|
|
|
# 2. 模糊(非常关键,压制粗糙纹理)
|
|
blur = cv2.GaussianBlur(gray, (7, 7), 0)
|
|
|
|
# 3. Canny
|
|
edges = cv2.Canny(blur, CANNY_LOW, CANNY_HIGH)
|
|
|
|
# 4. 找轮廓(只用来拿点)
|
|
contours, _ = cv2.findContours(
|
|
edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE
|
|
)
|
|
|
|
horizontal_points = []
|
|
|
|
# 5. 筛选“近似水平”的边缘点
|
|
for cnt in contours:
|
|
if len(cnt) < 50:
|
|
continue
|
|
|
|
pts = cnt.squeeze()
|
|
if pts.ndim != 2:
|
|
continue
|
|
|
|
x = pts[:, 0]
|
|
y = pts[:, 1]
|
|
|
|
dx = x.max() - x.min()
|
|
dy = y.max() - y.min()
|
|
|
|
if dx < 100: # 太短的不要
|
|
continue
|
|
|
|
slope = dy / (dx + 1e-6)
|
|
|
|
if slope < MAX_SLOPE:
|
|
horizontal_points.append(pts)
|
|
|
|
if not horizontal_points:
|
|
print("❌ 没找到合适的水平边")
|
|
return
|
|
|
|
# 6. 合并所有候选点
|
|
all_pts = np.vstack(horizontal_points)
|
|
|
|
# 7. 选“最靠上的那一条”
|
|
# 方法:按 y 排序,取 y 最小的一部分
|
|
all_pts = all_pts[all_pts[:, 1].argsort()]
|
|
|
|
# 取前 20% 作为“上沿候选”
|
|
top_n = int(len(all_pts) * 0.2)
|
|
top_edge_pts = all_pts[:top_n]
|
|
|
|
# 8. 用最小二乘拟合直线 y = ax + b
|
|
xs = top_edge_pts[:, 0]
|
|
ys = top_edge_pts[:, 1]
|
|
|
|
a, b = np.polyfit(xs, ys, 1)
|
|
|
|
# 9. 画线
|
|
x0, x1 = 0, w - 1
|
|
y0 = int(a * x0 + b)
|
|
y1 = int(a * x1 + b)
|
|
|
|
vis = img.copy()
|
|
cv2.line(vis, (x0, y0), (x1, y1), (0, 0, 255), 2)
|
|
|
|
# 可视化点
|
|
for p in top_edge_pts[::20]:
|
|
cv2.circle(vis, tuple(p), 1, (0, 255, 0), -1)
|
|
|
|
cv2.imshow("edges", edges)
|
|
cv2.imshow("top plate edge", vis)
|
|
cv2.waitKey(0)
|
|
cv2.destroyAllWindows()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|