2016年11月30日 星期三

Raspberry Pi 的 Webcam 影像移位重疊問題


早幾天,我用 Python 編寫了一個偵測移動物的程式。它是在 Raspberry Pi 上配合 Webcam 執行。當發現移動物時,除了把影像儲存外,還會經電郵傳送給我,好讓我知道家中的情況。可是,我經常收到錯誤的警報。原因是 Webcam 傳到 Python 的影像中,有時會上一幀跟下一幀移位重疊。由於動作偵測是以畫面變化來作準,移位重疊令到這個準則成立,觸發誤報。起初以為是 Python 對於處理記憶體物件是以指針來處理,程式工作得慢而導致下一幀已寫進記憶體,可是改用 .copy() 後問題依舊,似乎是鏡頭或是驅動程式的問題。有待解決。
##----------------------------------------------------------------------------------------
##  Motion Detector for Webcam
##----------------------------------------------------------------------------------------
##  Written by Pacess HO
##  Platform : Python3 + OpenCV3
##  Date : 2016.Nov.25
##  Copyright 2016 Pacess Studio.  All rights reserved.
##----------------------------------------------------------------------------------------

import smtplib
import imutils
import time
import cv2

# from msvcrt import getch
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

##----------------------------------------------------------------------------------------
##  Main program start
print("\nMotion Detector for Webcam\n\n")
print("Warming up, please wait...\n")
camera = cv2.VideoCapture(0)
time.sleep(2)

##  Loop over the frames of the video
skipCount = 0
lastStatus = False
thisStatus = False
masterFrame = None
print("Start capturing...\n")
while True:

   ##----------------------------------------------------------------------------------------
   ##  Grab the current frame
   grabbed = camera.grab()
 
   ##  If the frame could not be grabbed, then we have reached the end of the video
   if not grabbed:
      continue
 
   if skipCount > 0:
      skipCount = skipCount-1
      if skipCount == 0:
         print("Count down completed, resume capture.")
      continue

   ##----------------------------------------------------------------------------------------
   ##  Convert frame to grayscale, and blur it
   flag, frame = camera.retrieve()
   if not flag:
      continue

   currentFrame = frame.copy()
   gray = cv2.cvtColor(currentFrame, cv2.COLOR_BGR2GRAY)
   gray = cv2.GaussianBlur(gray, (21, 21), 0)
 
   # if the first frame is None, initialize it
   if masterFrame is None:
      masterFrame = gray
      continue

   ##----------------------------------------------------------------------------------------
   ##  Compute the absolute difference between the current frame and
   frameDelta = cv2.absdiff(masterFrame, gray)
   threshold = cv2.threshold(frameDelta, 68, 255, cv2.THRESH_BINARY)[1]

   threshold = cv2.dilate(threshold, None, iterations=2)
   (_, contourArray, _) = cv2.findContours(threshold.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) 

   ##  Loop over the contours
   lastStatus = thisStatus
   thisStatus = False
   for contour in contourArray:

      ##  If the contour is too small, ignore it
      difference = cv2.contourArea(contour)
      if difference < 10000:
         continue
 
      thisStatus = True

      ##  Compute the bounding box for the contour
      (x, y, w, h) = cv2.boundingRect(contour)
      cv2.rectangle(currentFrame, (x, y), (x+w, y+h), (0, 255, 0), 2)

   ##----------------------------------------------------------------------------------------
   ##  Save frame and send email alert
   if thisStatus == True:

      masterFrame = gray

      ##  Save frame
      id = time.strftime("%Y%m%d%H%M%S")
      filename = "./capture/capture_"+str(id)+".jpg"
      cv2.imwrite(filename, currentFrame)
      print("Capture: "+filename)

      ##  Sending email notification
      email = MIMEMultipart()
      email["Subject"] = "Raspberry Pi Motion Alert"
      email["From"] = "pacess@pacess.com"
      email["To"] = "pacess@pacess.com"

      with open(filename, "rb") as filePointer:
         image = MIMEImage(filePointer.read())
      email.attach(image)

      smtp = smtplib.SMTP("smtp.mail.yahoo.com", 587)
      smtp.ehlo()
      smtp.starttls()
      smtp.login("pacess@yahoo.com", "12345678")
      smtp.sendmail(email["From"], email["To"], email.as_string())
      smtp.quit()

      ##  Skip some frames to prevent sending too much image
      print("Email sent, wait for a while...")
      skipCount = 100

##----------------------------------------------------------------------------------------
##  Cleanup the camera and close any open windows
camera.release()
cv2.destroyAllWindows()

2016年11月26日 星期六

Facebook Live 遊戲


還在玩 Facebook Live 投票?看看我的 Facebook Live 遊戲!

自從研發了「即時 Facebook 互動投票」後,這兩個星期經常在 Facebook 看到不同的品牌應用起來。我在想,現在已經玩得很爛,已經很悶;下一步可以做些甚麼呢?想到了用戶給予反應可以當作是按鍵,基於這個想法,便可以發展成為遊戲。問題是每個用戶只能按鍵一次,那麼遊戲要如何玩?於是我想到了煙花遊戲。

這個應用應該是香港首個,甚至是全球第一。我急不及待,花了一整天去完成程式及相關的美術後期製作。其實還有其他的想法,但以一人之力去完成編程及繪圖,所花的時間很多;而且這是概念驗證,上面的已經很有水準。

2016年11月23日 星期三

用 Google Prediction API 預測股價

今日試另一樣新東西,用 Google Prediction API 預測股價。我在想,外國應該有很多高人試過,既然沒甚麼消息,想必準繩度沒有提高多少。雖然如此,也想體驗當中樂趣。不過,我運用了過習過的玄學知識滲入其中,外國人不太懂的中國哲理,隨時能讓我比他們更快找到箇中奧妙。

首先編寫了一個 PHP 程式讀出《股票經理》的股票數據,再加入升級了的天干地支 API 數值,生產出 0005.HK 五年的 .csv 資料集。然後把它上傳到 Google Cloud Storage;再把上傳了的 .csv 檔案供給 Google APIs Explorer 建立預測模型。


生成模型需要一點時間,但 Google Cloub Platform 比 IBM Watson 快出幾倍。執行 predition.trainedmodels.get 指令能查看訓練模型的狀態。第一次訓練時出現「trainingStatus: ERROR」錯誤,發現訓練用的 .csv 不能有標籤或檔頭之類東西。


修正過後,重新上傳 .csv 並進行訓練。約十秒時間模型便完成了。


利用「prediction.trainedmodels.predict」指令及參數便能預測結果。由於我的 .csv 包含了價錢、年、月、日、時數據,而第一個必須為預測的內容,亦即是價錢;所以輸入的參數便是年、月、日、時。下圖是當刻的預測結果,跟市價差不多,好像能拿來作為參考。我再做了兩次預測,結果是今日股價下跌到 $59.74,但明天中午回升至 $60.28。能否作準,明天自有結果。好緊張 p(^_^)q

2016年11月22日 星期二

用 Google Cloud Vision API 辨認 Captcha


準備好 Captcha 資料集後,下一步是上傳到 Google Cloud Storage。

接著用 APIs Explorer 進行測試,可惜失敗率很高。看來用 Google Cloud Vision API 來對抗 Captcha 的方法不行,得找另一個方案。

2016年11月21日 星期一

用 Google Cloud Vision API 辨認文字


今日繼續嘗試 Google Cloud Vision API v1 我在 Apple 網頁抓了上面的圖片,把它上傳到 Google Cloud Storage。然後用 APIs Explorer 進行文字辨認。
Request
POST https://vision.googleapis.com/v1/images:annotate?key={YOUR_API_KEY}
{
   "requests": [{
      "features": [{
         "type": "TEXT_DETECTION"
      }],
      "image": {
         "source": {
            "gcsImageUri": "gs://dummy/macbook_pro_en.png"
         }
      }
   }]
}

ResponseResponse
{
   "responses": [{
      "textAnnotations": [{
         "locale": "la",
         "description": "MacBook Pro\nA touch of genius.\n",
         "boundingPoly": {
            "vertices": [{
               "x": 126,
               "y": 47
            }, {
               "x": 410,
               "y": 47
            }, {
               "x": 410,
               "y": 122
            }, {
               "x": 126,
               "y": 122
            }]
         }
      }, {
         "description": "MacBook",
         "boundingPoly": {
            "vertices": [{
               "x": 175,
               "y": 47
            }, {
               "x": 306,
               "y": 47
            }, {
               "x": 306,
               "y": 73
            }, {
               "x": 175,
               "y": 73
            }]
         }
      }, {
         "description": "Pro",
         "boundingPoly": {
            "vertices": [{
               "x": 320,
               "y": 47
            }, {
               "x": 362,
               "y": 47
            }, {
               "x": 362,
               "y": 73
            }, {
               "x": 320,
               "y": 73
            }]
         }
      }, {
         "description": "A",
         "boundingPoly": {
            "vertices": [{
               "x": 126,
               "y": 79
            }, {
               "x": 141,
               "y": 79
            }, {
               "x": 141,
               "y": 122
            }, {
               "x": 126,
               "y": 122
            }]
         }
      }, {
         "description": "touch",
         "boundingPoly": {
            "vertices": [{
               "x": 157,
               "y": 79
            }, {
               "x": 243,
               "y": 79
            }, {
               "x": 243,
               "y": 122
            }, {
               "x": 157,
               "y": 122
            }]
         }
      }, {
         "description": "of",
         "boundingPoly": {
            "vertices": [{
               "x": 258,
               "y": 79
            }, {
               "x": 286,
               "y": 79
            }, {
               "x": 286,
               "y": 122
            }, {
               "x": 258,
               "y": 122
            }]
         }
      }, {
         "description": "genius.",
         "boundingPoly": {
            "vertices": [{
               "x": 296,
               "y": 79
            }, {
               "x": 410,
               "y": 79
            }, {
               "x": 410,
               "y": 122
            }, {
               "x": 296,
               "y": 122
            }]
         }
      }]
   }]
}

英文文字認得不錯,那中文又如何?我也試了一次。效果很好:
Request
POST https://vision.googleapis.com/v1/images:annotate?key={YOUR_API_KEY}
{
   "requests": [{
      "features": [{
         "type": "TEXT_DETECTION"
      }],
      "image": {
         "source": {
            "gcsImageUri": "gs://dummy/macbook_pro_tc.png"
         }
      }
   }]
}

Response
{
   "responses": [{
      "textAnnotations": [{
         "locale": "zh-Hant",
         "description": "MacBook Pro\n天才橫溢,一觸而發\n亢。\no\n",
         "boundingPoly": {
            "vertices": [{
               "x": 110,
               "y": 52
            }, {
               "x": 426,
               "y": 52
            }, {
               "x": 426,
               "y": 115
            }, {
               "x": 110,
               "y": 115
            }]
         }
      }, {
         "description": "MacBook",
         "boundingPoly": {
            "vertices": [{
               "x": 171,
               "y": 52
            }, {
               "x": 300,
               "y": 52
            }, {
               "x": 300,
               "y": 75
            }, {
               "x": 171,
               "y": 75
            }]
         }
      }, {
         "description": "Pro",
         "boundingPoly": {
            "vertices": [{
               "x": 311,
               "y": 52
            }, {
               "x": 355,
               "y": 52
            }, {
               "x": 355,
               "y": 75
            }, {
               "x": 311,
               "y": 75
            }]
         }
      }, {
         "description": "天才",
         "boundingPoly": {
            "vertices": [{
               "x": 110,
               "y": 82
            }, {
               "x": 176,
               "y": 82
            }, {
               "x": 176,
               "y": 115
            }, {
               "x": 110,
               "y": 115
            }]
         }
      }, {
         "description": "橫溢",
         "boundingPoly": {
            "vertices": [{
               "x": 178,
               "y": 82
            }, {
               "x": 244,
               "y": 82
            }, {
               "x": 244,
               "y": 114
            }, {
               "x": 178,
               "y": 114
            }]
         }
      }, {
         "description": ",",
         "boundingPoly": {
            "vertices": [{
               "x": 254,
               "y": 96
            }, {
               "x": 258,
               "y": 96
            }, {
               "x": 258,
               "y": 104
            }, {
               "x": 254,
               "y": 104
            }]
         }
      }, {
         "description": "一",
         "boundingPoly": {
            "vertices": [{
               "x": 277,
               "y": 96
            }, {
               "x": 307,
               "y": 96
            }, {
               "x": 307,
               "y": 99
            }, {
               "x": 277,
               "y": 99
            }]
         }
      }, {
         "description": "觸",
         "boundingPoly": {
            "vertices": [{
               "x": 311,
               "y": 83
            }, {
               "x": 342,
               "y": 83
            }, {
               "x": 342,
               "y": 114
            }, {
               "x": 311,
               "y": 114
            }]
         }
      }, {
         "description": "而",
         "boundingPoly": {
            "vertices": [{
               "x": 345,
               "y": 84
            }, {
               "x": 376,
               "y": 84
            }, {
               "x": 376,
               "y": 114
            }, {
               "x": 345,
               "y": 114
            }]
         }
      }, {
         "description": "發",
         "boundingPoly": {
            "vertices": [{
               "x": 380,
               "y": 83
            }, {
               "x": 410,
               "y": 83
            }, {
               "x": 410,
               "y": 114
            }, {
               "x": 380,
               "y": 114
            }]
         }
      }, {
         "description": "亢",
         "boundingPoly": {
            "vertices": [{
               "x": 395,
               "y": 91
            }, {
               "x": 409,
               "y": 92
            }, {
               "x": 409,
               "y": 102
            }, {
               "x": 395,
               "y": 101
            }]
         }
      }, {
         "description": "。",
         "boundingPoly": {
            "vertices": [{
               "x": 419,
               "y": 94
            }, {
               "x": 426,
               "y": 94
            }, {
               "x": 426,
               "y": 102
            }, {
               "x": 419,
               "y": 102
            }]
         }
      }, {
         "description": "o",
         "boundingPoly": {
            "vertices": [{
               "x": 419,
               "y": 95
            }, {
               "x": 425,
               "y": 95
            }, {
               "x": 425,
               "y": 103
            }, {
               "x": 419,
               "y": 103
            }]
         }
      }]
   }]
}

2016年11月20日 星期日

準備 Captcha 資料集


2015 年 2 月,嘗試過 Captcha 解碼,但失敗了。最近不斷吸收機器學習方面的知識,坊間也有用 Google Cloud Vision API 對 Google Captcha 的方案,我也想試試自己在這方面的能力。但在這之前,我需要有一些 Captcha 圖案及標籤,用來訓練大腦。最簡單的方法就是用程式來自行生產。我用 PHP 寫了兩個程序:

第一個是生成 Captcha 圖像及標籤的程式:
<?php
//----------------------------------------------------------------------------------------
//  Captcha Generator
//----------------------------------------------------------------------------------------
//  Platform: CentOS7 + PHP + Apache
//  Written by Pacess HO
//  Copyright 2016 Pacess Studio.  All rights reserved.
//----------------------------------------------------------------------------------------

header("Access-Control-Allow-Origin: https://home.pacess.com");
header("Access-Control-Allow-Methods: POST");

date_default_timezone_set("Asia/Hong_Kong");
mb_internal_encoding("UTF-8");
ini_set("memory_limit", "-1");
set_time_limit(0);

session_start();

//----------------------------------------------------------------------------------------
require_once "./securimage/securimage.php";

//========================================================================================
if ($_REQUEST["code"] == "1")  {

   header("Content-Type: text/html");
   $codeArray = $_SESSION["securimage_code_disp"];
   echo("Captcha:".$codeArray["default"]);
   exit(0);
}

$securimage = new Securimage();
$securimage->show();

?>

第二個是讀取圖像並把它以標籤作為檔名的程式:
<?php
//----------------------------------------------------------------------------------------
//  Captcha Dataset Generator
//----------------------------------------------------------------------------------------
//  Platform: CentOS7 + PHP + Apache
//  Written by Pacess HO
//  Copyright 2016 Pacess Studio.  All rights reserved.
//----------------------------------------------------------------------------------------

header("Content-type: text/html");
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Tue, 10 Mar 1987 00:00:00 GMT");

date_default_timezone_set("Asia/Hong_Kong");
mb_internal_encoding("UTF-8");
ini_set("memory_limit", "-1");
set_time_limit(0);

//----------------------------------------------------------------------------------------
$path = "./files/";
$cookieFile = $path."_cookie.txt";
$count = 1;

//========================================================================================
//  Main program
if (isset($_REQUEST["count"]))  {$count = intval($_REQUEST["count"]);}
for ($i=0; $i<$count; $i++)  {

   //  Get a Captcha image
   $curl = curl_init();
   curl_setopt($curl, CURLOPT_URL, "http://sitachan.local/captcha/getCode.php");
   curl_setopt($curl, CURLOPT_POST, 1);
   curl_setopt($curl, CURLOPT_POSTFIELDS, "code=0");
   curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
   curl_setopt($curl, CURLOPT_COOKIEJAR, $cookieFile); 
   curl_setopt($curl, CURLOPT_COOKIEFILE, $cookieFile); 
   $pngContent = curl_exec($curl);
   curl_close($curl);

   //  Get a Captcha value
   $curl = curl_init();
   curl_setopt($curl, CURLOPT_URL, "http://sitachan.local/captcha/getCode.php");
   curl_setopt($curl, CURLOPT_POST, 1);
   curl_setopt($curl, CURLOPT_POSTFIELDS, "code=1");
   curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
   curl_setopt($curl, CURLOPT_COOKIEJAR, $cookieFile); 
   curl_setopt($curl, CURLOPT_COOKIEFILE, $cookieFile); 
   $string = curl_exec($curl);
   curl_close($curl);

   //  String: "Captcha: nYY6FF"
   $array = explode(":", $string);
   $code = $array[1];
   if (strlen($code) == 0)  {$code = "default";}
   $filename = $code.".png";

   //  Save image
   $filePath = $path.$filename;
   $file = fopen($filePath, "w");
   fwrite($file, $pngContent);
   fclose($file);

   //----------------------------------------------------------------------------------------
   //  Output
   echo("<img src='$filePath' />");
   echo("Image size: ".strlen($pngContent));
   echo("String: $string");
   echo("Filename: $filename");
}
?>

2016年11月19日 星期六

初試 Google Cloud Natural Language API


除了 Google Prediction API 能依照留言分類成正評及負評外,Google 還有 Google Cloud Natural Language API 可以達成。用 APIs Explorer 進行測試,可惜暫時還未支援正體中文...。

2016年11月15日 星期二

用 Google Prediction API 來判斷留言者的情緒


繼 IBM Watson 後,今日嘗試了另一個 Machine Learning Framework: Google Preduction API。同樣地,希望能用它來做 Sentiment Analysis,判斷用戶的留言是正評,還是負評。要測試 Google Prediction API 有六個步驟:

1. 在 Google Cloud Console 啟動 Google Prediction API

2. 準備訓練數據。我選用了 https://inclass.kaggle.com/c/si650winter11/data 內的資料。

3. 上傳到 Google Cloud Storage

4. 執行訓練程序:
Request
POST https://www.googleapis.com/prediction/v1.6/projects/dummy-c15ed/trainedmodels?key={YOUR_API_KEY}
{
   "id": "sentiment",
   "storageDataLocation": "dummy-c15ed.appspot.com/sentiment_training.txt"
}
 
Response
{
   "kind": "prediction#training",
   "id": "sentiment",
   "selfLink": "https://www.googleapis.com/prediction/v1.6/projects/dummy-c15ed/trainedmodels/sentiment",
   "storageDataLocation": "dummy-c15ed.appspot.com/sentiment_training.txt"
}

5. 檢查訓練狀態:
Request
GET https://www.googleapis.com/prediction/v1.6/projects/dummy-c15ed/trainedmodels/sentiment?key={YOUR_API_KEY}
 
Response
{
   "kind": "prediction#training",
   "id": "sentiment",
   "selfLink": "https://www.googleapis.com/prediction/v1.6/projects/dummy-c15ed/trainedmodels/sentiment",
   "created": "2016-11-15T07:15:34.690Z",
   "trainingStatus": "RUNNING"
}

直至完成:
Request
GET https://www.googleapis.com/prediction/v1.6/projects/dummy-c15ed/trainedmodels/sentiment?key={YOUR_API_KEY}
 
Response
{
   "kind": "prediction#training",
   "id": "sentiment",
   "selfLink": "https://www.googleapis.com/prediction/v1.6/projects/dummy-c15ed/trainedmodels/sentiment",
   "created": "2016-11-15T07:15:34.690Z",
   "trainingComplete": "2016-11-15T07:16:26.026Z",
   "modelInfo": {
      "numberInstances": "7085",
      "modelType": "classification",
      "numberLabels": "2",
      "classificationAccuracy": "0.98"
   },
   "trainingStatus": "DONE"
}

6. 輸入新的留言進行測試:
Request
POST https://www.googleapis.com/prediction/v1.6/projects/dummy-c15ed/trainedmodels/sentiment/predict?key={YOUR_API_KEY}
{
   "input": {
      "csvInstance": [
         "This is really a poor product, waste my time!"
      ]
   }
}
 
Response
{
   "kind": "prediction#output",
   "id": "sentiment",
   "selfLink": "https://www.googleapis.com/prediction/v1.6/projects/dummy-c15ed/trainedmodels/sentiment/predict",
   "outputLabel": "Negative",
   "outputMulti": [
      {
         "label": "Positive",
         "score": "0.353047"
      }, {
         "label": "Negative",
         "score": "0.646953"
      }
   ]
}
Request
POST https://www.googleapis.com/prediction/v1.6/projects/dummy-c15ed/trainedmodels/sentiment/predict?key={YOUR_API_KEY}
{
   "input": {
      "csvInstance": [
         "Pretty cool!  I love it!"
      ]
   }
}
 
Response
{
   "kind": "prediction#output",
   "id": "sentiment",
   "selfLink": "https://www.googleapis.com/prediction/v1.6/projects/dummy-c15ed/trainedmodels/sentiment/predict",
   "outputLabel": "Positive",
   "outputMulti": [
      {
         "label": "Positive",
         "score": "0.998411"
      }, {
         "label": "Negative",
         "score": "0.001589"
      }
   ]
}

由於使用的是英語素材進行訓練,所以測試也要以英文進行。看來效果不錯。下一步是要找出中文素材。

2016年11月13日 星期日

在 Facebook Live 直播時插入即時用戶反應


昨晚花了數個小時開發了一個名為「Interactive Facebook Live」的示範程式。程式以 Facebook Javascript SDK + Graph API + Velocity.JS + 能把畫面輸出到 Facebook Live 的直播軟件去達成。製作這個示範,是希望在進行 Facebook Live 直播時,能即時把當刻的用戶反應包含在直播內容中,相信這樣更有趣味。

首先,當然是建立 HTML 檔案,加入 Facebook Javascript SDK。我是取得旗下專頁其中一個貼文的用戶反應,所以要加入 Facebook 登入,好讓 SDK 能取得 Token 來繼續往後的查詢工作。有了登入,之後就是利用 Graph API 取得用戶反應數據,得到的是一個 Array 數組,進行解讀說行,這個工作很簡單。為了畫面更加生動,於是加入 Velocity.JS 來表現彈一彈的動畫。這樣,網頁部份基本上完成了。只要把網頁的畫面經過 Facebook Live 直播軟件直播出去,就能達成這個「Interactive Facebook Live」的示範。大家不妨到我的專頁 https://www.facebook.com/Pacess-Studio-901621989960199/?fref=ts 看看。

2016年11月10日 星期四

IBM Watson 平台用後感


IBM Watson 平台的 30 天試用已經過了一半,得趕緊在到期前完成測試。

作為依懶社交平台生存的生意,要是能用人工智能把用戶的評語作出分析,可以讓企業能快速應對,同時也能減輕工作量。Personality Insights 看來幫到忙。設定好「Service Credentials」後就能把生成的用戶名稱及密碼用在 API 接口。看完教學文件後嘗試連接,得出 404 錯誤碼。明明跟足教學,卻出現錯誤,實在奇怪。於是再找其他辦法,卻發現:

在文件一指出用:
https://gateway.watsonplatform.net/personality-insights/api

但在文件二就叫用:
https://gateway.watsonplatform.net/personality-insights/api/v3

而在文件三則用:
https://gateway.watsonplatform.net/personality-insights/api/v3/profile

多個文件版本,唯有逐一測試。最終發現前兩個地址傳回 404 錯誤碼,根本不能運作。意味著文件內容過時及誤導。文件東一份西一份,就算正確的那份文件,也沒有對傳回值作完整的說明,實在非常差勁。以下是成功的呼叫指令:

curl -X POST --user b7654abc-a1b2-4567-abcd-1234567890cd:NWes1Ncdk2CR --header "Content-Type:text/plain;charset=utf-8" --data-binary "@profile.txt" "https://gateway.watsonplatform.net/personality-insights/api/v3/profile?version=2016-10-20&consumption_preferences=true&raw_scores=true"

2016年11月9日 星期三

讓 Google Data Studio 接入 AWS 伺服器

同事用 Google Data Studio 連接到 AWS 上的數據庫時出現問題。經過一輪的追查後,發現是防火牆只容許指定的電腦接入。只要加入 Google 的伺服器地址就能解決問題。可是,Google 那麼多伺服器,究竟用哪個地址呢?

於是我在登入 SSH 後,用「watch -n 5 tcpdump -i eth0 -s 1500 port 3306」指令監聽著接入的訊號,待同事用 Google Data Studio 接入,找出了地址是「74.125.72.161」。我估計 Google 有多台伺服器,決定讓「74.125.72.0」到「74.125.72.255」都通過防火牆。在 AWS 上輸入 CIDR 格式「74.125.72.0/24」即可。

2016年11月8日 星期二

Google Hack





今日利用幾招 Google 搜尋方法去找 Sita 陳僖儀的資料,找到一些關於她的 PDF,當中有幾個包含了我沒有看過的圖片。我用的指令是「filetype:pdf "陳僖儀"」。Google Hack 的秘技如下:

2016年11月5日 星期六

未來人類不用工作


這個星期花了四天時間看完 Elon Musk 的傳記,是我讀得最快的一本書。原因是內容吸引,而且某些方面跟自己的想法十分接近,很有共鳴。

恰巧,昨天看到一篇 Elon Musk 的報導,指他預計未來基於自動化及機械人取代勞力,工作種類變少,政府會為平民進行補貼。這個構思跟我早幾個月前的想法不約而同都很相似。我的思路是: 自動化及機械人取代勞力,糧食產出提升,政府能確保人民得到溫飽。人類不用耕種,可以有更多時間發展興趣,提升國家競爭力。同時也因為得到溫飽,有些人不去工作。所以國家會提高成為人民的水平門檻,逐漸排除低學歷低智商的市民,留下高學歷高智商的人,生產有質素的下一代。在排除了低下的一群後,會有更多的資源分配給留下來的人,再次提升人民的生活水平。

其實 Elon Musk 在書中有提過需要保持高質素市民的人口,將這個想法套用到他的說法上,就有可能跟我上面所說的事情差不多。

2016年11月4日 星期五

Natural Language Processing With Python


近兩個星期,一有時間便會學習「Machine Learning」方面的事情。由於還沒有具體的成果,所以沒有作出分享。其中一個目標是開發一套軟件去聽取用戶在 Facebook 及其他社交平台的意見,然後把意見轉化為情緒指標。看看滿意的有多少百份比、憤怒的有多少百份比、各指標的變化及趨勢。這有利我們把活動及貼文做得更好;而且不用瀏覽所有留言才得悉用戶的大概想法。要達到以上目標,「Natural Language Processing」應該幫到忙,不過對於處理中文來說,還是跟英文很不一樣。在 YouTube 上有一系列的教學,這個是我目前在觀看的。這位作者還有很多圍繞 Python 方面的教學影片,之前看關於股市走勢的也有他份,十分利害。