用Python快速實現一個垃圾分類APP|附帶微信小程序

最近北京開始實行垃圾分類,致使你們對垃圾的研究熱度忽然漲高,垃圾們也紛紛表示歷來沒有得到過這麼高的關注度。其實,上海市去年已經開始實行,網上已經有很多成熟的教程了,像什麼《垃圾分類從入門到精通》、《深刻淺出垃圾分類》、《垃圾分類你應該掌握的10條基本原則》。這種教程若是咱們親自去學顯然不符合程序員的個性,做爲一個程序員,咱們應該把這事兒交給機器來作,這樣才能省下更多的時間投入到996中。前端

扯了這麼多廢話,下面言歸正傳,今天這篇文章主要介紹如何利用現有的工具來實現一個垃圾分類的應用。這個想法是我昨天才有的,今天用了不到一天的時間就完成了,主要作了三個核心內容:程序員

  • 對比現有垃圾分類服務,挑選一個合適並編碼實現
  • 開發桌面版垃圾分類APP
  • 開發垃圾分類微信小程序

上面這三部分第一部分是後端的活兒,其餘兩部分都是前端的活兒,因此,我在這三塊沒有太多經驗,基本上是面向搜索引擎編程。雖然個人主業是作大數據的,但我確實想作這樣一個比較有意思的項目,畢竟一個不會後端的前端不是一個好的大數據工程師。web

老規矩,先看效果圖,PC版:編程

小程序:json

附上小程序二維碼,你們能夠體驗一下。若是打開看不到效果可能審覈沒經過,稍微晚點再開便可。小程序

這篇文章會貼比較多的代碼,而且公衆號閱讀起來不是很方便,因此文末我在文末會附上源碼的獲取方式。(公衆號回覆關鍵字 垃圾分類 便可獲取整篇文章所有源碼)後端

那麼,接下來咱們進入到具體的細節是如何作的。其實垃圾分類已經開始很長一段時間了,確定會有一些服務商把垃圾分類的能力經過API的方式開放出來,供你們調用。我找了3家簡單對比下供你們參考:微信小程序

  • 聚合數據(www.juhe.cn):提供文本、圖像、語音分類。免費調用20次,訂價不靈活只能批量購買
  • 天行數據(www.tianapi.com):提供文本、圖像、語音分類。文本分類5000次,其餘50次,訂價按量計費
  • 京東AI開放平臺:提供文本、圖像、語音分類。免費,每日5000次

簡單對比了圖像分類狀況,聚合和天行數據明顯更好,再綜合訂價因素最終我決定用天行數據。 下面就來編寫代碼,將API接口封裝成咱們須要的服務,以文本(垃圾名稱)分類接口爲例,請求的接口以下api

http://api.tianapi.com/txapi/lajifenlei/index?key=APIKEY&word=眼鏡
複製代碼

APIKEY須要到天行網站註冊來獲取,返回的結果以下:微信

{
  "code":200,
  "msg":"success",
  "newslist":[
    {
      "name":"隱形眼鏡",
      "type":3,
      "aipre":0,
      "explain":"幹垃圾即其它垃圾,指除可回收物、有害垃圾、廚餘垃圾(溼垃圾)之外的其它生活廢棄物。",
      "contain":"常見包括磚瓦陶瓷、渣土、衛生間廢紙、貓砂、污損塑料、毛髮、硬殼、一次性製品、灰土、瓷器碎片等難以回收的廢棄物",
      "tip":"儘可能瀝乾水分;難以辨識類別的生活垃圾均可以投入幹垃圾容器內"
    },
    {
      "name":"眼鏡",
      "type":3,
      "aipre":0,
      "explain":"幹垃圾即其它垃圾,指除可回收物、有害垃圾、廚餘垃圾(溼垃圾)之外的其它生活廢棄物。",
      "contain":"常見包括磚瓦陶瓷、渣土、衛生間廢紙、貓砂、污損塑料、毛髮、硬殼、一次性製品、灰土、瓷器碎片等難以回收的廢棄物",
      "tip":"儘可能瀝乾水分;難以辨識類別的生活垃圾均可以投入幹垃圾容器內"
    },
  ]
}
複製代碼

接口的字段說明你們能夠看官網文檔,這裏我就再也不贅述了。下面來編寫請求文本分類接口的代碼:

import base64
import requests


class TxApiService:
    def __init__(self):
        self.appkey = 'xxx'  # 須要換成本身的
        self.text_cls_url_root = 'https://api.tianapi.com/txapi/lajifenlei/index?key=%s&word=%s'
        self.img_cls_url_root = 'https://api.tianapi.com/txapi/imglajifenlei/index'

    def get_text_cls_res(self, garbage_name):
        url = self.text_cls_url_root % (self.appkey, garbage_name)
        response = requests.get(url)

        res = []
        if response.status_code == 200:
            res_json = response.json()
            if res_json.get('newslist'):
                new_list_json = res_json['newslist']
                for item in new_list_json:
                    name = item.get('name')
                    cat = self.garbage_id_to_name(item.get('type'))
                    tip = item.get('tip')
                    ai_pre = item.get('aipre')
                    pre_type = 'None'
                    if ai_pre == 0:
                        pre_type = '正常結果'
                    if ai_pre == 1:
                        pre_type = '預判結果'
                    item_dict = {'name': name, 'type': cat, 'tip': tip, 'pre_type': pre_type}
                    res.append(item_dict)
                return res
            else:
                return None
        return None

    def garbage_id_to_name(self, id):
        if id == 0:
            return '可回收物'
        if id == 1:
            return '有害垃圾'
        if id == 2:
            return '廚餘垃圾'
        if id == 3:
            return '其餘垃圾'
        return None
複製代碼

代碼比較簡單,用Python的requests庫請求垃圾分類接口,並對返回的數據格式化。 下面再來編寫請求圖像分類的接口

def get_img_cls_res(self, img_base64):
    headers = {
        'Content-Type''application/x-www-form-urlencoded'
    }
    body = {
        'key': self.appkey,
        "img": img_base64,
    }
    response = requests.post(self.img_cls_url_root, headers=headers, data=body)

    res = []
    if response.status_code == 200:
        res_json = response.json()
        if res_json.get('newslist'):
            new_list_json = res_json['newslist']
            for item in new_list_json:
                name = item.get('name')
                cat = self.garbage_id_to_name(item.get('lajitype'))
                tip = item.get('lajitip')
                trust = item.get('trust')
                if trust <= 80:
                    continue
                item_dict = {'name': name, 'type': cat, 'tip': tip, 'pre_score': trust}
                res.append(item_dict)
            return res
        else:
            return None
    return None
複製代碼

函數的參數是圖像的base64編碼,請求方式是POST請求,返回值字段與文本分類略有不一樣,但思路是同樣的。這兩部份內容其實比簡單,這裏我就再也不過多解釋了。

有了數據服務,下面咱們就來開發GUI,這裏我用的是tkinter,用它編寫的APP能夠運行在Linux、Windows和Mac系統,關於tkinter的使用這裏我不會作過多介紹,不瞭解的朋友自行百度,以前我也沒結果過基本上看網上的教程照貓畫虎。 首選,建立GarbageClassificationApp類,來定義用到的各類組件

import base64
import tkinter

from tkinter import *
import hashlib
import time
from tkinter import filedialog

from TxApiService import TxApiService

class GarbageClassificationApp:
    def __init__(self, tk):
        """
        初始化各個組件
        :param tk:
        """

        self.tk = tk

        # 第一行定義文本分類相關的組件
        self.text_cls_label = Label(self.tk, text="垃圾名:")
        self.garbage_name_text = Entry(self.tk)
        self.text_cls_button = \
            Button(self.tk, text="垃圾名分類", bg="lightblue", width=10, height=1, command=self.garbage_name_cls)

        # 第二行定義圖像分類相關的組件
        self.img_cls_label = Label(self.tk, text="垃圾圖片:")
        self.select_file_button = Button(self.tk, text='選擇圖片', command=self.select_pic)
        self.img_cls_button = \
            Button(self.tk, text="圖片分類", bg="lightblue", width=10, height=1, command=self.garbage_img_cls)
        self.img_name_text = Text(self.tk, height=2)
        self.img_name_text.insert(1.0'未選擇圖片:')
        self.img_name_text['state'] = DISABLED

        # 第三行定義輸出結果相關的組件
        self.cls_result_label = Label(self.tk, text="分類結果:")
        self.output_cls_result_list_box = Listbox(self.tk, width=100, height=30)

        # 初始化 api 服務
        self.api_service = TxApiService()

        self.set_init_window()
複製代碼

再來建立set_init_window函數對各個組件進行佈局

# 各組件佈局
def set_init_window(self):
    self.tk.title("垃圾分類")
    self.tk.geometry('1068x681+350+200')  # 1068x681爲窗口大小,+100 +100 定義窗口彈出時的默認展現位置

    # 第一行文本分類各組件的佈局
    self.text_cls_label.grid(row=0, column=0, sticky=E)
    self.garbage_name_text.grid(row=0, column=1)
    self.text_cls_button.grid(row=0, column=2, padx=10)

    # 第二行圖像分類各組件的佈局
    self.img_cls_label.grid(row=1, column=0, sticky=E)
    self.select_file_button.grid(row=1, column=1)
    self.img_cls_button.grid(row=1, column=2, padx=10)
    self.img_name_text.grid(row=1, column=3, padx=10)

    # 第三行輸出結果各組件的佈局
    self.cls_result_label.grid(row=2, column=0, rowspan=2, sticky=E)
    self.output_cls_result_list_box.grid(row=4, column=1, columnspan=10, pady=10, sticky=E)
複製代碼

這樣,界面就完成了。上面定義的一些組件中會有一些事件處理邏輯,好比一個按鈕Button被按下時,它就會調用commond屬性指定的函數。以文本分類Button爲例(text_cls_button),用戶按下該按鈕後,程序就會執行garbage_name_cls函數,在該函數中咱們就能夠請求文本分類服務,並將返回的數據顯示到界面上。代碼以下:

def garbage_name_cls(self):
    garbage_name = self.garbage_name_text.get()
    cat_arr = self.api_service.get_text_cls_res(garbage_name)
    self.output_cls_result_list_box.delete(0, END)

    if cat_arr:
        i = 0
        for item in cat_arr:
            name = '垃圾名稱: %s' % item.get('name''None')
            self.output_cls_result_list_box.insert(i, name)
            i += 1
            cat = '垃圾類別: %s' % item.get('type''None')
            self.output_cls_result_list_box.insert(i, cat)
            i += 1
            pre_type = '預判類型: %s' % item.get('pre_type''None')
            self.output_cls_result_list_box.insert(i, pre_type)
            i += 1
            tip = '投放提示: %s' % item.get('tip''None')
            self.output_cls_result_list_box.insert(i, tip)
            i += 1

            self.output_cls_result_list_box.insert(i, '')
            i += 1
複製代碼

其餘事件處理邏輯相似,代碼以下

def select_pic(self):
    """
    單選圖片
    :return:
    """

    file_name = filedialog.askopenfilename(
        filetypes=[('圖片', ('.png''.jpg''.jpeg'))])
    if file_name:
        self.img_name_text['state'] = NORMAL
        self.img_name_text.delete(1.0, END)
        self.img_name_text.insert(1.0'已選擇圖片:%s' % file_name)
        self.img_name_text['state'] = DISABLED

def garbage_img_cls(self):
    img_name_text = self.img_name_text.get(1.0, END)
    if img_name_text.startswith('已選擇圖片:'):
        file_path = img_name_text[6:].strip()
    else:
        return
    with open(file_path, 'rb'as f:
        base64_data = base64.b64encode(f.read())
        img_base64 = base64_data.decode()
    cat_arr = self.api_service.get_img_cls_res(img_base64)
    self.output_cls_result_list_box.delete(0, END)

    if cat_arr:
        i = 0
        for item in cat_arr:
            name = '垃圾名稱: %s' % item.get('name''None')
            self.output_cls_result_list_box.insert(i, name)
            i += 1
            cat = '垃圾類別: %s' % item.get('type''None')
            self.output_cls_result_list_box.insert(i, cat)
            i += 1
            pre_type = '預測得分: %s' % item.get('pre_score''None')
            self.output_cls_result_list_box.insert(i, pre_type)
            i += 1
            tip = '投放提示: %s' % item.get('tip''None')
            self.output_cls_result_list_box.insert(i, tip)
            i += 1

            self.output_cls_result_list_box.insert(i, '')
            i += 1
複製代碼

至此,PC端桌面APP就開發完成,這裏咱們沒有實現語音分類服務,但思路是同樣的,你們能夠嘗試一下。小程序的代碼我就不貼了,我會一塊兒放到源碼目錄中,在公衆號回覆關鍵字 垃圾分類 便可獲取整篇文章所有源碼。今天開發這個小項目仍是花了很多的時間,文章整理出來已經比較晚了,如今是凌晨1點左右,若是又很差理解的後者須要我深刻講解的你們能夠給我留言。另外,時間比較緊,因此APP作的比較挫,交互也比較差,有任何建議也歡迎你們留言。

歡迎公衆號「渡碼」,輸出別地兒看不到的乾貨。

相關文章
相關標籤/搜索