
收到一個工作請求,是要自動解釋網頁的 Captcha 內容。之前做過一點影像分析的研究,對於 Captcha 的解碼也感到興趣,嘗試用自己的方法處理。初步已把文字位置鎖定,但是對於文字的讀取,還是未有甚麼頭緒...。
My studies, development and creations on Robotics, iPhone Apps, Andriod Apps,...etc.
-- アクションバンク位置戻す用スクリプト setPhase(9); --changeAnime( 5, 0, 0); -- 待機ポーズ --changeAnime( 5, 1, 100); -- 待機ポーズ setDisp(8, 0, 0); setDisp(8, 1, 0); setMoveKey( -7, 0, 0, 0); -- キーフ -- 死亡 (通常) setPhase(9); ATK_dead = 0; ATK_end = ATK_dead+160; -- エフェクト全消し removeAllEffect(0); --[[ entryEffect( 1, 1703, 0, -1, 0, 0, 0); --ガラス playSe( 1, 1041);--SE playSe( ATK_dead, 1040);--SE playSe( 2, 1054) -- 死亡 (最終) setPhase(9); ATK_dead = 0; ATK_end = ATK_dead+160; -- エフェクト全消し removeAllEffect(0); --entryEffect( 1, 1703, 0, -1, 0, 0, 0); --ガラス --playSe( 1, 1041);--SE --playSe( ATK_dead, 1040);--SE --playSe( 2, 10 print ("[lua]exec a0003"); ATK_01 = 0; ATK_02 = ATK_01+86; ATK_03 = ATK_02+12; ATK_04 = ATK_03+20; ATK_05 = ATK_04+26; ATK_06 = ATK_05+15; ATK2_01 = ATK_06+5; ATK2_02 = ATK2_01+4; ATK2_03 = ATK2_02+4; ATK2_04 = ATK2_03+4; ATK2_05 = ATK2_04+5; print ("[lua]exec a0012"); ATK_01 = 12; ATK_02 = ATK_01+46; ATK_03 = ATK_02+4; ATK_end = ATK_03+20; ---------------------------------------------- --パンチの応手鵜 setEnvZoomEnable( ATK_01,1);--ズーム fcolor_r = 245; fcolor_g = 245; fcolor_b = 245; SP_ATK_0 = 6; SP_ATK_1 = SP_ATK_0+10; SP_ATK_2 = SP_ATK_1+63; SP_ATK_3 = SP_ATK_2+92; SP_ATK_4 = SP_ATK_3+45; SE_01 = 1035; --気を貯める SE_02 = 1036; --気が広がる SE_03 = 1036;
$ ffmpeg -loop 1 -f image2 -i visit_us.jpg -r 30 -t 4 visit_us.mp4
ffmpeg -i video.mov -c copy -t 00:00:30.0 video_30s.mp4
ffmpeg -i video.mov -strict -2 -t 00:00:30.0 video_30s.mp4
ffmpeg -i video.mov -strict -2 -vcodec libx264 -preset slower -s 640x360 video_640x360.mp4
sudo apt-get update && sudo apt-get upgrade完成後安裝 Nginx:
sudo apt-get install nginx啟動 Nginx:
sudo /etc/init.d/nginx start
*filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -m comment --comment "SFTP port" -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -m comment --comment "Apache WEB server port" -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 3306 -s 192.168.1.1 -m comment --comment "MySQL server port" -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 8080 -m comment --comment "Tomcat Server port" -j ACCEPT -A INPUT -j REJECT --reject-with icmp-host-prohibited -A FORWARD -j REJECT --reject-with icmp-host-prohibited COMMIT之後簡單寫了一個 JSP 測試以 JNDI 方式讀取 MySQL 內的數據:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@page import="com.mysql.*,java.util.*,javax.naming.*,java.sql.*,javax.sql.*" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <h1>JSP MySQL Connection Test</h1> <% Context context = new InitialContext(); String lookup = "java:comp/env/jdbc/MySQLDS"; DataSource dataSource = (DataSource)context.lookup(lookup); Connection connection = dataSource.getConnection(); Statement statement = connection.createStatement(); String query = "SELECT * FROM databaseName.tableName"; ResultSet resultSet = statement.executeQuery(query); while (resultSet.next()) { out.println(resultSet.getString(1)); out.println(resultSet.getString(2)); out.println("<br>"); } %> </body> </html>
# cd /tmp # wget http://apache.communilink.net/tomcat/tomcat-7/v7.0.59/bin/apache-tomcat-7.0.59.tar.gz # tar xzf apache-tomcat-7.0.59.tar.gz # mv apache-tomcat-7.0.59 /usr/local/tomcat7啟動 Tomcat:
# /usr/local/tomcat7/bin/startup.sh關閉 Tomcat:
# /usr/local/tomcat7/bin/shutdown.sh由於 Tomcat 用上 8080 埠,因此記得要在 /etc/sysconfig/iptables 進行開通:
*filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -m comment --comment "SFTP port" -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -m comment --comment "Apache WEB server port" -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 3306 -s 192.168.1.1 -m comment --comment "MySQL server port" -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 8080 -m comment --comment "Tomcat Server port" -j ACCEPT -A INPUT -j REJECT --reject-with icmp-host-prohibited -A FORWARD -j REJECT --reject-with icmp-host-prohibited COMMIT最後,還要設定用戶權限。
# vi /usr/local/tomcat7/conf/tomcat-users.xml
<role rolename="programmers" /> <user username="username" password="password" roles="rssbus_appuser,rssbus_admin,admin-gui,manager-gui,manager-status,manager-script,manager-jmx,programmers" />
vi /etc/sysconfig/iptables把內容改為:
# Generated by iptables-save v1.4.7 on Mon Feb 16 13:15:58 2015 *filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -m comment --comment "SFTP port" -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -m comment --comment "Apache WEB server port" -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 3306 -s 192.168.1.10 -m comment --comment "MySQL server port" -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 8080 -m comment --comment "Tomcat Server port" -j ACCEPT -A INPUT -j REJECT --reject-with icmp-host-prohibited -A FORWARD -j REJECT --reject-with icmp-host-prohibited COMMIT # Completed on Mon Feb 16 13:15:58 2015留意所有 ACCEPT 的句子都要放在 REJECT 之上。不然句子會變成無效。
wpa_passphrase "Your Hidden SSID" 'Your Wireless LAN Password'這樣就會看差不多的輸出:
network={ ssid="Your Hidden SSID" #psk="Your Wireless LAN Password" psk= 8888ce8df8f88cdb888cdeceebcd88da8de8888888f88aa888cbd8888888888b }之後便是檢查無線隱藏網絡的資料。在終端機輸入:
sudo iwlist wlan0 scan會看到差不多的輸出:
Cell 04 - Address: 66:33:44:88:00:22 ESSID:"要留意的是,有些無線網絡的 Group Cipher 及 Pairwise Ciphers 是 TKIP。而我的 Apple AirPort 則是 CCMP。如果是 TKIP 的話,則把以下「CCMP」的字眼改為「TKIP」即可。現在正式進入設定的工作。在終端機輸入:" Protocol:IEEE 802.11bgn Mode:Master Frequency:2.462 GHz (Channel 11) Encryption key:on Bit Rates:1 Mb/s; 2 Mb/s; 5.5 Mb/s; 11 Mb/s; 6 Mb/s 9 Mb/s; 12 Mb/s; 18 Mb/s; 24 Mb/s; 36 Mb/s 48 Mb/s; 54 Mb/s Extra:rsn_ie=00000000000abc000000000abc000000000abc000000 IE: IEEE 802.11i/WPA2 Version 1 Group Cipher : CCMP Pairwise Ciphers (1) : CCMP Authentication Suites (1) : PSK Signal level=100/100
sudo nano /etc/network/interfaces然後把有關 wlan0 的內容改為:
auto wlan0 allow-hotplug wlan0 iface wlan0 inet dhcp wpa-scan-ssid 1 wpa-ap-scan 1 wpa-key-mgmt WPA-PSK wpa-proto RSN WPA wpa-pairwise CCMP TKIP wpa-group CCMP TKIP wpa-ssid "Your Hidden SSID" wpa-psk 8888ce8df8f88cdb888cdeceebcd88da8de8888888f88aa888cbd8888888888b儲存後重新啟動 Raspberry Pi 就能成功連線。
MacBook-Pro-Pacess:~ pacess$ sudo diskutil unmount /dev/disk2s1 Password: Volume RPI_2 on disk2s1 unmounted MacBook-Pro-Pacess:~ pacess$ cd Desktop/ MacBook-Pro-Pacess:Desktop pacess$ sudo dd bs=1m if=pi-snappy.img of=/dev/rdisk2 2861+1 records in 2861+1 records out 3000000000 bytes transferred in 296.119930 secs (10131030 bytes/sec) MacBook-Pro-Pacess:Desktop pacess$把 Micro SD 卡插入 Raspberry Pi 2 後開機,同樣出現一張 RGB 色階圖,今次等候的時間比以往長。出現四顆 Raspberry 圖案一會兒會後便跳到登入的終端,沒有了過去一大堆的載入資料。以 ubuntu 名稱及 ubuntu 密碼登入。輸入 rasp-config 卻出現錯誤。
ubuntu@localhost:~$ sudo rasp-config sudo: rasp-config: command not found ubuntu@localhost:~$看看 Apache2 是否已被執行:
ubuntu@localhost:/$ service apache2 status ● apache2.service Loaded: not-found (Reason: No such file or directory) Active: inactive (dead) ubuntu@localhost:/$似乎跟我認識的環境很不一樣。用 MacBook 以 SSH 登入呢,反而成功了。LAN 沒有設定便能正常運作,這點較易入手。由於 apt-get 沒有了,改為 snappy,嘗試更新一下系統:
ubuntu@localhost:/etc$ snappy update-versions Traceback (most recent call last): File "/usr/bin/snappy", line 25, in那麼看看目前版本呢?status = Main().__main__() File "/usr/lib/python3/dist-packages/snappy/main.py", line 195, in __main__ return callback(args) File "/usr/lib/python3/dist-packages/snappy/main.py", line 292, in _do_update_versions ClickDataSource().versions()): File "/usr/lib/python3/dist-packages/snappy/click.py", line 189, in versions all_updates_list = repo.get_upgradable() File "/usr/lib/python3/dist-packages/click/repository.py", line 183, in get_upgradable headers={"content-type": "application/json"}) File "/usr/lib/python3/dist-packages/click/network.py", line 70, in http_request curl.perform() pycurl.error: (60, 'server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none') ubuntu@localhost:/etc$
ubuntu@localhost:/etc$ snappy versions Traceback (most recent call last): File "/usr/bin/snappy", line 25, in又死...。看來 Ubuntu 還沒有準備好...。status = Main().__main__() File "/usr/lib/python3/dist-packages/snappy/main.py", line 195, in __main__ return callback(args) File "/usr/lib/python3/dist-packages/snappy/main.py", line 334, in _do_versions click_versions = ClickDataSource().versions(all) File "/usr/lib/python3/dist-packages/snappy/click.py", line 189, in versions all_updates_list = repo.get_upgradable() File "/usr/lib/python3/dist-packages/click/repository.py", line 183, in get_upgradable headers={"content-type": "application/json"}) File "/usr/lib/python3/dist-packages/click/network.py", line 70, in http_request curl.perform() pycurl.error: (60, 'server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none') ubuntu@localhost:/etc$
ubuntu@localhost:~$ sudo date -s "Thu Feb 12 22:06:00 UTC 2015" Thu Feb 12 22:06:00 UTC 2015 ubuntu@localhost:~$ sudo snappy versions Part Tag Installed Available Fingerprint Active ubuntu-core edge 2 - f442b1d8d6db3f * webdm edge 0.1 - 1604c8b7c9f6c5 * ubuntu@localhost:~$
#!/usr/bin/env python ##------------------------------------------------------------------------------ ## Raspberry Pi Remote Camera ## Written by Pacess HO ## Copyright (c) 2015 Pacess Studio. All rights reserved. ##------------------------------------------------------------------------------ import picamera import datetime import time import json ##============================================================================== with picamera.PiCamera() as camera: # Prepare output filename today = time.time() dateObject = datetime.datetime.fromtimestamp(today) filename = dateObject.strftime("%Y%m%d%H%M%S")+".jpg" filePath = "./_files/"+filename # Prepare camera camera.resolution = (640, 480) camera.rotation = 180 camera.start_preview() # Camera warm-up time time.sleep(2) camera.capture(filePath) # Prepare for output response = {"apiName": "capture.py", "authToken": "", "message": "Done", "result":filePath, "status":0} print("Content-type: application/json") print("") print(json.JSONEncoder().encode(response))完成後先在瀏覽器試一試 http://raspberrypi.local/capture.py。竟然是顯示了 capture.py 內整份內容。要令 Apache2 執行 Python 程式,需要做一點設定。在終端機執行:
sudo nano /etc/apache2/sites-enabled/000-default把 <Directory /var/www/> 內加入以下紅字:
<Directory /var/www/> Options Indexes FollowSymLinks MultiViews +ExecCGI AllowOverride None Order allow,deny allow from all AddHandler cgi-script .py </Directory>儲存後回到終端機執行:
sudo service apache2 reload回到瀏覽器再試。今次出現了「Internal Server Error」:
tail -f /var/log/apache2/error.log發現問題是:
malformed header from script. Bad header=* failed to open vchiq instanc: capture.py解決方法是執行:
sudo chmod o+rwx /dev/vchiq再次回到瀏覽器,今次成功了!有了拍攝的程式,下一步便是網頁介面。我利用了上星期學到的 Bootstrap 來開發:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="apple-mobile-web-app-status-bar-style" content="black" /> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="format-detection" content="telephone=no" /> <title>Raspberry Pi Remote Camera</title> <!-- Bootstrap core CSS --> <link rel="stylesheet" href="/_css/bootstrap.min.css"> <!-- Custom styles for this page --> <link rel="stylesheet" href="/_css/main.css" type="text/css"> <!-- Custom font --> <link href='http://fonts.googleapis.com/css?family=Cookie' rel='stylesheet' type='text/css'> <!-- Custom styles for this template --> <link rel="stylesheet" href="/_css/main.css" type="text/css"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <script src="/_js/main.js"></script> </head> <body> <div id="divTitle">Raspberry Pi Remote Camera</div> <div class="divBody"> <img src="/_files/capture.jpg" id="previewImage" /><br> <button type="button" class="btn btn-warning" onclick="capture();">Capture</button> </div> <!-- Bootstrap core JavaScript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script src="/_js/bootstrap.min.js"></script> </body> </html>接著便是網頁的 Javascript 程式,負責跟 Raspberry Pi 的 capture.py 溝通:
//------------------------------------------------------------------------------ // Raspberry Pi Remote Camera // Written by Pacess HO // Copyright (c) 2015 Pacess Studio. All rights reserved. //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // App constants //------------------------------------------------------------------------------ var API = { CAPTURE: "capture.py", }; var REQUEST_METHOD = { GET: "GET", POST: "POST" }; var HTTP_STATUS = { OK: 0, ERROR_INVALID_AUTH_TOKEN: 10, ERROR_PARAMETER_NOT_FOUND: 20, ERROR_USER_ALREADY_EXISTS: 30, ERROR_USER_UPDATE_FAILED: 31, ERROR_ACCESS_DENIED: 80, ERROR_UNKNOWN: 99, // HTTP Status ERROR_FILE_NOT_FOUND: 404, ERROR_INTERNAL_SERVER_ERROR: 500 }; //------------------------------------------------------------------------------ // AJAX related functions //------------------------------------------------------------------------------ function makeParameterString(parameters) { var keyValues = []; for (var key in parameters) { if (parameters.hasOwnProperty(key)) { keyValues.push(key + "=" + parameters[key]); } } return keyValues.join("&"); } //------------------------------------------------------------------------------ function ajaxPost(urlString, parameterString, callback) { ajaxRequest(urlString, REQUEST_METHOD.POST, parameterString, callback) } //------------------------------------------------------------------------------ function ajaxRequest(urlString, requestMethod, parameterString, callback) { // Create new request var request = new XMLHttpRequest(); request.onreadystatechange = function() { if (request.readyState == 4) { switch (request.status) { default: { var httpStatus = request.status; var jsonObject = { timeStamp: 0, apiName: urlString.split("/").pop(), status: httpStatus, authToken: 0, results: "發生錯誤,請稍後再試 ("+exitValue+")" }; var jsonString = JSON.stringify(jsonObject); callback(jsonString); } break; case 200: { callback(request.responseText); } break; } } }; // Send the request now if (requestMethod == REQUEST_METHOD.GET) { urlString = urlString + "?" + parameterString; request.open(requestMethod, urlString, true); request.send(); return; } if (requestMethod == REQUEST_METHOD.POST) { request.open(requestMethod, urlString, true); request.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); request.send(parameterString); return; } } //------------------------------------------------------------------------------ function connectionResponse(data) { var jsonObject = data; if (data == undefined) {alert("Undefined"); return;} if (data == "") {alert("Empty"); return;} if (typeof(data) == "string") {jsonObject = jQuery.parseJSON(data);} // Fetch value from the JSON object _authToken = jsonObject.authToken; var apiName = jsonObject.apiName; var message = jsonObject.message; var result = jsonObject.result; var status = jsonObject.status; switch (status) { // 404 = File not found case HTTP_STATUS.ERROR_FILE_NOT_FOUND: alert(message); break; // 500 = Internal server error case HTTP_STATUS.ERROR_INTERNAL_SERVER_ERROR: alert(message); break; case HTTP_STATUS.ERROR_INVALID_AUTH_TOKEN: case HTTP_STATUS.ERROR_PARAMETER_NOT_FOUND: case HTTP_STATUS.ERROR_ACCESS_DENIED: case HTTP_STATUS.ERROR_UNKNOWN: { alert(message); } return; } //------------------------------------------------------------------------------ switch (apiName) { case API.CAPTURE: { var filePath = result; $("#previewImage").attr("src", filePath); } break; } } //------------------------------------------------------------------------------ // Custom functions //------------------------------------------------------------------------------ function capture() { // Connect now var apiPath = API.CAPTURE; ajaxPost(apiPath, null, connectionResponse); }這時,所有程式已經準備好,執行看看。成功能令 Raspberry Pi 進行指攝並傳回最新圖像!
pi@raspberrypi ~ $ sudo apt-get install apache2 php5 libapache2-mod-php5 Reading package lists... Done Building dependency tree Reading state information... Done The following extra packages will be installed: apache2-mpm-prefork apache2-utils apache2.2-bin apache2.2-common libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap libonig2 libqdbm14 lsof php5-cli php5-common ssl-cert Suggested packages: apache2-doc apache2-suexec apache2-suexec-custom php-pear openssl-blacklist The following NEW packages will be installed: apache2 apache2-mpm-prefork apache2-utils apache2.2-bin apache2.2-common libapache2-mod-php5 libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap libonig2 libqdbm14 lsof php5 php5-cli php5-common ssl-cert 0 upgraded, 17 newly installed, 0 to remove and 0 not upgraded. Need to get 7493 kB of archives. After this operation, 22.4 MB of additional disk space will be used. Do you want to continue [Y/n]? Y為了學習更多 Raspberry Pi 及 Python 的東西,我想出了一個項目。這個項目需要用上 Apache。而 NOOBS 預設沒有帶 Apache,需要另外安裝。在終端輸入「sudo apt-get install apache2 php5 libapache2-mod-php5」。等候安裝完成輸入「sudo reboot」重啟一次。最後還要把網頁的根目錄設定權限「sudo chown -R pi /var/www」。大功告成!
pi@octopi ~ $ raspistill -v raspistill Camera App v1.3.4 Width 2592, Height 1944, quality 85, filename (null) Time delay 5000, Raw no Thumbnail enabled Yes, width 64, height 48, quality 35 Link to latest frame enabled no Full resolution preview No Capture method : Single capture Preview Yes, Full screen Yes Preview window 0,0,1024,768 Opacity 255 Sharpness 0, Contrast 0, Brightness 50 Saturation 0, ISO 0, Video Stabilisation No, Exposure compensation 0 Exposure Mode 'auto', AWB Mode 'auto', Image Effect 'none' Metering Mode 'average', Colour Effect Enabled No with U = 128, V = 128 Rotation 0, hflip No, vflip No ROI x 0.000000, y 0.000000, w 1.000000 h 1.000000 mmal: mmal_vc_component_enable: failed to enable component: ENOSPC mmal: camera component couldn't be enabled mmal: main: Failed to create camera component mmal: Failed to run camera app. Please check for firmware updates