備忘錄_20160105(定位) 修改 回首頁

程式 2025-11-05 15:41:36 1762328496 100
辨識藥品顆粒並圈出來

辨識藥品顆粒並圈出來

count.py
import cv2
import time
from datetime import datetime
import numpy as np
import sys
import os
import platform
import subprocess
from scipy.stats import norm

def open_image(path):
  
  system = platform.system()
  if system == "Windows":
    os.startfile(path)  # 直接呼叫預設看圖程式
  elif system == "Darwin":  # macOS
    subprocess.run(["open", path])
  else:  # Linux (例如 Raspberry Pi, Ubuntu)
    subprocess.run(["xdg-open", path])

def count_and_draw(in_path, out_path="output.png"):
  
  img = cv2.imread(in_path)
  gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  blur = cv2.GaussianBlur(gray, (7,7), 0)
  ret, thresh = cv2.threshold(blur, 200, 255, cv2.THRESH_BINARY) # 手動閥值 200
  contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  
  # 將極端面積的區域去除(極小面積、極大面積)
  # 計算一顆藥錠的面積 unit_area
  area_list=[]
  for i,contour in enumerate(contours):
    area = cv2.contourArea(contour)
    area_list.append(area)
  area_list_sorted=np.sort(area_list)
  trim_percent=0.3
  n=len(area_list_sorted)
  lower_idx=int(n*trim_percent)
  upper_idx=int(n*(1-trim_percent))
  area_list_trimmed=area_list_sorted[lower_idx:upper_idx]
  data=np.array(area_list_trimmed)
  unit_area,std=norm.fit(data)
  print(area_list_trimmed)
  print("unit_area="+str(unit_area))
  
  min_area = unit_area*0.6
  max_area = unit_area*1.4
  
  total1=0
  total2=0
  
  for i,contour in enumerate(contours):
    area = cv2.contourArea(contour)
    x, y, w, h = cv2.boundingRect(contour)
    
    if area<min_area:
      continue
    elif area>max_area:
      count_maybe=int(round(area/unit_area,0))
      cv2.drawContours(img, [contour], -1, (0,0,255), 2)
      cv2.putText(img, str(count_maybe), (int(x+w/2)-10+3, int(y+h/2)+10+3), cv2.FONT_HERSHEY_SIMPLEX, 2, (61,135,38), 5)
      cv2.putText(img, str(count_maybe), (int(x+w/2)-10, int(y+h/2)+10), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 5)
      total2=total2+count_maybe
    else:
      cv2.drawContours(img, [contour], -1, (255,0,0), 2)
      total1=total1+1
    
  cv2.putText(img, "1="+str(total1), (30,100), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 0, 0), 4)
  cv2.putText(img, "2="+str(total2), (30,150), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 4)
  if total1>total2:
    cv2.putText(img, "n="+str(total1+total2), (30,200), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 0, 255), 4)
    cv2.imwrite(out_path, img)
    return total1, total2, out_path
  else:
    return count_and_draw_advanced(in_path, out_path, unit_area)

def count_and_draw_advanced(in_path, out_path, unit_area_ver1):
  
  img = cv2.imread(in_path)
  gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  blur = cv2.GaussianBlur(gray, (7,7), 0)
  ret, thresh = cv2.threshold(blur, 200, 255, cv2.THRESH_BINARY) # 手動閥值 200
  contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  
  # 將極端面積的區域去除(極小面積、極大面積)
  # 計算一顆藥錠的面積 unit_area
  area_list=[]
  for i,contour in enumerate(contours):
    area = cv2.contourArea(contour)
    if area>(unit_area_ver1/5) and area<unit_area_ver1:
      area_list.append(area)
  area_list_sorted=np.sort(area_list)
  trim_percent=0.3
  n=len(area_list_sorted)
  lower_idx=int(n*trim_percent)
  upper_idx=int(n*(1-trim_percent))
  area_list_trimmed=area_list_sorted[lower_idx:upper_idx]
  data=np.array(area_list_trimmed)
  unit_area,std=norm.fit(data)
  print(area_list_trimmed)
  print("unit_area="+str(unit_area))
  
  min_area = unit_area*0.6
  max_area = unit_area*1.4
  
  total1=0
  total2=0
  
  for i,contour in enumerate(contours):
    area = cv2.contourArea(contour)
    x, y, w, h = cv2.boundingRect(contour)
    
    if area<min_area:
      continue
    elif area>max_area:
      count_maybe=int(round(area/unit_area,0))
      cv2.drawContours(img, [contour], -1, (0,0,255), 2)
      cv2.putText(img, str(count_maybe), (int(x+w/2)-10+3, int(y+h/2)+10+3), cv2.FONT_HERSHEY_SIMPLEX, 2, (61,135,38), 5)
      cv2.putText(img, str(count_maybe), (int(x+w/2)-10, int(y+h/2)+10), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 5)
      total2=total2+count_maybe
    else:
      cv2.drawContours(img, [contour], -1, (255,0,0), 2)
      total1=total1+1
    
  cv2.putText(img, "1="+str(total1), (30,100), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 0, 0), 4)
  cv2.putText(img, "2="+str(total2), (30,150), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 4)
  if total2>=total1:
    cv2.putText(img, "should be "+str(total1+total2), (30,200), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 0, 255), 4)
  else:
    cv2.putText(img, "n="+str(total1+total2), (30,200), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 0, 255), 4)
  
  cv2.imwrite(out_path, img)
  
  return total1, total2, out_path

def main(device_index=0, width=1920, height=1080):
    
  cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
  
  if not cap.isOpened():
    print("無法打開攝影機,請確認 device_index 是否正確或裝置是否連接。")
    return
  
  cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
  cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
  scale=0.6
  
  while True:
    ret, frame = cap.read()
    if not ret:
      print("讀取影格失敗,結束。")
      break
    
    resized_frame = cv2.resize(frame, (0, 0), fx=scale, fy=scale)
    cv2.imshow("Camera", resized_frame)
    
    key = cv2.waitKey(1) & 0xFF
    
    if key == ord('q'): # 按 q 鍵離開
      
      break
    
    if key == ord(' '): # 空白鍵
      
      timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
      filename = f"capture_{timestamp}.jpg"
      cv2.imwrite(filename, frame)
      print(f"已儲存影像:{filename}")
      
      in_path=filename
      out_path=filename.replace('.', '_out.')
      total1, total2, out_path = count_and_draw(in_path, out_path)
      print(f"Total1:{total1}. Total2:{total2} Result image saved to {out_path}")
      
      if (total1+total2)<1:
        result = subprocess.run([sys.executable, "speak_chinese.py", "什麼都沒有喔!"], capture_output=True, text=True)
      if total2>=total1:
        open_image(out_path)
        result = subprocess.run([sys.executable, "speak_chinese.py", "請看一下,藥錠應該是 "+str(total1+total2)+" 顆。"], capture_output=True, text=True)
      else:
        open_image(out_path)
        result = subprocess.run([sys.executable, "speak_chinese.py", "太棒了,你有藥錠 "+str(total1+total2)+" 顆。"], capture_output=True, text=True)
  
  cap.release()
  cv2.destroyAllWindows()

if __name__ == "__main__":
    # device_index 預設 0;如果有多個攝影機,改成 1, 2, ...
    main(device_index=0, width=1920, height=1080)

speak_chinese.py
from gtts import gTTS
from pydub import AudioSegment
from pydub.playback import play
import io
import sys

# windows 7 底下
# pip install gtts

if len(sys.argv) > 1:
  
  received_text = sys.argv[1]
  
  tts = gTTS(text=received_text, lang='zh-tw')
  
  fp = io.BytesIO()
  tts.write_to_fp(fp)
  fp.seek(0)
  
  audio = AudioSegment.from_file(fp, format="mp3")
  play(audio)