AI學習筆記三十三:基于Opencv的單目標跟蹤

若該文為原創文章,轉載請注明原文出處。

一、功能介紹

主要是想實現跟蹤視頻中的一個特定目標。

使用了OpenCV庫來實現視頻中特定目標的跟蹤。需要提供視頻文件路徑以及目標在第一幀中的位置坐標(x, y, width, height),程序會自動跟蹤該目標在整個視頻中的移動。

二、環境搭建

pip install opencv-contrib-python==3.4.13.47 -i https://pypi.tuna.tsinghua.edu.cn/simple

其他版本自行測試。

三、工作原理

1. 初始化階段

  • 程序首先讀取視頻的第一幀,獲取圖像尺寸
  • 根據用戶提供的坐標初始化目標區域(ROI - Region of Interest)
  • 保存初始目標區域作為模板用于后續重新檢測
  • 嘗試創建并初始化跟蹤器(優先級順序:CSRT、KCF、MOSSE)

2. 主跟蹤循環

  • 逐幀讀取視頻
  • 使用跟蹤器更新目標位置
  • 如果跟蹤成功:
    • 在當前幀上繪制目標邊界框
    • 更新位置歷史信息
    • 定期更新模板庫(用于重新檢測)
    • 重置失敗計數器
  • 如果跟蹤失敗:
    • 啟動重新檢測機制

3. 重新檢測機制

當跟蹤失敗時,程序采用多種策略來重新找到目標:

多層次搜索策略:
  1. 局部搜索:在預測位置附近區域搜索
  2. 擴展搜索:逐漸擴大搜索區域
  3. 全圖搜索:在整個圖像中搜索
多種檢測方法:
  1. 模板匹配:使用保存的模板與當前圖像進行匹配
  2. ORB特征匹配:使用ORB特征檢測和匹配算法
  3. 全圖特征匹配:在整個圖像上進行特征匹配
動態調整機制:
  • 根據失敗次數動態調整搜索區域大小
  • 使用歷史位置預測目標可能位置
  • 循環使用不同檢測方法

4. 模板管理

程序維護一個模板庫:

  • 保存多個不同時刻的目標圖像作為模板
  • 定期更新模板以適應目標外觀變化
  • 限制模板數量防止內存過度使用

5. 運動預測

  • 記錄目標的歷史位置
  • 計算平均運動向量預測下一位置
  • 根據預測位置調整搜索區域中心

6. 失敗處理

  • 設置最大失敗次數限制
  • 多種重新檢測策略輪換使用
  • 如果重新檢測成功則重新初始化跟蹤器

四、源碼

import cv2
import sys
import os
import numpy as np
import time
import mathdef safe_roi(roi, img_width, img_height):"""確保ROI在圖像范圍內"""x, y, w, h = roix = max(0, x)y = max(0, y)w = min(w, img_width - x)h = min(h, img_height - y)w = max(0, w)h = max(0, h)return (x, y, w, h)def adaptive_template_match(search_area, templates, scales=[0.4, 0.6, 0.8, 1.0, 1.2, 1.5, 1.8, 2.0]):"""自適應模板匹配,支持多模板和多尺度"""best_match = Nonebest_val = -1best_scale = 1.0best_template_idx = 0for scale in scales:# 縮放搜索區域if scale != 1.0:scaled_search = cv2.resize(search_area, None, fx=scale, fy=scale)else:scaled_search = search_areafor idx, template in enumerate(templates):# 確保模板小于搜索區域if template.shape[0] > scaled_search.shape[0] or template.shape[1] > scaled_search.shape[1]:continuetry:# 模板匹配res = cv2.matchTemplate(scaled_search, template, cv2.TM_CCOEFF_NORMED)min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)# 更新最佳匹配if max_val > best_val:best_val = max_valbest_loc = max_locbest_scale = scalebest_template_idx = idxexcept:continuereturn best_val, best_loc, best_scale, best_template_idxdef validate_detection(frame, candidate_roi, templates, min_similarity=0.3):"""驗證檢測結果是否有效"""x, y, w, h = candidate_roi# 確保ROI有效if w <= 5 or h <= 5:return False# 提取候選區域candidate_img = frame[y:y+h, x:x+w]if candidate_img.size == 0 or candidate_img.shape[0] == 0 or candidate_img.shape[1] == 0:return False# 與所有模板比較相似度max_similarity = 0for template in templates:try:# 調整模板大小以匹配候選區域resized_template = cv2.resize(template, (w, h))# 計算直方圖相似度hist1 = cv2.calcHist([candidate_img], [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256])hist2 = cv2.calcHist([resized_template], [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256])cv2.normalize(hist1, hist1)cv2.normalize(hist2, hist2)similarity = cv2.compareHist(hist1, hist2, cv2.HISTCMP_CORREL)if similarity > max_similarity:max_similarity = similarityif similarity > min_similarity:return Trueexcept:continueprint(f"直方圖驗證失敗: 最大相似度={max_similarity:.2f}")return Falsedef contour_similarity(img1, template):"""通過輪廓比較圖像相似度"""try:# 預處理圖像if len(img1.shape) == 3:gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)else:gray1 = img1.copy()if len(template.shape) == 3:gray2 = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)else:gray2 = template.copy()# 二值化_, thresh1 = cv2.threshold(gray1, 127, 255, cv2.THRESH_BINARY)_, thresh2 = cv2.threshold(gray2, 127, 255, cv2.THRESH_BINARY)# 查找輪廓contours1, _ = cv2.findContours(thresh1, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)contours2, _ = cv2.findContours(thresh2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)if not contours1 or not contours2:return 0.0# 取最大輪廓cnt1 = max(contours1, key=cv2.contourArea)cnt2 = max(contours2, key=cv2.contourArea)# 計算相似度 (值越小越相似)similarity = cv2.matchShapes(cnt1, cnt2, cv2.CONTOURS_MATCH_I2, 0.0)# 轉換為相似度分數 (1-相似度,值越大越相似)return 1.0 - min(similarity, 1.0)except Exception as e:print(f"輪廓相似度計算錯誤: {e}")return 0.0def template_match_score(frame, roi, templates):"""評估候選區域的模板匹配分數"""x, y, w, h = roiif w <= 5 or h <= 5:return 0.0patch = frame[y:y+h, x:x+w]if patch.size == 0:return 0.0best_score = 0.0for template in templates:try:# 調整模板大小resized_tpl = cv2.resize(template, (w, h))# 計算匹配分數result = cv2.matchTemplate(patch, resized_tpl, cv2.TM_CCOEFF_NORMED)_, max_val, _, _ = cv2.minMaxLoc(result)if max_val > best_score:best_score = max_valexcept:continuereturn best_scoredef detect_with_orb(frame, templates, search_area_roi=None):"""使用ORB特征匹配檢測目標"""if search_area_roi:x1, y1, x2, y2 = search_area_roisearch_area = frame[y1:y2, x1:x2]else:search_area = frameif search_area.size == 0:return None# 初始化ORB檢測器orb = cv2.ORB_create(nfeatures=2000)# 檢測搜索區域的關鍵點和描述符kp_search, des_search = orb.detectAndCompute(search_area, None)if des_search is None or len(kp_search) < 10:return Nonebest_match = Nonebest_matches = 0for template in templates:# 檢測模板的關鍵點和描述符kp_template, des_template = orb.detectAndCompute(template, None)if des_template is None or len(kp_template) < 5:continue# 創建BFMatcher對象bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)try:# 匹配描述符matches = bf.match(des_template, des_search)matches = sorted(matches, key=lambda x: x.distance)# 選擇最佳匹配if len(matches) > 10:# 獲取匹配點坐標src_pts = np.float32([kp_template[m.queryIdx].pt for m in matches]).reshape(-1, 1, 2)dst_pts = np.float32([kp_search[m.trainIdx].pt for m in matches]).reshape(-1, 1, 2)# 計算單應性矩陣M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)if M is not None:# 獲取模板的角點h, w = template.shape[:2]pts = np.float32([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]).reshape(-1, 1, 2)# 應用單應性矩陣dst = cv2.perspectiveTransform(pts, M)# 計算邊界框xs = [p[0][0] for p in dst]ys = [p[0][1] for p in dst]x, y, w, h = int(min(xs)), int(min(ys)), int(max(xs)-min(xs)), int(max(ys)-min(ys))if w > 5 and h > 5 and w < frame.shape[1] and h < frame.shape[0]:# 計算匹配質量match_quality = len(matches) * (1.0 - np.mean([m.distance for m in matches[:10]])/100.0)if match_quality > best_matches:best_matches = match_qualitybest_match = (x, y, w, h)except Exception as e:print(f"ORB匹配錯誤: {e}")continueif best_match and search_area_roi:# 調整坐標到原圖x, y, w, h = best_matchbest_match = (x + search_area_roi[0], y + search_area_roi[1], w, h)return best_matchdef main(video_path, roi_coords):# 打開視頻文件cap = cv2.VideoCapture(video_path)if not cap.isOpened():print("無法打開視頻文件")return# 讀取第一幀獲取圖像尺寸ret, frame = cap.read()if not ret:print("無法讀取第一幀")returnheight, width = frame.shape[:2]print(f"視頻尺寸: {width}x{height}")# 解析并驗證ROI坐標try:if len(roi_coords) != 4:raise ValueError("需要4個坐標值: x, y, width, height")x, y, w, h = map(int, roi_coords)print(f"初始ROI: x={x}, y={y}, w={w}, h={h}")if w <= 0 or h <= 0:raise ValueError("ROI寬度和高度必須為正數")roi_box = safe_roi((x, y, w, h), width, height)if roi_box[2] <= 0 or roi_box[3] <= 0:raise ValueError(f"調整后ROI無效: {roi_box}")print(f"有效ROI: x={roi_box[0]}, y={roi_box[1]}, w={roi_box[2]}, h={roi_box[3]}")except Exception as e:print(f"ROI坐標錯誤: {e}")print(f"請確保ROI在圖像范圍內 (0-{width}, 0-{height})")cap.release()return# 保存初始模板用于重新檢測x0, y0, w0, h0 = roi_boxinitial_template = frame[y0:y0+h0, x0:x0+w0].copy()# 創建跟蹤器tracker = Nonetracker_types = [('CSRT', cv2.TrackerCSRT_create),('KCF', cv2.TrackerKCF_create),('MOSSE', cv2.TrackerMOSSE_create)]# 重新讀取第一幀cap.set(cv2.CAP_PROP_POS_FRAMES, 0)ret, frame = cap.read()# 嘗試不同的跟蹤器for tracker_name, tracker_creator in tracker_types:try:print(f"嘗試使用 {tracker_name} 跟蹤器...")tracker = tracker_creator()success = tracker.init(frame, roi_box)if success:print(f"{tracker_name} 跟蹤器初始化成功")breakelse:print(f"{tracker_name} 跟蹤器初始化失敗")tracker = Noneexcept:print(f"{tracker_name} 跟蹤器創建失敗")tracker = Noneif tracker is None:print("無法初始化任何跟蹤器")cap.release()returnprint("開始跟蹤目標...")# 創建窗口cv2.namedWindow("目標跟蹤", cv2.WINDOW_NORMAL)# 顯示初始幀和ROIcv2.rectangle(frame, (roi_box[0], roi_box[1]), (roi_box[0] + roi_box[2], roi_box[1] + roi_box[3]), (0, 255, 0), 2)cv2.putText(frame, "按ESC退出", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)cv2.imshow("目標跟蹤", frame)cv2.waitKey(1000)frame_count = 0tracking_failures = 0max_failures = 200  # 允許更多失敗幀reinit_threshold = 3  # 更早開始重新檢測last_known_position = roi_boxreinit_attempts = 0last_success_time = time.time()# 模板管理templates = [initial_template]  # 模板列表max_templates = 5  # 更多模板template_update_interval = 5  # 更頻繁更新模板# 運動預測prev_positions = []max_history = 10# 重新檢測狀態reinit_mode = 0  # 0: 模板匹配, 1: ORB特征匹配, 2: 全圖搜索while True:ret, frame = cap.read()if not ret:print("視頻結束")breakframe_count += 1# 更新跟蹤器success, bbox = tracker.update(frame)# 處理跟蹤結果if success:x, y, w, h = [int(v) for v in bbox]safe_bbox = safe_roi((x, y, w, h), width, height)if safe_bbox[2] > 0 and safe_bbox[3] > 0:# 更新位置歷史if len(prev_positions) >= max_history:prev_positions.pop(0)prev_positions.append((x, y, w, h))cv2.rectangle(frame, (safe_bbox[0], safe_bbox[1]), (safe_bbox[0] + safe_bbox[2], safe_bbox[1] + safe_bbox[3]), (0, 255, 0), 2)status_text = f"跟蹤成功 (幀 {frame_count})"cv2.putText(frame, status_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)tracking_failures = 0last_known_position = safe_bboxreinit_attempts = 0last_success_time = time.time()reinit_mode = 0  # 重置重新檢測模式# 定期更新模板if frame_count % template_update_interval == 0:# 獲取當前目標區域target_roi = frame[safe_bbox[1]:safe_bbox[1]+safe_bbox[3], safe_bbox[0]:safe_bbox[0]+safe_bbox[2]]# 檢查與現有模板的相似度min_similarity = 0.7too_similar = Falsefor tpl in templates:if target_roi.shape[0] > 5 and target_roi.shape[1] > 5:# 計算直方圖相似度hist1 = cv2.calcHist([target_roi], [0], None, [256], [0,256])hist2 = cv2.calcHist([tpl], [0], None, [256], [0,256])cv2.normalize(hist1, hist1)cv2.normalize(hist2, hist2)similarity = cv2.compareHist(hist1, hist2, cv2.HISTCMP_CORREL)if similarity > min_similarity:too_similar = Truebreak# 只添加顯著不同的模板if not too_similar:# 如果模板數量已達上限,移除最舊的if len(templates) >= max_templates:templates.pop(0)# 添加新模板if target_roi.size > 0 and target_roi.shape[0] > 5 and target_roi.shape[1] > 5:templates.append(target_roi.copy())print(f"更新模板,當前模板數: {len(templates)}")# 處理跟蹤失敗if not success:tracking_failures += 1status_text = f"跟蹤失敗 (幀 {frame_count})"cv2.putText(frame, status_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)# 嘗試重新檢測目標if tracking_failures >= reinit_threshold and reinit_attempts < 30:reinit_attempts += 1print(f"嘗試重新檢測目標 (嘗試 {reinit_attempts}, 模式 {reinit_mode})")# 動態調整搜索區域邊界base_margin = 300dynamic_margin = min(1000, base_margin + 50 * reinit_attempts)  # 更大搜索范圍# 使用位置歷史預測搜索區域if len(prev_positions) >= 2:# 計算平均運動向量dx = 0dy = 0speeds = []for i in range(1, len(prev_positions)):dx_i = prev_positions[i][0] - prev_positions[i-1][0]dy_i = prev_positions[i][1] - prev_positions[i-1][1]dx += dx_idy += dy_ispeeds.append(np.sqrt(dx_i**2 + dy_i**2))# 計算平均速度avg_speed = np.mean(speeds) if speeds else 0# 基于速度和方向預測predict_frames = min(20, 5 + reinit_attempts)  # 預測幀數predict_x = last_known_position[0] + int(dx * predict_frames / len(prev_positions))predict_y = last_known_position[1] + int(dy * predict_frames / len(prev_positions))# 根據速度調整搜索范圍speed_factor = min(3.0, 1.0 + avg_speed/50.0)dynamic_margin = int(min(1000, 300 * speed_factor + 50 * reinit_attempts))# 確保預測位置在圖像范圍內predict_x = max(0, min(width - 1, predict_x))predict_y = max(0, min(height - 1, predict_y))search_center = (predict_x, predict_y)else:search_center = (last_known_position[0] + last_known_position[2] // 2, last_known_position[1] + last_known_position[3] // 2)# 計算搜索區域search_x1 = max(0, search_center[0] - dynamic_margin)search_y1 = max(0, search_center[1] - dynamic_margin)search_x2 = min(width, search_center[0] + dynamic_margin)search_y2 = min(height, search_center[1] + dynamic_margin)search_area_roi = (search_x1, search_y1, search_x2, search_y2)# 根據重新檢測模式選擇方法candidate_roi = None# 模式0: 模板匹配if reinit_mode == 0:if search_x2 > search_x1 and search_y2 > search_y1:search_area = frame[search_y1:search_y2, search_x1:search_x2]# 自適應模板匹配best_val, best_loc, best_scale, best_template_idx = adaptive_template_match(search_area, templates)print(f"模板匹配結果: 置信度={best_val:.2f}, 尺度={best_scale}, 模板={best_template_idx}")# 動態匹配閾值match_threshold = max(0.35, 0.7 - 0.02 * reinit_attempts)  # 更低閾值if best_val > match_threshold:# 計算匹配位置match_x = search_x1 + int(best_loc[0] / best_scale)match_y = search_y1 + int(best_loc[1] / best_scale)# 計算縮放后的模板尺寸scaled_w = int(w0 * (1.0 / best_scale))scaled_h = int(h0 * (1.0 / best_scale))# 創建新的ROIcandidate_roi = (match_x, match_y, scaled_w, scaled_h)print(f"模板匹配候選: {candidate_roi}")# 模式1: ORB特征匹配if reinit_mode == 1 or (reinit_mode == 0 and candidate_roi is None):print("嘗試ORB特征匹配...")candidate_roi = detect_with_orb(frame, templates, (search_x1, search_y1, search_x2, search_y2))if candidate_roi:print(f"ORB檢測到候選目標: {candidate_roi}")# 模式2: 全圖搜索if reinit_mode == 2 or (reinit_mode == 1 and candidate_roi is None):print("嘗試金字塔全圖搜索...")# 使用圖像金字塔pyramid_levels = 3best_candidate = Nonebest_score = -1for level in range(pyramid_levels):scale = 1.0 / (2 ** level)resized_frame = cv2.resize(frame, None, fx=scale, fy=scale)# 在縮小后的圖像上搜索candidate = detect_with_orb(resized_frame, templates)if candidate:# 縮放回原圖坐標x, y, w, h = candidatecandidate = (int(x/scale), int(y/scale), int(w/scale), int(h/scale))# 評分 (使用模板匹配驗證)score = template_match_score(frame, candidate, templates)if score > best_score:best_score = scorebest_candidate = candidatecandidate_roi = best_candidateif candidate_roi:print(f"金字塔搜索檢測到候選目標: {candidate_roi}, 分數={best_score:.2f}")# 處理檢測結果if candidate_roi:safe_new_roi = safe_roi(candidate_roi, width, height)if safe_new_roi[2] > 5 and safe_new_roi[3] > 5:# 添加更靈活的驗證閾值min_sim = max(0.25, 0.4 - 0.01 * reinit_attempts)  # 隨嘗試次數降低閾值# 添加多種驗證方法valid = validate_detection(frame, safe_new_roi, templates, min_sim)# 添加輪廓相似度驗證if not valid and len(templates) > 0:template = templates[-1]  # 使用最新模板candidate_img = frame[safe_new_roi[1]:safe_new_roi[1]+safe_new_roi[3], safe_new_roi[0]:safe_new_roi[0]+safe_new_roi[2]]if candidate_img.size > 0:contour_sim = contour_similarity(candidate_img, template)print(f"輪廓相似度: {contour_sim:.2f}")if contour_sim > 0.6:  # 輪廓相似度閾值print(f"輪廓驗證通過: {contour_sim:.2f}")valid = Trueif valid:# 嘗試重新初始化跟蹤器for tracker_name, tracker_creator in tracker_types:try:new_tracker = tracker_creator()init_success = new_tracker.init(frame, safe_new_roi)if init_success:tracker = new_trackertracking_failures = 0reinit_attempts = 0last_known_position = safe_new_roiprint(f"重新檢測到目標!使用 {tracker_name} 跟蹤器")# 繪制重新檢測到的區域cv2.rectangle(frame, (safe_new_roi[0], safe_new_roi[1]), (safe_new_roi[0] + safe_new_roi[2], safe_new_roi[1] + safe_new_roi[3]), (255, 0, 0), 2)cv2.putText(frame, "重新檢測到目標!", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)# 更新最后成功時間last_success_time = time.time()breakexcept:continueelse:print("檢測結果驗證失敗")# 繪制搜索區域cv2.rectangle(frame, (search_x1, search_y1), (search_x2, search_y2), (0, 255, 255), 1)cv2.putText(frame, f"搜索區域 (模式:{reinit_mode})", (search_x1, search_y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 1)# 升級重新檢測模式if reinit_attempts % 5 == 0:reinit_mode = (reinit_mode + 1) % 3print(f"升級重新檢測模式到 {reinit_mode}")if tracking_failures > max_failures:print(f"連續跟蹤失敗超過 {max_failures} 次,停止跟蹤")break# 顯示幀cv2.imshow("目標跟蹤", frame)# 按ESC退出if cv2.waitKey(30) & 0xFF == 27:break# 釋放資源cap.release()cv2.destroyAllWindows()print(f"跟蹤完成,處理了 {frame_count} 幀")def get_first_frame(video_path):"""提取并保存視頻第一幀"""cap = cv2.VideoCapture(video_path)if not cap.isOpened():print("無法打開視頻文件")return Noneret, frame = cap.read()cap.release()if not ret:print("無法讀取視頻第一幀")return Noneoutput_path = "first_frame.jpg"cv2.imwrite(output_path, frame)print(f"已保存第一幀為: {output_path}")return output_pathif __name__ == "__main__":if len(sys.argv) < 6:print("請提供視頻文件路徑和初始ROI坐標")print("用法: python tracker.py <video_path> <x> <y> <width> <height>")print("示例: python tracker.py video.mp4 100 50 200 150")# 如果只有視頻路徑,提取第一幀if len(sys.argv) == 2:video_path = sys.argv[1]if os.path.exists(video_path):first_frame = get_first_frame(video_path)if first_frame:print(f"請使用圖像查看軟件打開 '{first_frame}' 獲取ROI坐標")else:video_path = sys.argv[1]roi_coords = sys.argv[2:6]  # 讀取四個坐標值# 檢查視頻文件是否存在if not os.path.exists(video_path):print(f"錯誤: 視頻文件 '{video_path}' 不存在")else:main(video_path, roi_coords)

五、測試

python .\02_tracker.py .\normal_video.mp4 185 375 70 70

測試結果

測試過程中發現,中途如果目標消失或目標過小,那就檢測不到,算法還有待優化

如有侵權,或需要完整代碼,請及時聯系博主。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/91468.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/91468.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/91468.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

第二篇:Three.js核心三要素:場景、相機、渲染器

第二篇&#xff1a;Three.js核心三要素&#xff1a;場景、相機、渲染器 引言 在Three.js的世界里&#xff0c;場景(Scene)、相機(Camera)和渲染器(Renderer)構成了最基礎的"鐵三角"。它們如同導演、攝像機和放映機&#xff0c;共同決定了3D內容的呈現方式。本篇將深入…

RagFlow本地源碼部署(非Docker)

參考官方文檔做個總結 1. 提前安裝好uv pipx install uv pre-commit2. 下載源碼&#xff1a; git clone https://github.com/infiniflow/ragflow.git cd ragflow/ uv sync --python 3.10 --all-extras # install RAGFlow dependent python modules uv run download_deps.py …

[免費]基于Python的招聘職位信息推薦系統(獵聘網數據分析與可視化)(Django+requests庫)【論文+源碼+SQL腳本】

大家好&#xff0c;我是python222_小鋒老師&#xff0c;看到一個不錯的基于Python的招聘職位信息推薦系統(獵聘網數據分析與可視化)(Djangorequests庫)&#xff0c;分享下哈。 項目視頻演示 【免費】基于Python的招聘職位信息推薦系統(獵聘網數據分析與可視化)(Django爬蟲) P…

國產化PDF處理控件Spire.PDF教程:Java 提取 PDF 圖片,高質量提取與圖片過濾技巧

在處理包含圖片的 PDF 文件時&#xff0c;例如掃描文檔、產品手冊或宣傳資料&#xff0c;我們經常需要將其中的圖像提取出來&#xff0c;用于保存、識別或再加工。E-iceblue旗下Spire系列產品&#xff0c;是文檔處理領域的佼佼者&#xff0c;支持國產化信創。本文將介紹如何使用…

Cesium 快速入門(七)材質詳解

Cesium 快速入門&#xff08;七&#xff09;材質詳解 看過的知識不等于學會。唯有用心總結、系統記錄&#xff0c;并通過溫故知新反復實踐&#xff0c;才能真正掌握一二 作為一名摸爬滾打三年的前端開發&#xff0c;開源社區給了我飯碗&#xff0c;我也將所學的知識體系回饋給大…

C++:結構體(Structure)

目錄 第一性原理出發&#xff1a;我們要解決什么問題&#xff1f; 定義結構體&#xff08;Defining Structures&#xff09; 問題&#xff1a;名字太長怎么辦&#xff1f; 如何定義結構體變量&#xff1f; 結構體的大小&#xff08;Size of Structures&#xff09; 初始化…

化學結構式解讀指南:從基礎認知到InDraw智能識別

中文名稱&#xff1a;3-[2-(二甲基氨基)乙基]-1H-吲哚英文名稱&#xff1a;3-[2-(dimethylamino)ethyl]-1H-indole分子式: C12H16N2分子量: 188.2740這是什么結構式&#xff1f;怎么繪制呢&#xff1f;可以用InDraw里的AI圖像識別這個結構式&#xff0c;也可以手動繪圖&#xf…

如何使用一臺電腦adb調試多個Android設備

目錄 一、臨時斷開其中一個設備連接 二、指定調試設備 總結 當我們使用Android調試工具調試多個設備&#xff0c;例如一開始使用adb連接了一臺Android真機進行調試&#xff0c;此時又在Android studio中打開了一個模擬機&#xff0c;此時我們在adb命令窗口中使用adb命令的…

ChatGPT的下一站:從“答案引擎”到“思維教練”

摘要&#xff1a;我們正處在一個“萬物皆可ChatGPT”的時代&#xff0c;但當它淪為最高效的“代碼搬運工”和“作業速成器”時&#xff0c;我們得到的究竟是效率的提升還是思維的退化&#xff1f;本文深入探討一個引人深思的概念——“導師模式”的AI。它不再直接提供答案&…

SpringBoot集成Flyway

SpringBoot集成Flyway_springboot flyway-CSDN博客 Flyway 本質上是一個開源的數據庫遷移工具&#xff0c;它能夠以自動化、可重復且可靠的方式管理數據庫的變更。無論是小型項目還是大型企業級應用&#xff0c;Flyway 都能助力開發者輕松應對數據庫架構的演進。它支持多種數據…

【實時Linux實戰系列】實時圖像處理應用開發

在當今快速發展的技術領域&#xff0c;實時圖像處理應用在眾多領域發揮著至關重要的作用。從自動駕駛汽車、工業自動化檢測到醫療影像診斷&#xff0c;實時圖像處理技術的應用場景無處不在。通過在實時Linux系統中開發圖像處理應用&#xff0c;開發者能夠充分利用Linux的穩定性…

Caterpillar Fungus Optimizer, CFO

核心算法解析1. 算法框架與初始化class EnhancedCFO: def __init__(self, objective_func, dim10, pop_size30, max_iter200, lb-10, ub10):??改進點??&#xff1a;針對傳統優化算法后期易停滯的問題&#xff0c;結合了精英策略、多樣性控制和自適應參數??關鍵特性??&a…

c++設計模式編程練習

一、運用觀察者模式原理編寫鳥類模型運行結果&#xff1a;二、運用簡單工廠模式編寫打怪掉裝備模型運行結果

FastMCP本地構建Server和Clinet交互

1. MCP Server介紹 MCP Server 是實現模型上下文協議&#xff08;MCP&#xff09;的服務器&#xff0c;旨在為 AI 模型提供一個標準化接口&#xff0c;連接外部數據源和工具&#xff0c;例如文件系統、數據庫或 API。 相比之下&#xff0c;在MCP出現前&#xff0c;AI調用工具…

工業企業與清潔生產匹配數據庫(1998-2015年)

1484工業企業與清潔生產匹配數據庫&#xff08;1998-2015年&#xff09;“清潔生產”近年發文趨勢及主題分布數據來源中華人民共和國生態環境部以及中國工業企業數據庫&#xff0c;由數據皮皮俠團隊整理時間跨度1998-2015年數據范圍各工業企業數據指標參考文獻孫博文,鄭世林.環…

第13屆藍橋杯C++青少組中/高級組選拔賽2022年1月22日真題

第13屆藍橋杯C青少組中/高級組選拔賽2022年1月22日真題 更多內容請查看網站&#xff1a;【試卷中心 -----> 藍橋杯----> C ----> 選拔賽】 網站鏈接 青少年軟件編程歷年真題模擬題實時更新 編程題 第 1 題 比大小 題目描述&#xff1a; 給出兩個不同的整數&#…

從0到1學PHP(七):PHP 與 HTML 表單:實現數據交互

目錄一、表單的創建與提交方式1.1 HTML 表單的基本結構1.2 GET 和 POST 提交方式的區別及適用場景二、表單數據的接收與處理2.1 使用\$_GET、\$_POST 超全局變量獲取表單數據2.2 對接收的數據進行驗證三、表單安全處理3.1 防止 XSS 攻擊的方法3.2 防止 CSRF 攻擊的措施一、表單…

Docker compose和Docker-compose的區別

Docker Compose 的兩個命令形式 docker compose&#xff08;空格連接&#xff09;與 docker-compose&#xff08;短橫線連接&#xff09;核心區別如下&#xff1a;一、技術本質docker-compose&#xff08;短橫線&#xff09;獨立可執行文件&#xff1a;早期實現方式&#xff0c…

自定心深凹槽參數檢測裝置及檢測方法 - 激光頻率梳 3D 輪廓檢測

一、引言在機械零件深凹槽檢測中&#xff0c;傳統方法常因定心不準導致檢測誤差。如平臺推表檢測時零件基準面與測量平臺難以精準對齊&#xff0c;三坐標測量需人工找正&#xff0c;效率低且誤差大。激光頻率梳 3D 輪廓檢測雖精度高&#xff0c;但缺乏自定心機制會影響深凹槽軸…

C語言---結構體(格式、用法、嵌套、初始化)、共用體、枚舉類型、typedef類型

目錄 結構體與共用體 1、結構體(struct) (1) 格式與用法 (2) 結構體允許嵌套 (3) 結構體成員初始化 (4) 指針替換變量 (5) 求結構體在內存空間所占字節 2、共用體(union) (1) 格式與概念 (2) 應用 3、枚舉類型(enum) (1) 格式與概念 (2) 應用 4、typedef 類型 結構體與共用…