Files
琉璃月光 67883f1a50 最新推送
2026-03-10 14:14:14 +08:00

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()