linux上的通用拍照程序

最近因為工作需要,在ubuntu上開發了一個拍照程序。

為了找到合適的功能研究了好幾種實現方式,在這里記錄一下。

目錄

太長不看版

探索過程

v4l2

QT

opencv4.2

打開攝像頭

為什么不直接打開第一個視頻節點

獲取所有分辨率

切換攝像頭


太長不看版

技術:python3.8+opencv4.2+tkinter

支持的功能如下:

  • 預覽
  • 切換攝像頭
  • 切換分辨率
  • 拍照(點擊拍照之后,照片會顯示在右邊)

實現代碼在這里:

import tkinter as tk
import cv2
from PIL import Image, ImageTk
import tkinter.messagebox as messagebox
import sys
import os# Initialize window
root = tk.Tk()
root.title("UVC Camera")
root.geometry("1700x700")# Detect available cameras
camera_indexes = []
for i in range(10):cap = cv2.VideoCapture(i)if not cap.isOpened():continuecamera_indexes.append(i)cap.release()print("Available cameras:", camera_indexes)# Show error message if no camera is available
if len(camera_indexes) == 0:messagebox.showerror("Error", "Can't find the camera")sys.exit(0)# Show error message if camera cannot be opened
try:camera = cv2.VideoCapture(camera_indexes[0])  # Open the first detected camera by defaultcamera.set(6, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')) 
except:messagebox.showerror("Error", "The camera won't open, the equipment is damaged or the contact is bad.")sys.exit(0)# Detect available resolutions
res_options = []
width = int(camera.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT))
res_options.append([width, height])for j in range(30):old_width = int(camera.get(cv2.CAP_PROP_FRAME_WIDTH))old_height = int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT))camera.set(cv2.CAP_PROP_FRAME_WIDTH, width+j*100)camera.set(cv2.CAP_PROP_FRAME_HEIGHT, height+j*100)new_width = int(camera.get(cv2.CAP_PROP_FRAME_WIDTH))new_height = int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT))if new_width != old_width:res_options.append([new_width, new_height])print("Available resolutions:", res_options)# Set the lowest resolution as the default
camera.set(cv2.CAP_PROP_FRAME_WIDTH, res_options[0][0])
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, res_options[0][1])# Button callback functionsdef on_capture():home_dir = os.path.expanduser('~')cv2.imwrite(home_dir + "/capture.png", img)# Resize the image while maintaining the aspect ratiocv2image = cv2.cvtColor(img, cv2.COLOR_BGR2RGBA)current_image = Image.fromarray(cv2image)w, h = current_image.sizeratio = min(850.0 / w, 638.0 / h)current_image = current_image.resize((int(ratio * w), int(ratio * h)), Image.ANTIALIAS)imgtk = ImageTk.PhotoImage(image=current_image)photo_panel.imgtk = imgtkphoto_panel.config(image=imgtk)messagebox.showinfo("Info", "Photo taken successfully")def on_switch_res(value):global cameracamera.set(cv2.CAP_PROP_FRAME_WIDTH, value[0])camera.set(cv2.CAP_PROP_FRAME_HEIGHT, value[1])def on_switch_cam(value):global camera# print("切換攝像頭")# print("選擇的值是: ", str(value))# 結束預覽root.after_cancel(video_loop_id)camera.release()# 創建新的捕捉對象并打開攝像頭camera = cv2.VideoCapture(value)camera.set(6, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')) if not camera.isOpened():messagebox.showerror("Error", "The camera cannot be turned on.")sys.exit()on_video_loop()def on_video_loop():global img,video_loop_idsuccess, img = camera.read() # 從攝像頭讀取照片if success:cv2.waitKey(10)cv2image = cv2.cvtColor(img, cv2.COLOR_BGR2RGBA) # 轉換顏色從BGR到RGBAcurrent_image = Image.fromarray(cv2image)        # 將圖像轉換成Image對象# 等比縮放照片w,h = current_image.sizeratio = min(850.0/w, 600.0/h)current_image = current_image.resize((int(ratio * w), int(ratio * h)), Image.ANTIALIAS)imgtk = ImageTk.PhotoImage(image=current_image)video_panel.imgtk = imgtkvideo_panel.config(image=imgtk)video_loop_id = root.after(1, on_video_loop)video_panel = tk.Label(root)
photo_panel = tk.Label(root)video_panel.grid( # 左上居中對齊row=0, column=0, columnspan=4, padx=20, pady=20, sticky=tk.NW
)photo_panel.grid( # 右上居中對齊row=0, column=4, columnspan=2,sticky=tk.EW, padx=20, pady=20
)# 攝像頭標簽+下拉框
label3 = tk.Label(root, text="Select camera")
label3.grid(row=1, column=0, sticky="E", padx=10, pady=10)variable1 = tk.StringVar(root)
variable1.set(camera_indexes[0])
cam_dropdown = tk.OptionMenu(root, variable1, *camera_indexes, command=on_switch_cam)
cam_dropdown.grid(row=1, column=1, sticky="W", padx=10, pady=10)# 分辨率標簽+下拉框
label4 = tk.Label(root, text="Select resolution")
label4.grid(row=1, column=2, sticky="E", padx=10, pady=10)variable2 = tk.StringVar(root)
variable2.set(res_options[0])
res_dropdown = tk.OptionMenu(root, variable2, *res_options, command=on_switch_res)
res_dropdown.grid(row=1, column=3, sticky="W", padx=10, pady=10)# 拍照和退出按鈕
capture_button = tk.Button(root, text="Take a picture", command=on_capture)
capture_button.grid(row=1, column=4, padx=10, pady=10)exit_button = tk.Button(root, text="Quit", command=root.quit)
exit_button.grid(row=1, column=5, padx=10, pady=10)# 一些頁面設置
root.grid_columnconfigure(0, weight=1)
root.grid_columnconfigure(1, weight=1)
root.grid_columnconfigure(2, weight=1)
root.grid_columnconfigure(3, weight=1)
root.grid_columnconfigure(4, weight=2)
root.grid_columnconfigure(5, weight=2)
root.grid_rowconfigure(0, weight=13)
root.grid_rowconfigure(1, weight=1)on_video_loop()
root.mainloop()

探索過程

v4l2

一開始在網上找到的其實是拍照程序是v4l2的,純c接口。

不過這個相機需要預覽,v4l2接口雖然拍照正常但是沒法預覽,所以放棄了這套方案。

相關內容記錄在:V4L2 零基礎入門(一)——打開攝像頭和獲取攝像頭基本信息_v4l2攝像頭采集-CSDN博客

QT

查看資料發現QT有封裝攝像頭相關的接口,在qtcreator里可以直接找到。

這個demo的功能很齊全,拍照,錄像都有,不過有個致命問題,高分辨率的時候預覽卡的太厲害,簡直卡成ppt。

opencv4.2

為了解決預覽卡頓的問題,開始查找其他的方案,最終找到了Python調用opencv接口。

這套方案在高分辨率下的預覽也很流暢。

實現的代碼我放在一開頭啦,有問題歡迎評論區。

在這邊解釋一些實現的細節。

打開攝像頭

我這里是先打開前10個視頻節點,10是為了處理同時連接多個攝像頭的情況(一個攝像頭有1或者2個節點)。

10這個數是隨便選的,可以改成其他的數

循環前10個節點,看哪個節點能被打開,把能打開的序號存儲在數組里。

最后打開數組里存儲的第一個節點,并設置照片格式為mjpg。

# Detect available cameras
camera_indexes = []
for i in range(10):cap = cv2.VideoCapture(i)if not cap.isOpened():continuecamera_indexes.append(i)cap.release()print("Available cameras:", camera_indexes)# Show error message if no camera is available
if len(camera_indexes) == 0:messagebox.showerror("Error", "Can't find the camera")sys.exit(0)# Show error message if camera cannot be opened
try:camera = cv2.VideoCapture(camera_indexes[0])  # Open the first detected camera by defaultcamera.set(6, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')) 
except:messagebox.showerror("Error", "The camera won't open, the equipment is damaged or the contact is bad.")sys.exit(0)
為什么不直接打開第一個視頻節點

這里解釋一下,為什么繞這么大彎,挨個找哪個節點能打開。

一般來說,直接打開第一個視頻節點一般都不會有問題。

#直接打開第一個視頻節點,代碼會是這種形式
camera = cv2.VideoCapture(0)  

但是可能出現這樣一種情況,即先連接了兩個攝像頭,此時視頻設備的節點編號分別為1和2。

如果取下了視頻設備的節點編號為1攝像頭,再打開拍照程序,如果直接打開第一個節點會出現錯誤。

簡單畫的示意圖如下:

獲取所有分辨率

獲取分辨率的流程有點復雜,先是通過CAP_PROP_FRAME_WIDTH和CAP_PROP_FRAME_HEIGHT獲取最小的分辨率。

然后循環將當前已知的最大的分辨率的x和y分別+100,嘗試這個分辨率在攝像頭上能否設置成功。

如果設置成功,則記錄改分辨率,在這個分辨率的的x和y基礎上分別+100,重復這個過程。

我這里設置了循環30次,這個也是隨意設置的,大家算一下能循環到攝像頭的最大分辨率即可。

# Detect available resolutions
res_options = []
width = int(camera.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT))
res_options.append([width, height])for j in range(30):# 前兩行是獲取當前分辨率old_width = int(camera.get(cv2.CAP_PROP_FRAME_WIDTH))old_height = int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT))camera.set(cv2.CAP_PROP_FRAME_WIDTH, width+j*100)camera.set(cv2.CAP_PROP_FRAME_HEIGHT, height+j*100)new_width = int(camera.get(cv2.CAP_PROP_FRAME_WIDTH))new_height = int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT))# 如果出現了新的可以設置成功的分辨率,保存下來if new_width != old_width:res_options.append([new_width, new_height])print("Available resolutions:", res_options)

這里可能會有個問題,如果x和y分別+100的所有分辨率都不是攝像頭支持的怎么辦呢?

其實攝像頭設置分辨率是比較智能的,不需要完全匹配。

假如支持是分辨率是950*650,實際設置分辨率1000*700,這種差的不太遠的,攝像頭會自動識別成自己支持的分辨率。(這只是個例子,實際差多少之內可以識別,沒有詳細測過)

切換攝像頭

切換攝像頭需要先把當前的預覽停掉,釋放當前的攝像頭。

再重新打開攝像頭,設置圖片類型。

def on_switch_cam(value):global camera# print("切換攝像頭")# print("選擇的值是: ", str(value))# 結束預覽root.after_cancel(video_loop_id)camera.release()# 創建新的捕捉對象并打開攝像頭camera = cv2.VideoCapture(value)camera.set(6, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')) if not camera.isOpened():messagebox.showerror("Error", "The camera cannot be turned on.")sys.exit()on_video_loop()# 預覽        
def on_video_loop():global img,video_loop_idsuccess, img = camera.read() # 從攝像頭讀取照片if success:cv2.waitKey(10)cv2image = cv2.cvtColor(img, cv2.COLOR_BGR2RGBA) # 轉換顏色從BGR到RGBAcurrent_image = Image.fromarray(cv2image)        # 將圖像轉換成Image對象# 等比縮放照片w,h = current_image.sizeratio = min(850.0/w, 600.0/h)current_image = current_image.resize((int(ratio * w), int(ratio * h)), Image.ANTIALIAS)imgtk = ImageTk.PhotoImage(image=current_image)video_panel.imgtk = imgtkvideo_panel.config(image=imgtk)video_loop_id = root.after(1, on_video_loop)

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

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

相關文章

七要素微氣象儀氣象數據監測助手

WX-WQX7 隨著科技的發展,氣象預測的準確性已成為人們日常生活的重要參考。而七要素微氣象儀,作為新型的氣象探測設備,以其精細化的數據測量和解析能力,正在改變我們的天氣預測方式。 一、產品介紹 七要素微氣象儀是一款集成了溫…

某投資公司薪酬激勵體系改革項目成功案例紀實

——優化薪酬結構,實現薪酬公平,提高員工工作積極性 【客戶行業】金融行業 【問題類型】薪酬激勵 【客戶背景】 某投資管理公司位于一線城市,是經市人民ZF批準,在2000年左右設立的市直屬綜合性投資公司。主要承擔ZF重大建設項目…

JAVA實現flappy bird游戲

圖片素材 實現代碼 import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.util.Date; import java.text.SimpleDateFormat; i…

飛翔的小鳥游戲

GameApp類 package App;import main.GameFrame;public class GameApp {public static void main(String[] args) {//游戲的入口new GameFrame();} } main Barrier 類 package main;import util.Constant; import util.GameUtil;import java.awt.*; import java.awt.image.Buf…

gitea倉庫鏡像同步至gitlab

1、參考文檔:倉庫鏡像 | Gitea Documentation 2、錯誤一:賬號密碼錯誤問題 解決方法: 出現以上錯誤為第三步用戶名(Oauth2應用名稱)或者密碼(Gitlab個人訪問令牌)錯誤。 1)如下圖1…

【UE4虛幻】UTexture2D紋理裁剪函數

參數說明: 源2D紋理圖片:UTexture2D* SourceTexture 裁剪區域的左上角:const FVector2D TopLeft 裁剪區域的右下角:const FVector2D BottomRight 返回值是裁剪之后的紋理圖片 UTexture2D* APointProjection::CropPicture(UTextur…

洗地機哪個牌子好用?洗地機選購攻略

傳統的清潔方式都是掃把拖把的結合,既繁瑣也勞累,每次清潔完后還得累的腰酸背痛的,像廚房這種地方甚至會不容易清潔干凈,總感覺地板灰蒙蒙的。洗地機的誕生就很好的解決了這些問題,不用一遍遍的重復掃地拖地擦地&#…

1410. HTML 實體解析器 --力扣 --JAVA

題目 「HTML 實體解析器」 是一種特殊的解析器,它將 HTML 代碼作為輸入,并用字符本身替換掉所有這些特殊的字符實體。 HTML 里這些特殊字符和它們對應的字符實體包括: 雙引號:字符實體為 " ,對應的字符是 &qu…

JOSEF約瑟 過電流繼電器 JL15-300/11 觸點形式一開一閉 板前接線

系列型號 JL15-1.5/11電流繼電器JL15-2.5/11電流繼電器 JL15-5/11電流繼電器JL15-10/11電流繼電器 JL15-15/11電流繼電器JL15-20/11電流繼電器 JL15-30/11電流繼電器JL15-40/11電流繼電器 JL15-60/11電流繼電器JL15-80/11電流繼電器 JL15-100/11電流繼電器JL15-150/11電流繼電…

linux的系統

10.4 I2C 系統的重要結構體 參考資料: ? Linux 驅動程序:(某版本的 Linux,比如 Linux-4.9.88)/drivers/i2c ? I2CTools: https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/ 284 / 577 10.4.1 重要結構體 使用一句…

母嬰團隊半年破億秘訣揭秘,合規經營成就輝煌

這支母嬰產品銷售團隊在短短半年內實現了過億的銷售額,這是一個令人矚目的成就。他們既不依賴線下門店,也不是傳統的電商平臺,那么他們是如何做到這一點的呢? 這個團隊從16年10月開始經營母嬰產品,如今已經過去了6年時…

生成式AI:SEO的末日?

由于在搜索結果中引入生成式AI (GAI),以 SEO 為主導的內容的未來成為最近的熱門話題,這是有充分理由的。 對于出版商和網站所有者(從現在開始我們將他們稱為內容創建者)的影響可能是毀滅性的。 如下圖所示,谷歌新的搜…

成為AI產品經理——模型構建過程(上)

目錄 一、背景 1.對內 2.對外 二、模型構建過程 1.模型設計 2.特征工程 ① 數據清洗 ② 特征提取 數值型數據 標簽/描述類數據特征 非結構化數據(處理文本特征) 網絡關系型數據 ③ 特征選擇 ④ 訓練集/測試集 一、背景 雖然產品經理不…

Linux應用開發基礎知識——I2C應用編程(十二)

前言: I2C(Inter-Integrated Circuit BUS)是集成電路總線,是目前應用最廣泛的總線之一,最初由PHILIPS(現為NXP)設計。它使用多主從架構,主要用于連接低速周邊設備。I2C總線在硬件物理…

WorkPlus即時通訊,打通上下游產業鏈,構建企業生態圈

如今,隨著信息技術的迅速發展,智慧水務、智慧醫療、智慧城市、智慧教育、智慧政務等領域正蓬勃發展。在這個智慧時代,企業需要一個具備開放性和擴展性的平臺級產品,以滿足多樣化的業務需求。WorkPlus作為一款全新的移動底座產品&a…

String 真的不可變嗎?

為什么 String 類不可變 final修飾符: String類被聲明為final,這意味著它不能被繼承。因此,無法創建String的子類來修改其行為。私有字符數組(char[]): String類內部使用私有的字符數組來存儲字符串的內容…

Excel文件比較不再繁瑣,xlCompare助您快速找出差異

概要 在現代職場中,Excel 已成為工作中不可或缺的利器。 在日常操作中,我們會遇到需要對兩個或多個 Excel 文件進行比較的情況,此時,一款高效的 Excel 文件比較工具就顯得尤為重要。 本文將為您介紹一款功能強大、優勢明顯的 Exc…

創新建筑形式:氣膜體育館助力校園體育設施革新

體育場館在校園中扮演著重要的角色,是學生們進行體育鍛煉、比賽和各類體育活動的場所。傳統的室內體育館建設往往需要大量資金和漫長的建設周期,但隨著氣膜體育館的嶄露頭角,校園體育設施的面貌正迎來一場革新。 快速搭建,靈活性極…

電機應用開發-直流有刷電機電流環控制實現

目錄 直流有刷電機電流環控制實現 硬件設計 直流電機電流環控制-位置式PID實現 編程要點 配置ADC可讀取電流值 配置基本定時器6產生定時中斷讀取當前電路中驅動電機的電流值并執行PID運算 配置定時器1輸出PWM控制電機 ADC數據處理 編寫位置式PID算法 直流電機電流環控…

3、領導跟你談話,講到你的團隊里面的好友,公司會進行觀察裁員,你會去傳話么?

作為一個團隊成員,我會認真聽取領導的意見,并尊重公司的決定。然而,作為一個好友,我也會考慮他們的利益,我會與他們溝通,提醒他們注意自己的表現和工作,努力提高業績和工作質量,以確…