程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Django REST項目實戰:在線中文字符識別

編輯:Python

我們一起開發在線中文字符識別系統實訓以了解Django REST項目,體會前後端分離開發的思想並掌握基本開發流程。

**01、**RESTful概述

RESTful架構風格最初由Roy T. Fielding(HTTP/1.1協議專家組負責人)在其2000年的博士學位論文中提出。HTTP就是該架構風格的一個典型應用。從其誕生之日開始,它就因其可擴展性和簡單性受到越來越多的架構師和開發者們的青睐。一方面,隨著雲計算和移動計算的興起,許多企業願意在互聯網上共享自己的數據和功能;另一方面,在企業中,RESTful API也逐漸受到重視。時至今日,RESTful架構風格已成為企業級服務的標配。REST即Representational State Transfer的縮寫,譯為“表現層狀態轉化”。REST最大的幾個特點為:資源統一接口URI無狀態。所謂“資源”,就是網絡上的一個實體,或者說是網絡上的一個具體信息,它可以是一段文本、一張圖片、一首歌曲等。資源通過某種載體反應其內容,文本可以用txt格式表現,也可以用HTML格式或XML格式表現,甚至可以采用二進制格式;圖片可以用JPG格式表現,也可以用PNG格式表現;JSON是現在最常用的資源表示格式。

在前面的企業門戶網站實戰項目中,為了能夠動態的顯示頁面內容,使用了Django提供的模板機制,即在前端HTML頁面中嵌入了大量的Django模板標簽,這些標簽並不是HTML的標簽,而是需要通過後台Django服務器對這些標簽進行解析再返回頁面內容給前端。盡管利用Django模板標簽,可以使得後端開發人員比較方便的對前端頁面內容進行控制,但是這種處理方式導致各個模板文件不再是純粹的HTML頁面,而是嵌入了一堆浏覽器無法直接識別的模板標簽,前端設計人員在不熟悉Django的情況下無法對這些內容進行設計和修改。目前,很多大型Web項目的開發往往是采用一種前後端分離的合作方式,前端設計人員專注於頁面和交互功能的實現,通過HTML、CSS和JS即可在浏覽器端進行設計並且查看效果。後端開發人員僅處理前端發來的各種請求,並返回各請求對應的內容即可,其中後端開發人員不再需要關注頁面的設計,而是通過雙方約定好的API接口協議進行資源上傳和接收。這種前後端分離、僅通過內容交換實現的Web架構即為REST。

在前後端分離的應用模式中,後端僅返回前端所需要的數據,不再渲染HTML頁面,不再控制前端的效果。前端用戶看到什麼效果、從後端請求的數據如何加載到前端中,這些都由前端決定。例如網頁有網頁的設計方式,手機APP有手機APP的處理方式,但無論哪種前端其所需要的數據基本相同,後端僅需開發同一套邏輯對外提供資源數據即可。

02、搭建框架

我們將采用Django來開發一個基於RESTful風格的項目實例:中文字符識別。

我們首先完成基礎框架搭建。新建一個ocr文件夾用於存放項目,在該文件夾下分別建立三個子文件夾frontend、ocr和app,其中frontend文件夾用於存放前端文件,包括html、css、js和img文件等,ocr文件夾用於存放項目的配置文件,app文件夾作為應用文件夾用於存放每個獨立應用文件。參照Django項目目錄結構,在各子文件夾下創建一些空文件和空文件夾,完整結構如圖1所示。

▍圖 1 項目文件結構圖

接下來將單文件Django項目的各模塊內容填入到指定的文件中。打開settings.py文件,添加代碼如下:

import?os
#?設置項目根目錄
BASE_DIR?=?os.path.dirname(os.path.dirname(os.path.abspath(__file__)))?
#?加密簽名
SECRET_KEY?=?'b!iohd&[email protected]@k9t01_k^[email protected])1fnez^8l'??????
DEBUG?=?True?????????????????#?設置當前為調試模式
INSTALLED_APPS?=?['app']???#?添加應用
ROOT_URLCONF?=?'ocr.urls'??#?設置項目路由文件urls

述代碼將原Django項目中的必要部分剝離出來,旨在能夠建立更輕量更易於理解的Django項目。打開app應用下的views.py文件,添加代碼:

from?django.http?import?HttpResponse
def?home(request):
????return?HttpResponse('Hello?World')

通過導入的HttpResponse函數響應前端,返回內容為一個字符串。在urls.py文件中添加路由:

from?django.urls?import?path
from?app.views?import?home
urlpatterns?=?[path('',?home,?name='home')]

上述代碼將訪問根路徑與視圖home函數進行綁定。最後,在項目根目錄下的manage.py文件中添加運行代碼:

if?__name__?==?'__main__':
????import?sys
????import?django
????import?os
????DJANGO_SETTINGS_MODULE?=?'ocr.settings'
????#?設置環境變量
????os.environ.setdefault('DJANGO_SETTINGS_MODULE',?'ocr.settings')
????django.setup()
????from?django.core.management?import?execute_from_command_line
????execute_from_command_line(sys.argv)

其中注意,我們將所有的配置全部放置在單文件腳本中,使用settings.configure(**settings)來加載配置項,此處我們將所有的配置項放置在了獨立的settings.py文件中。為了能夠加載該配置文件,需要采用django.setup()函數進行設置,該函數會自動查詢環境變量DJANGO_SETTINGS_MODULE的值,把這個值作為配置文件的路徑。保存所有修改後,在終端中運行命令:

python?manage.py?runserver

然後打開浏覽器訪問127.0.0.1:8000,查看頁面是否輸出對應的字符串“Hello World”。

03、前端開發

我們擬實現一個在線中文字符識別系統,用戶在網頁上上傳圖片,然後通過Ajax技術將圖片傳輸至後台服務器,後台服務器調用中文字符識別算法將圖片中的文字識別出來,並以JSON字符串的形式返回結果給前端頁面進行顯示。整個開發過程分為前端和後端,後端不再使用Django提供的模板機制來控制前端頁面的執行邏輯,前後端之間所有的交互全部通過API接口進行。由於采用了前後端分離的機制,因此,前端開發人員可以使用純HTTP和JS來開發頁面和交互邏輯,並且能夠在不借助後端的情況下運行頁面查看效果。本小節先進行前端開發。

前端所有的開發文件全部放置在frontend文件夾中。為了程序美觀,本實例依然采用Bootstrap框架設計頁面。將Bootstrap包中的bootstrap.min.css、jquery.min.js和bootstrap.min.js文件按照文件類型分別放置在frontend/css和frontend/js文件夾中,然後在img文件夾下放置一張名為sample.jpg的圖片文件用於展示圖像顯示區域。在css文件夾下額外新建一個空的style.css文件,該文件將作為本實例的個性化樣式定制文件使用。

接下來開始編輯前端頁面index.html。首先設置頁面標題title和元信息meta,然後在頁面頭部引入必要的css和js文件:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>在線中文字符識別</title>
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<script src="js/jquery.min.js"></script>
<script src="js/bootstrap.min.js"></script>
</head>
<body>
</body>
</html>

在頁面部分采用Bootstrap柵格結構進行布局,主要分為左右兩部分,各占6個柵格。左側用來上傳待識別的圖像並顯示,右側用來顯示識別結果。詳細代碼如下:

<div?class="container">
????<!--?標題?-->
????<div?class="row">
????????<div?class="col-lg-12">
????????????<p?class="text-center?h1">
????????????????在線中文字符識別
????????????</p>
????????</div>
????</div>
????<!--?分隔符?-->
????<div?class="hr">
????????<hr?/>
????</div>
????<!--?主體內容?-->
????<div?class="row">
????????<br>
????????<!--?圖片上傳?-->
????????<div?class="col-md-6">
????????????<img?id="photoIn"?src="img/sample.jpg"?class="img-responsive">
????????????<input?type="file"?id="photo"?name="photo"?/>
????????</div>
????????<!--?運行結果?-->
????????<div?class="col-md-6">
????????????<div?class="col-md-12">
????????????????<textarea?id="output"?disabled?class="form-control"?rows="5">
????????????????????</textarea>
????????????</div>
????????????<br>
????????????<div?class="col-md-12">
????????????????<p?class="text-center?h4">識別結果</p>
????????????</div>
????????</div>
????</div>
????<br>
????<div?class="row">
????????<div?class="text-center">
????????????<button?type="button"?id="recognition"?class="btn?btn-primary">
識別</button>
????????</div>
????</div>
</div>

在style.css文件中添加分割線對應的樣式設計:

div.hr?{
????height:?3px;
????background:?#818080;
}
div.hr?hr?{
????display:?none;
}

為了能夠實現圖像浏覽和上傳的功能,需要使用js來實現。具體的,在部分末尾添加代碼:

<script>
????$(function?()?{
????????$('#photo').on('change',?function?()?{
????????????var?r?=?new?FileReader();
????????????f?=?document.getElementById('photo').files[0];
????????????r.readAsDataURL(f);
????????????r.onload?=?function?(e)?{
????????????????document.getElementById('photoIn').src?=?this.result;
????????????};
????????});
????});
</script>

接下來,在前端頁面中繼續添加代碼完成圖像向後端的傳輸以及獲取到後端發來的結果後的顯示處理:

<!--?圖像發送至後台服務器進行識別?-->
<script>
????$('#recognition').click(function?()?{
????????formdata?=?new?FormData();
????????var?file?=?$("#photo")[0].files[0];
????????formdata.append("image",?file);
????????$.ajax({
????????????url:?'/ocr/',?????????//?調用Django服務器計算函數
????????????type:?'POST',?????????//?請求類型
????????????data:?formdata,
????????????dataType:?'json',?????//?期望獲得的響應類型為json
????????????processData:?false,
????????????contentType:?false,
????????????success:?ShowResult???//?在請求成功之後調用該回調函數輸出結果
????????})
????})
</script>
<!--?返回結果顯示?-->
<script>
????function?ShowResult(data)?{
????????output.value?=?data['output'];
????}
</script>

圖像的傳輸采用了Ajax技術,當用戶單擊“識別”按鈕時將圖像數據封裝到formdata變量並發送至後端,發送地址為’/ocr/',發送方式為POST。收到結果後執行ShowResult函數,將輸出文本的值改為識別到的文字信息。

保存所有修改後,用浏覽器直接打開index.html頁面,單擊“浏覽”按鈕,選擇一張待識別的圖片進行上傳,可以看到選擇的圖片顯示在指定位置,如圖2所示。

▍圖2前端開發效果圖

到這裡我們發現整個的前端設計和開發不再依賴後端服務器,並且由於頁面沒有嵌入Django模板標簽,因此可以直接被浏覽器解析和運行。這種前後端分離的開發模式可以極大的提高團隊開發人員的溝通效率,使得項目的協同合作更加方便。

04、後端開發

我們繼續將對後端進行開發。一般情況下,采用前後端分離機制以後前端靜態資源(html頁面、css樣式文件、jpg圖片等)會采用額外的前端服務器來提供靜態文件服務。我們為了簡化服務器的搭建和使用,依然使用Django來提供靜態文件服務,將所有的靜態資源例如css、js和jpg圖片文件等按照文件夾路徑創建對應的視圖處理函數,以文件讀取方式獲取文件內容並通過HttpResponse返回。

在views.py文件中添加代碼如下:

def?read_css(request,?filename):
????with?open('frontend/css/{}'.format(filename),?'rb')?as?f:
????????css_content?=?f.read()
????print('css文件')
????return?HttpResponse(content=css_content,?content_type='text/css')
def?read_js(request,?filename):
????with?open('frontend/js/{}'.format(filename),?'rb')?as?f:
????????js_content?=?f.read()
????print('js文件')
????return?HttpResponse(content=js_content,
????????????????????????content_type='application/JavaScript')
def?read_img(request,?filename):
????with?open('frontend/img/{}'.format(filename),?'rb')?as?f:
????????img_content?=?f.read()
????print('img文件')
????return?HttpResponse(content=img_content,?content_type='image/jpeg')

上述代碼分別創建js、css和jpg文件訪問的視圖處理函數,然後在urls.py文件中設置訪問路由:

from?app.views?import?read_css,?read_js,?read_img
urlpatterns?=?[
????...其他路由...
????path('css/<str:filename>',?read_css,?name='read_css'),
????path('js/<str:filename>',?read_js,?name='read_js'),
????path('img/<str:filename>',?read_img,?name='read_img'),
]

通過這種方式,可以使得在不改變前端代碼的情況下能夠正確的提供靜態資源請求服務。重新編輯views.py中的home函數:

def?home(request):
????with?open('frontend/index.html','rb')?as?f:
????????html?=?f.read()
????return?HttpResponse(html)

同樣的,html頁面也以文件讀取方式獲取內容並通過HttpResponse返回。保存所有修改後啟動項目:

python?manage.py?runserver

打開浏覽器查看頁面效果。可以發現頁面效果與使用浏覽器直接打開index.html頁面相同。這說明,後端服務器正確的充當了靜態資源服務器的角色,在不使用Django模板標簽的情況下能夠實現前端頁面的正確渲染。

最後需要開發中文字符識別對應的Ajax視圖處理函數。為了實現中文字符識別,我們采用開源庫Tesseract-OCR來進行文字識別任務。Tesseract是惠普布裡斯托實驗室在1985~1995年間開發的一個開源的字符識別引擎,曾經在1995 UNLV精確度測試中名列前茅。2005年,惠普將其對外開源。2006年由Google對Tesseract進行改進並對其進行深度優化。

Tesseract的下載網址為:

https://digi.bib.uni-mannheim.de/tesseract/。根據系統版本進行選擇,如果使用Windows 64位系統,可以下載windows 64對應的版本:tesseract-ocr-w64-setup-v4.1.0.20190314.exe。下載完成後雙擊進入安裝界面,展開Additional language data,勾選arabic和Chinese simplified使得能夠同時支持阿拉伯數字和簡體中文字符的識別。

▍圖 3 Tesseract-OCR安裝界面

為了能夠在Python中使用該引擎庫,需要安裝對應的Python庫:

pip?install?pytesseract

然後修改pytesseract庫文件,在pytesseract安裝包中找到pytesseract.py文件,修改tesseract_cmd字段的值,將tesseractOCR的安裝目錄填入其中:

tesseract_cmd?=?r'<tesseractOCR安裝目錄> esseract.exe'

通過上述修改,就可以使得Python能夠找到本地的文字識別程序完成識別。接下來在views.py文件中添加視圖處理函數,完整代碼如下:

import?numpy?as?np????
import?urllib?????????
import?numpy?as?np????
import?urllib?????????
import?json??????????
import?cv2??????????
import?pytesseract
from?PIL?import?Image
import?os??
from?django.views.decorators.csrf?import?csrf_exempt?
from?django.http?import?JsonResponse??
def?read_image(stream=None):
????data_temp?=?stream.read()
????image?=?np.asarray(bytearray(data_temp),?dtype="uint8")
????image?=?cv2.imdecode(image,?cv2.IMREAD_COLOR)
????return?image
@csrf_exempt??#?用於規避跨站點請求攻擊
def?ocrDetect(request):
????result?=?{"code":?None}??
????if?request.method?==?"POST":
????????if?request.FILES.get("image",?None)?is?not?None:??
????????????img?=?read_image(stream=request.FILES["image"])
????????#?OpenCV轉PIL
????????img?=?Image.fromarray(cv2.cvtColor(img,?cv2.COLOR_BGR2RGB))
????????#?執行識別
????????code?=?pytesseract.image_to_string(img,?lang='chi_sim')
????????result.update({"output":?code})
????return?JsonResponse(result)

通過客戶端浏覽器上傳的圖像照片進行識別處理然後返回結果,不同之處在於此處返回的結果是以JSON字符串形式給出,不需要再額外的進行圖像編碼。識別部分主要采用pytesseract.image_to_string函數進行識別,其中lang='chi_sim’表示當前識別中文簡體字符。

最後,在urls.py文件的urlpatterns字段中添加對應的路由:

path('ocr/',?ocrDetect,?name='ocrDetect'),??#?在線中文字符識別api

完成所有修改後保存並運行項目,最終識別效果如圖4所示。

▍圖 4 中文字符識別效果圖

經過測試,tesseract—OCR對於印刷體中文字符效果較好,對於手寫體中文字符效果一般。如果需要更高的檢測精度和更好的適應性,則需要進一步優化算法。

先自我介紹一下,小編13年上師交大畢業,曾經在小公司待過,去過華為OPPO等大廠,18年進入阿裡,直到現在。深知大多數初中級java工程師,想要升技能,往往是需要自己摸索成長或是報班學習,但對於培訓機構動則近萬元的學費,著實壓力不小。自己不成體系的自學效率很低又漫長,而且容易碰到天花板技術停止不前。因此我收集了一份《java開發全套學習資料》送給大家,初衷也很簡單,就是希望幫助到想自學又不知道該從何學起的朋友,同時減輕大家的負擔。添加下方名片,即可獲取全套學習資料哦


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved