由于使用的相机软件没有拍摄快捷键触发拍摄所以想到这个笨方法。实现原理自动按顺序投影投影第一张图片时鼠标自动依次按下坐标1和坐标2拍第一张图后续每投影一张新图片鼠标自动按下坐标2拍摄以实现自投影拍照的效果。补充不用相机软件也可以直接在代码里调用相机接口实现拍照功能。首先相机软件全屏并截下整个电脑屏幕存为一张图片如下图。importcv2importjsonimportosimportnumpyasnp# 配置SCREENSHOT_PATHxjcap.png# 全屏截图路径COORDS_FILEcamera_coords.json# 保存两个坐标的文件defresize_image_for_display(img,max_width1200,max_height800): 调整图片大小以便在屏幕上完整显示 height,widthimg.shape[:2]# 计算缩放比例scale1.0ifwidthmax_widthorheightmax_height:scale_widthmax_width/width scale_heightmax_height/height scalemin(scale_width,scale_height)# 调整大小new_widthint(width*scale)new_heightint(height*scale)resized_imgcv2.resize(img,(new_width,new_height),interpolationcv2.INTER_AREA)returnresized_img,scaledefselect_coordinate(img,window_title,instruction,scale1.0): 显示图片让用户框选区域返回原始坐标考虑缩放比例 print(f\n{instruction})print(操作说明)print( - 拖动鼠标选择矩形区域)print( - 按Enter键确认选择)print( - 按C键取消选择)print( - 按ESC键跳过此步骤)# 创建可调整大小的窗口cv2.namedWindow(window_title,cv2.WINDOW_NORMAL)cv2.resizeWindow(window_title,1000,800)# 设置初始窗口大小# 显示图片cv2.imshow(window_title,img)# 获取ROIroicv2.selectROI(window_title,img,fromCenterFalse,showCrosshairTrue)cv2.destroyWindow(window_title)x,y,w,hroiifw0orh0:print( ⚠️ 未选择有效区域)returnNone# 转换回原始坐标考虑缩放比例ifscale!1.0:x_origint(x/scale)y_origint(y/scale)w_origint(w/scale)h_origint(h/scale)print(f 坐标已从显示尺寸转换回原始尺寸)else:x_orig,y_orig,w_orig,h_origx,y,w,h# 计算中心坐标center_xint(x_origw_orig/2)center_yint(y_origh_orig/2)print(f ✅ 选择成功)print(f 显示坐标: ({x},{y},{w},{h}))print(f 原始坐标: ({x_orig},{y_orig},{w_orig},{h_orig}))print(f 中心坐标: ({center_x},{center_y}))return{rect:{x:x_orig,y:y_orig,w:w_orig,h:h_orig},center:{x:center_x,y:center_y},scale:scale}defmain():# 检查截图文件是否存在ifnotos.path.exists(SCREENSHOT_PATH):print(f❌ 错误找不到截图文件{SCREENSHOT_PATH})print( 请先将全屏截图保存为 xjcap.png 并放在当前目录)print( 截图应包含)print( 1. 任务栏上的相机软件图标位置)print( 2. 相机软件窗口中的拍照按钮位置)return# 读取图片imgcv2.imread(SCREENSHOT_PATH)ifimgisNone:print(❌ 无法读取图片请检查文件格式是否正确)print( 支持的格式PNG, JPG, JPEG, BMP等)returnprint( 相机坐标设置工具 )print(本工具需要设置两个关键坐标)print(1. 相机软件在任务栏的位置用于切换到相机窗口)print(2. 相机软件中的拍照按钮位置用于自动拍照)print(\n 操作步骤)print( 第一步框选任务栏中相机软件图标的位置)print( 第二步框选相机软件窗口中的拍照按钮位置)input(\n按 Enter 键开始...)# 调整图片大小以便显示print(\n️ 正在调整图片大小以便显示...)display_img,scaleresize_image_for_display(img.copy())print(f 原始图片尺寸:{img.shape[1]}x{img.shape[0]})print(f 显示图片尺寸:{display_img.shape[1]}x{display_img.shape[0]})print(f 缩放比例:{scale:.3f})# 第一步选择相机软件位置 print(\n*50)print( 第一步选择相机软件位置)print(*50)print(请在任务栏上找到GalaxyView相机软件图标)print(通常在屏幕底部任务栏图标较小)print( 提示你可以调整窗口大小来查看更多区域)camera_app_coordsNonewhilecamera_app_coordsisNone:camera_app_coordsselect_coordinate(display_img.copy(),1. 框选相机软件位置任务栏图标, 请框选任务栏中GalaxyView相机软件图标的位置,scale)ifcamera_app_coordsisNone:retryinput(未选择有效区域是否重新选择(y/n): ).strip().lower()ifretry!y:print(❌ 操作已取消)return# 第二步选择拍照按钮位置 print(\n*50)print( 第二步选择拍照按钮位置)print(*50)print(请在相机软件窗口中找到拍照按钮)print(通常是圆形或方形的大按钮位于屏幕中央或底部)print( 提示调整窗口大小可以更好地查看按钮细节)capture_button_coordsNonewhilecapture_button_coordsisNone:capture_button_coordsselect_coordinate(display_img.copy(),2. 框选拍照按钮位置, 请框选相机软件中的拍照按钮位置,scale)ifcapture_button_coordsisNone:retryinput(未选择有效区域是否重新选择(y/n): ).strip().lower()ifretry!y:print(❌ 操作已取消)return# 保存坐标 coords_data{camera_app:{description:相机软件在任务栏的位置用于切换窗口,rect:camera_app_coords[rect],center:camera_app_coords[center]},capture_button:{description:相机软件中的拍照按钮位置用于自动拍照,rect:capture_button_coords[rect],center:capture_button_coords[center]},metadata:{screenshot_file:SCREENSHOT_PATH,original_image_size:{width:img.shape[1],height:img.shape[0]},display_scale:scale,created_at:2026-04-03 18:02}}# 保存到JSON文件try:withopen(COORDS_FILE,w,encodingutf-8)asf:json.dump(coords_data,f,indent2,ensure_asciiFalse)print(f\n✅ 坐标已成功保存至:{COORDS_FILE})# 显示保存的坐标print(\n 保存的坐标信息)app_centercoords_data[camera_app][center]button_centercoords_data[capture_button][center]print(f 相机软件位置: ({app_center[x]},{app_center[y]}))print(f 拍照按钮位置: ({button_center[x]},{button_center[y]}))print(\n 设置完成)print( 使用提示)print( - 在主程序中读取此JSON文件获取坐标)print( - 使用pyautogui.click()函数点击这些坐标)print( - 坐标基于原始屏幕分辨率请勿更改分辨率)print(f - 原始截图尺寸:{img.shape[1]}x{img.shape[0]}像素)# 验证坐标是否合理print(\n 坐标验证)screen_width,screen_heightimg.shape[1],img.shape[0]if(0app_center[x]screen_widthand0app_center[y]screen_heightand0button_center[x]screen_widthand0button_center[y]screen_height):print(✅ 坐标在有效范围内)else:print(⚠️ 坐标可能超出屏幕范围请检查是否正确)exceptExceptionase:print(f❌ 保存坐标时出错:{e})if__name____main__:main()保存坐标后就可以用下面代码自投影拍照了。importosimporttimeimportglobimportjsonimportpygameimportpyautoguifromscreeninfoimportget_monitors# 配置 FOLDER_PATH./Atw# 投影图片所在路径SUPPORTED_EXTS(.jpg,.jpeg,.png,.bmp,.tiff)COORDS_FILEcamera_coords.jsonDELAY_BEFORE_CLICK0.3CLICK_INTERVAL0.15DELAY_AFTER_CAPTURE0.1SWITCH_DELAY0.1# 切换下一张前的延迟秒# defload_coordinates():ifnotos.path.exists(COORDS_FILE):print(f错误找不到坐标文件{COORDS_FILE})returnNonewithopen(COORDS_FILE,r,encodingutf-8)asf:datajson.load(f)coords{camera_app:{center:data[camera_app][center]},capture_button:{center:data[capture_button][center]}}print(已加载坐标)print(f 坐标1相机图标: ({coords[camera_app][center][x]},{coords[camera_app][center][y]}))print(f 坐标2拍摄按钮: ({coords[capture_button][center][x]},{coords[capture_button][center][y]}))returncoordsdefget_projection_screen():monitorsget_monitors()fori,minenumerate(monitors):ifnotm.is_primary:print(f使用副屏:{m.width}x{m.height}位置({m.x},{m.y}))returni,mfori,minenumerate(monitors):ifm.x!0orm.y!0:print(f使用副屏:{m.width}x{m.height}位置({m.x},{m.y}))returni,mprint(错误未检测到扩展屏幕请设置为“扩展”模式)returnNone,Nonedefget_sorted_images(folder):img_files[fforfinglob.glob(os.path.join(folder,*))iff.lower().endswith(SUPPORTED_EXTS)]ifnotimg_files:return[]try:fromnatsortimportnatsortedreturnnatsorted(img_files)exceptImportError:print(未安装 natsort使用简单数字排序建议 pip install natsort)img_files.sort(keylambdax:int(.join(filter(str.isdigit,os.path.basename(x).split(.)[0])))ifany(c.isdigit()forcinos.path.basename(x))else0)returnimg_filesdefmain():print( 使用 pygame 的自动投影拍摄工具 )# 1. 加载坐标coordsload_coordinates()ifcoordsisNone:return# 2. 获取副屏索引和屏幕信息screen_idx,screen_infoget_projection_screen()ifscreen_idxisNone:return# 3. 获取图片列表ifnotos.path.exists(FOLDER_PATH):print(f错误文件夹{FOLDER_PATH}不存在)returnimg_filesget_sorted_images(FOLDER_PATH)ifnotimg_files:print(文件夹中没有支持的图片文件)returnprint(f共找到{len(img_files)}张图片)# 4. 初始化 pygamepygame.init()# 设置显示环境变量让 pygame 窗口出现在指定屏幕# 方法先获取屏幕尺寸然后创建全屏窗口pygame 默认会创建在主屏但可以通过设置 display index# 更好的方法创建一个无边框窗口然后移动到副屏再全屏# 这里使用 SDL 环境变量强制指定显示索引os.environ[SDL_VIDEO_WINDOW_POS]f{screen_info.x},{screen_info.y}screenpygame.display.set_mode((screen_info.width,screen_info.height),pygame.FULLSCREEN)pygame.display.set_caption(Projector)# 5. 循环处理图片foridx,img_pathinenumerate(img_files,1):filenameos.path.basename(img_path)print(f\n [{idx}/{len(img_files)}] 显示:{filename})# 加载图片并缩放imgpygame.image.load(img_path)# 缩放图片以适合屏幕保持比例img_rectimg.get_rect()screen_rectscreen.get_rect()scale_wscreen_rect.width/img_rect.width scale_hscreen_rect.height/img_rect.height scalemin(scale_w,scale_h)new_widthint(img_rect.width*scale)new_heightint(img_rect.height*scale)scaled_imgpygame.transform.scale(img,(new_width,new_height))# 计算居中位置x(screen_rect.width-new_width)//2y(screen_rect.height-new_height)//2# 清屏并绘制图片screen.fill((0,0,0))screen.blit(scaled_img,(x,y))pygame.display.flip()# 等待投影稳定time.sleep(DELAY_BEFORE_CLICK)# 执行点击ifidx1:# 第一张点击坐标1和坐标2print( 点击相机图标(坐标1))pyautogui.click(coords[camera_app][center][x],coords[camera_app][center][y])time.sleep(CLICK_INTERVAL)print( 点击拍摄按钮(坐标2))pyautogui.click(coords[capture_button][center][x],coords[capture_button][center][y])else:# 后续只点坐标2print( 点击拍摄按钮(坐标2))pyautogui.click(coords[capture_button][center][x],coords[capture_button][center][y])time.sleep(DELAY_AFTER_CAPTURE)# 6. 所有图片处理完毕print(\n 所有图片拍摄完成)time.sleep(0.5)pygame.quit()# 退出 pygame屏幕恢复print(扩展屏已恢复)if__name____main__:main()