2015-12-21

OpenCV quick start

Lâu ngày có dịp dùng OpenCV. Tính ra chỉ lúc học ĐH là xài nhiều sau đó dùng thêm 1 số dịp nữa là thôi. Kể từ lần cuối viết theo C style của version 1, hầu như chẳng đụng tới nó. Sẵn tiện coi thử có gì mới sau gần 10 năm.

Phiên bản giờ là OpenCV 3.1 ngày 2015-12-21, tuy nhiên do chưa đụng tới lâu nên xem thử trước version 2 là OpenCV 2.4.12 ngày 2015-07-30. Download bản dùng cho Windows, code C/C++ xài Visual Studio 2013/Windows nói chung khá là OK. Change logs thì có ở đây. So với những gì còn nhớ thì cách viết chuyển qua C++ hết, để NS, chuyển struct thành class :D. Nói chung ok, có vẻ dễ xài hơn.

Đầu tiên thì setup, sau khi download về và install vào đâu đó thường mình để chung 1 đám trong D:\dev, giả sử là D:\dev\opencv-2.4.12.



Setup

Tạo environment variable để dễ sử dụng cho configuration tên là OPENCV_DIR.
Chọn View Other Windows Property Manager Debug | x64.

Chọn Microsoft.Cpp.x64.user Properties, thêm user macro OPENCV_DIR, set macro là environment variable.

Thêm OpenCV vào project, tạo environment variable OPENCV_DIR dùng global property page
Với Visual Studio 2008 (vc10): Tools Options Projects and Solutions VC++ Directories.

Thêm đường dẫn bin (chứa các dll của OpenCV) vào PATH Control Panel System Advanced system settings Advanced Tab Environment variables... Tuy nhiên các này không tốt lắm, tốt nhất là thêm bin theo project khi debug.

Nếu theo project trong Project Properties Debugging Environment.
Ví dụ: OpenCV

PATH=$(OPENCV_DIR)\x64\vc12\bin%3b$(PATH)

Ví dụ: Qt + OpenCV

PATH=$(QTDIR)\bin%3b$(OPENCV_DIR)\x64\vc12\bin%3b$(PATH)

Thêm Project Properties Configuration Properties C/C++ General Additional Include Directories

OpenCV

.;$(OPENCV_DIR)\include;%(AdditionalIncludeDirectories)

Với Qt + OpenCV

.\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtWidgets;$(OPENCV_DIR)\include;%(AdditionalIncludeDirectories)

Thêm Project Properties Configuration Properties Linker General Additional Library Directories

$(OPENCV_DIR)\x64\vc12\lib;

Thêm Project Properties Configuration Properties Linker Input Additional Dependencies
opencv_[xxx]2412[d].lib với 2412 là version và d cho debug configuration.

opencv_calib3d2412d.lib
opencv_contrib2412d.lib
opencv_core2412d.lib
opencv_features2d2412d.lib
opencv_flann2412d.lib
opencv_gpu2412d.lib
opencv_highgui2412d.lib
opencv_imgproc2412d.lib
opencv_legacy2412d.lib
opencv_ml2412d.lib
opencv_nonfree2412d.lib
opencv_objdetect2412d.lib
opencv_ocl2412d.lib
opencv_photo2412d.lib
opencv_stitching2412d.lib
opencv_superres2412d.lib
opencv_ts2412d.lib
opencv_video2412d.lib
opencv_videostab2412d.lib

Showing Image

OK ví dụ đơn giản lấy ví dụ của OpenCV thử.

#include <opencv/cv.h> 
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv/highgui.h> 

using namespace std;

int main(int argcconst char** argv)
{
     cv::Mat image;
     cv::String fileName = "demo.jpg";

     if (argc == 2) {
        fileName = argv[1];     // Image path from args
     }

     if (fileName.empty()) {
        cout << " Usage: display_image /path/to/image" << endl;
        return -1;
     }
     else {
        image = cv::imread(fileName);
        if (image.empty()) {
           cout << "Could not open or find the image" << endl;
           return -1;
        }
        else {
           cv::namedWindow("Display window", cv::WINDOW_AUTOSIZE);  // Create a window for display.
           cv::imshow("Display window", image);               // Show our image inside it.

           cv::waitKey(0);                                 // Wait for a keystroke in the window
        }
     }
}

Các ví dụ có thể tìm tại OpenCV tutorials và module xử lý ảnh Image Processing

Dùng với Qt

Chuyển giữa OpenCV::Mat và Qt::QImage Convert between cv::mat and Qimage correctly
xem chi tiết tại Qt and OpenCV.

OpenCV dùng mặc định BGR thay cho RGB. Để hiển thị ảnh grayscale dùng QImage::Format_Grayscale8 hoặc dùng QImage::Format_RGB888 cho ảnh 8UC3.

//---------------------------------------------------------------------------
// Convert cv::Mat to QImage without copy(share the same buffer)
//---------------------------------------------------------------------------
// The trap at here is mat.step, the buffer of the cv::Mat may do some padding(factor of 4)
// on each row for the sake of speed. If you just wrote
// QImage(mat.data, mat.cols, mat.rows, format);

//---------------------------------------------------------------------------
// Convert the channels from RGB to BGR
//---------------------------------------------------------------------------
// The default channels of the openCV is BGR but the QImage is RGB,
// that means you need to convert the channels from RGB to BGR when
// convert between three channels image and vice versa.In QImage,
// you could call "rgbSwapped()";
// In cv::Mat, you could call cv::cvtColor to convert the channels.
// cvtColor(col, rgb,cv::COLOR_BGR2RGB);
auto image = QImage(mat.data, mat.cols, mat.rows,
     mat.step, QImage::Format_RGB888)
     .rgbSwapped();

//---------------------------------------------------------------------------
// Convert QImage to cv::Mat without copy(share the same buffer)
//---------------------------------------------------------------------------
//mat = cv::Mat(img.height(), img.width(),
//   format, img.bits(), img.bytesPerLine());

//---------------------------------------------------------------------------
// Convert cv::Mat to QImage by copy
//---------------------------------------------------------------------------
//img = QImage(mat.data, mat.cols, mat.rows,
//   mat.step, format).copy();

Đổi colorspace cho pixel

Để đổi colorspace cho single pixel, OpenCV ko expose method, copy struct conversion từ source code tại OpenCV Github nhưng nhanh nhất là chuyển sang Mat 1x1 pixel.
Lưu ý: OpenCV xài HSV/HSB với ảnh 8 bit range là (180-255-255) so với thông thường (360-100-100) để match với range 0-255.

Xem phần RGB / HSV (CV_BGR2HSV, CV_RGB2HSV, CV_HSV2BGR, CV_HSV2RGB)
8-bit images
$V  \leftarrow 255 V, S  \leftarrow 255 S, H  \leftarrow H/2  \text{(to fit to 0 to 255)}$

cv::Scalar colorBGR2HSV(const cv::Scalar& bgrClr) {
     // cv::Scalar.val[0-2] used
     cv::Mat bgr(1, 1, CV_8UC3);
     bgr.setTo(bgrClr);

     cv::Mat hsv;
     cv::cvtColor(bgr, hsv, cv::COLOR_BGR2HSV);

     auto clr = hsv.atVec3b>(cv::Point(0, 0));
     return cv::Scalar(clr.val[0], clr.val[1], clr.val[2]);
}

cv::Scalar colorBGR2Lab(const cv::Scalar& bgrClr) {
     // cv::Scalar.val[0-2] used
     cv::Mat bgr(1, 1, CV_8UC3);
     bgr.setTo(bgrClr);

     cv::Mat lab;
     cv::cvtColor(bgr, lab, cv::COLOR_BGR2Lab);

     auto clr = lab.atVec3b>(cv::Point(0, 0));
     return cv::Scalar(clr.val[0], clr.val[1], clr.val[2]);
}

cv::Scalar colorHSV2BGR(const cv::Scalar& hsvClr) {
     // cv::Scalar.val[0-2] used
     cv::Mat hsv(1, 1, CV_8UC3);
     hsv.setTo(hsvClr);

     cv::Mat bgr;
     cv::cvtColor(hsv, bgr, cv::COLOR_HSV2BGR);

     auto clr = bgr.atVec3b>(cv::Point(0, 0));
     return cv::Scalar(clr.val[0], clr.val[1], clr.val[2]);
}

cv::Scalar colorHSV2Lab(const cv::Scalar& hsvClr) {
     // cv::Scalar.val[0-2] used
     cv::Mat hsv(1, 1, CV_8UC3);
     hsv.setTo(hsvClr);

     cv::Mat bgr, lab;
     cv::cvtColor(hsv, bgr, cv::COLOR_HSV2BGR);
     cv::cvtColor(bgr, lab, cv::COLOR_BGR2Lab);

     auto clr = lab.atVec3b>(cv::Point(0, 0));
     return cv::Scalar(clr.val[0], clr.val[1], clr.val[2]);
}

Xác định màu của image, findContours

Để xác định object có màu xác định thông thường sẽ dùng threshold binrary với mask là lower/upper color.

Range để xác định một số màu cơ bản

void initializeRanges() {

 // Red lower 0°-28° (0-360)
 colorRanges.push_back(ColorRange(ColorType::Red,
  cv::Scalar(0, 27, 35), cv::Scalar(14, 255, 255)));

 // Brown 28°-40° (0-360)
 colorRanges.push_back(ColorRange(ColorType::Brown,
  cv::Scalar(14, 27, 35), cv::Scalar(20, 255, 255)));

 // Orange 40°-46° (0-360)
 colorRanges.push_back(ColorRange(ColorType::Orange,
  cv::Scalar(20, 27, 35), cv::Scalar(23, 255, 255)));

 // Yellow 46°-64° (0-360)
 colorRanges.push_back(ColorRange(ColorType::Yellow,
  cv::Scalar(23, 27, 35), cv::Scalar(34, 255, 255)));

 // Green 64°-146° (0-360)
 colorRanges.push_back(ColorRange(ColorType::Green,
  cv::Scalar(34, 27, 35), cv::Scalar(73, 255, 255)));

 // Aqua 146°-204° (0-360)
 colorRanges.push_back(ColorRange(ColorType::Aqua,
  cv::Scalar(73, 27, 35), cv::Scalar(102, 255, 255)));

 // Aqua-Blue, Blue 204°-254° (0-360)
 colorRanges.push_back(ColorRange(ColorType::Blue,
  cv::Scalar(102, 27, 35), cv::Scalar(127, 255, 255)));

 // Magenta 254°-298° (0-360)
 colorRanges.push_back(ColorRange(ColorType::Magenta,
  cv::Scalar(127, 27, 35), cv::Scalar(149, 255, 255)));

 // Pink 298°-350° (0-360)
 colorRanges.push_back(ColorRange(ColorType::Pink,
  cv::Scalar(149, 27, 37), cv::Scalar(175, 255, 255)));

 // Red upper 350°-360° (0-360)
 colorRanges.push_back(ColorRange(ColorType::Red,
  cv::Scalar(175, 27, 35), cv::Scalar(180, 255, 255)));

 // Black
 colorRanges.push_back(ColorRange(ColorType::Black,
  cv::Scalar(0, 0, 0), cv::Scalar(180, 255, 35)));

 // White (not blue) hue 0°-170° (0-360) & 230°-360° (0-360)
 colorRanges.push_back(ColorRange(ColorType::White,
  cv::Scalar(0, 0, 190), cv::Scalar(85, 27, 255)));
 colorRanges.push_back(ColorRange(ColorType::White,
  cv::Scalar(115, 0, 190), cv::Scalar(180, 27, 255)));

 colorRanges.push_back(ColorRange(ColorType::Blue,
  cv::Scalar(85, 0, 190), cv::Scalar(115, 27, 255)));
};

Xác định vùng theo specific color

// Threshold the HSV image to get only specific colors
cv::Mat mask, blurred, thresholded;
cv::inRange(hsv, lower, upper, mask);

cv::GaussianBlur(mask, blurred, cv::Size(9, 9), 5, 5);

// Separate out regions of an image corresponding to objects which we want to analyze. 
// This separation is based on the variation of intensity 
// between the object pixels and the background pixels.
cv::threshold(blurred, thresholded, 120, 255, CV_THRESH_BINARY);

// MORPH_RECT vs MORPH_CROSS vs MORPH_ELLIPSE
cv::Mat strEl = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(10, 10));

// Morphological opening (remove small objects from the foreground)
// Open dilate(erode(src, element))
//cv::erode(thresholded, thresholded, strEl);
//cv::dilate(thresholded, thresholded, strEl);
cv::morphologyEx(thresholded, thresholded, cv::MORPH_OPEN, strEl);

// Morphological closing (fill small holes in the foreground)
// Close erode(dilate(src, element))
//cv::dilate(thresholded, thresholded, strEl);
//cv::erode(thresholded, thresholded, strEl);
cv::morphologyEx(thresholded, thresholded, cv::MORPH_CLOSE, strEl);

cv::vector<cv::vector<cv::Point> > contours;
cv::vector<cv::Vec4i> hierarchy;

// CV_RETR_EXTERNAL/CV_RETR_LIST instead of CV_RETR_TREE 
// @see http://stackoverflow.com/questions/8830619/difference-between-cv-retr-list-cv-retr-tree-cv-retr-external
// Documentation http://opencv.itseez.com/2.4/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?#findcontours
// CV_RETR_EXTERNAL gives "outer" contours, so if you have (say) one contour enclosing another 
// (like concentric circles), only the outermost is given.
cv::findContours(thresholded, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);

//cv::RNG rng;
//cv::Mat mat = thresholded.clone();
//for (int i = 0; i < contours.size(); i++) {
// auto clr = cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
// cv::drawContours(mat, contours, i, clr, 1, 8, hierarchy, 0, cv::Point());
//}

Color difference

Chuyển qua L*a*b scale và dùng CIE Delta E 1976 algorithm hay CIE Delta E 2000.
Code của algorithm này như sau:

double deg2rad(double deg) {
 return (deg * (M_PI / 180.0));
}

double rad2deg(double rad) {
 return ((180.0 / M_PI) * rad);
}

// Computes the difference between two BGR colors by converting them to 
// the L*a*b scale BGR/CIE (CV_BGR2Lab) and comparing them using the 
// CIE76 algorithm http://en.wikipedia.org/wiki/Color_difference#CIE76
// The 1976 formula is the first color-difference formula that related 
// a measured to a known set of CIELAB coordinates. This formula has been 
// succeeded by the 1994 and 2000 formulas because the CIELAB space 
// turned out to be not as perceptually uniform as intended, especially in 
// the saturated regions. This means that this formula rates these 
// colors too highly as opposed to other colors.
// Lab, Delta E (standard Euclidean distance) sqrt(L² + a² + b²)
// CIE Delta E 1976
// JND: ~2.3
double deltaE1976(const cv::Scalar& lab1, const cv::Scalar& lab2)
{
 // RGB/CIE L*a*b*
 // 8-bit images: L ← L ∗ 255 / 100, a ← a + 128, b ← b + 128
 auto dL = lab1.val[0] - lab2.val[0];
 // Covert dL back CIE L*a*b*
 dL = dL * 100 / 255;

 auto da = lab1.val[1] - lab2.val[1];
 auto db = lab1.val[2] - lab2.val[2];

 return cv::sqrt(dL * dL + da * da + db * db);
}

// http://svn.int64.org/viewvc/int64/colors/colors.js
// http://www.ece.rochester.edu/~gsharma/ciede2000/
double deltaE2000(const cv::Scalar& lab1, const cv::Scalar& lab2) {

 // RGB/CIE L*a*b*
 // 8-bit images: L ← L ∗ 255 / 100, a ← a + 128, b ← b + 128
 auto x = lab1;
 auto y = lab2;

 /*
 * "For these and all other numerical/graphical delta E00 values
 * reported in this article, we set the parametric weighting factors
 * to unity(i.e., kL = kC = kH = 1.0)." (Page 27).
 */
 const double kL = 1.0, kC = 1.0, kH = 1.0;
 const double deg360InRad = deg2rad(360.0);
 const double deg180InRad = deg2rad(180.0);
 const double pow25To7 = 6103515625.0; /* pow(25, 7) */

 /*
 * Step 1
 */
 /* Equation 2 */
 double C1 = sqrt((x.val[1] * x.val[1]) + (x.val[2] * x.val[2]));
 double C2 = sqrt((y.val[1] * y.val[1]) + (y.val[2] * y.val[2]));

 /* Equation 3 */
 double barC = (C1 + C2) / 2.0;

 /* Equation 4 */
 double G = 0.5 * (1 - sqrt(pow(barC, 7) / (pow(barC, 7) + pow25To7)));

 /* Equation 5 */
 double a1Prime = (1.0 + G) * x.val[1];
 double a2Prime = (1.0 + G) * y.val[1];

 /* Equation 6 */
 double CPrime1 = sqrt((a1Prime * a1Prime) + (x.val[2] * x.val[2]));
 double CPrime2 = sqrt((a2Prime * a2Prime) + (y.val[2] * y.val[2]));

 /* Equation 7 */
 double hPrime1;
 if (x.val[2] == 0 && a1Prime == 0) {
  hPrime1 = 0.0;
 }
 else {
  hPrime1 = atan2(x.val[2], a1Prime);
  /*
  * This must be converted to a hue angle in degrees between 0
  * and 360 by addition of 2􏰏 to negative hue angles.
  */
  if (hPrime1 < 0){
   hPrime1 += deg360InRad;
  }
 }

 double hPrime2;
 if (y.val[2] == 0 && a2Prime == 0) {
  hPrime2 = 0.0;
 }
 else {
  hPrime2 = atan2(y.val[2], a2Prime);
  /*
  * This must be converted to a hue angle in degrees between 0
  * and 360 by addition of 2 to negative hue angles.
  */
  if (hPrime2 < 0){
   hPrime2 += deg360InRad;
  }
 }

 /*
 * Step 2
 */
 /* Equation 8 */
 double deltaLPrime = y.val[0] - x.val[0];

 /* Equation 9 */
 double deltaCPrime = CPrime2 - CPrime1;

 /* Equation 10 */
 double deltahPrime;
 double CPrimeProduct = CPrime1 * CPrime2;

 if (CPrimeProduct == 0) {
  deltahPrime = 0;
 }
 else {
  /* Avoid the fabs() call */
  deltahPrime = hPrime2 - hPrime1;
  if (deltahPrime < -deg180InRad) {
   deltahPrime += deg360InRad;
  }
  else if (deltahPrime > deg180InRad) {
   deltahPrime -= deg360InRad;
  }
 }
 /* Equation 11 */
 double deltaHPrime = 2.0 * sqrt(CPrimeProduct) *
  sin(deltahPrime / 2.0);

 /*
 * Step 3
 */
 /* Equation 12 */
 double barLPrime = (x.val[0] + y.val[0]) / 2.0;

 /* Equation 13 */
 double barCPrime = (CPrime1 + CPrime2) / 2.0;

 /* Equation 14 */
 double barhPrime, hPrimeSum = hPrime1 + hPrime2;

 if (CPrime1 * CPrime2 == 0) {
  barhPrime = hPrimeSum;
 }
 else {
  if (fabs(hPrime1 - hPrime2) <= deg180InRad) {
   barhPrime = hPrimeSum / 2.0;
  }
  else {
   if (hPrimeSum < deg360InRad) {
    barhPrime = (hPrimeSum + deg360InRad) / 2.0;
   }
   else {
    barhPrime = (hPrimeSum - deg360InRad) / 2.0;
   }
  }
 }

 /* Equation 15 */
 double T = 1.0 - (0.17 * cos(barhPrime - deg2rad(30.0))) +
  (0.24 * cos(2.0 * barhPrime)) +
  (0.32 * cos((3.0 * barhPrime) + deg2rad(6.0))) -
  (0.20 * cos((4.0 * barhPrime) - deg2rad(63.0)));

 /* Equation 16 */
 double deltaTheta = deg2rad(30.0) *
  exp(-pow((barhPrime - deg2rad(275.0)) / deg2rad(25.0), 2.0));

 /* Equation 17 */
 double RC = 2.0 * sqrt(pow(barCPrime, 7.0) /
  (pow(barCPrime, 7.0) + pow25To7));

 /* Equation 18 */
 double SL = 1 + ((0.015 * pow(barLPrime - 50.0, 2.0)) /
  sqrt(20 + pow(barLPrime - 50.0, 2.0)));

 /* Equation 19 */
 double SC = 1 + (0.045 * barCPrime);

 /* Equation 20 */
 double SH = 1 + (0.015 * barCPrime * T);

 /* Equation 21 */
 double RT = (-sin(2.0 * deltaTheta)) * RC;

 /* Equation 22 */
 double deltaE = sqrt(
  pow(deltaLPrime / (kL * SL), 2.0) +
  pow(deltaCPrime / (kC * SC), 2.0) +
  pow(deltaHPrime / (kH * SH), 2.0) +
  (RT * (deltaCPrime / (kC * SC)) * (deltaHPrime / (kH * SH))));

 return (deltaE);
}


2015-12-05

Disable CTRL-Space toggling Chinese IME (Windows 7)

Tắt Ctrl-Space chuyển mode Chinese IME.

Xem http://superuser.com/questions/327479/ctrl-space-always-toggles-chinese-ime-windows-7

Tóm tắt:

1. Bật Registry Edit: Start (Windows + R) > regedit
2. Tới HKEY_CURRENT_USER/Control Panel/Input Method/Hot Keys
3. Chọn 2 key name
  • 00000070 for the Chinese (Traditional) IME - Ime/NonIme Toggle hotkey
  • 00000010 for the Chinese (Simplified) IME - Ime/NonIme Toggle hotkey
4. Có 2 subkey cần chỉnh














  • Key Modifiers quy định Alt/Ctrl/Shift/etc giá trị đang là Ctrl (02c00000).
  • Virtual Key hiện tại Space (20000000).
5. Chỉnh Key Modifiers từ 02 thành 00
6. Chỉnh Virtual Key từ 20 thành FF
7. Log off hoặc restart.

2015-11-17

1TB+ free cloud storage with Tencent Weiyun 腾讯微云

Sau khi xài Baidu Cloud Manager 百度云管家 mình lại xài tiếp Tencent Weiyun 腾讯微云.
Thật ra thì Baidu xài 2TB vẫn chưa hết nhưng nghe nói Tencent Weiyun cho tới 10TB mà mình cũng có sẵn QQ nên đăng ký luôn. Sẵn tiện move 1 số thứ lên đây backup :D, hỗ trợ file size tới 32GB, mà Tencent thì thường cũng hỗ trợ English OK hơn. Trong list mấy cloud storage "anh hùng" còn thấy Qihoo 奇虎 360 Yunpan 360 云盘 cho tận 36TB (TB chứ ko phải GB nhé). Toàn hàng khủng, nếu ai thấy hứng thú thì cứ thử.


Chỉ cần search 1 chút trên net 10TB Free Cloud Storage with Tencent Weiyun thì có thể ra 1 đống link chỉ dẫn. Note lại

1. Tất nhiên là đăng ký QQ nếu ai chưa có thì đăng ký tại đây. (đăng ký ko cần install hay xài cũng được). Trang này tiếng Anh nên không có vấn đề gì. Mình thường chọn email outlook, dù sao thì Microsoft có lẽ chơi tốt với Tencent hơn.

Sau khi đăng ký QQ theo 1 số hướng dẫn chỉ cần install Weiyun app cho Android hay iOS và login bằng QQ thì sẽ nhận được 10TB. Có chỗ thì nói là ko nhận liền được 10TB mà sẽ từ từ tăng nếu dùng quá 1TB (1TB là dung lượng mặc định). 1 số comment còn kêu nó chỉ tăng khi nào up 1 số data, còn gợi ý là nên download Windows hay Mac client rồi up file khoảng 1,2 GB gì đó. Mình ko rõ là như thế nào, nhưng 1TB thì cũng lớn rồi? OK install mobile app rồi xem thế nào (sẽ update tình hình sau).

2. Install version dành cho Android và iOS. Màn hình login Android, tiếng Hoa nhập vào QQ ID và password.



















OK đăng nhập vào thử thấy dung lượng hơn 1TB rồi.

3. Nhiều chỗ kêu nhận thưởng tại http://www.weiyun.com/act/10t-en.html, click nút màu xanh bự bự ở dưới. Mình ko thấy gì cả??? chắc hết rồi sao đó (cry). Bọn nó còn chưng 2 tấm hình, còn mình toàn bị redirect về lại http://www.weiyun.com/.

Thôi kệ cứ login vào đã (vẫn chỉ 1TB hichic).

3. Download và install Weiyun cho Windows hay Mac, tất nhiên là login dùng QQ ID http://www.weiyun.com/download.html. Login vào giao diện giống giống Baidu Cloud

Có mấy chỗ chỉ chuyển language sang tiếng Anh như version 2, Weiyun này 3.1 chưa thấy có chỗ nào chỉ.




2015-09-21

Phát sinh data gần phân phối chuẩn với thang đo khoảng trong R - Generate a approximately normal distribution with interval scale (simulate Likert) in R

Để tạo data với phân phối chuẩn trong R thì rất dễ dàng với
rnorm(n, mean, sd).

Đôi khi muốn tạo data gần phân phối chuẩn nhưng với thang đo khoảng như thang đo Likert 5 hay Likert 7 mức thì việc dùng rnorm chưa đáp ứng được điều này.

  • Các đầu tiên là dùng rnorm sau đó làm tròn số > vấn đề cần thực hiện làm tròn số như thế nào
  • Một cách để thực hiện (có thể xài được) là sử dụng phân phối nhị thức làm hơi ngược ứng dụng của định lý Moivre Laplace (n lớn và p không quá gần 0 hay 1) để xấp xỉ phân phối nhị thức bằng phân phối chuẩn. Tất nhiên cách này không đúng lắm vì không thỏa mãn đk (n ≥ 30, np ≥ 5) nhưng có thể chấp nhận được. B(n; p) ~ N(np; np(1−p))


Giả sử cần tạo data với phân phối chuẩn N= 200, μ = 3.5, σ² = 0.8

Method 1:
Chia phân phối chuẩn thành 5 đoạn, -1 và 6 để bảo đảm không sinh ra NA do ngoài khoảng (lưu ý μ và 3σ)
xcut <- cut(rnorm(200, 3.5, 0.8), breaks = c(-1, 1, 2, 3, 4, 6))
x <- rep(1:5, as.vector(table(xcut)))
mean(x)
sd(x)
hist(x, col = 'light blue')

Tất nhiên cách này do chia khoảng nên mean và sd có thể không như mong đợi. Nên generate 1 vài lần để có kết quả gần đúng nhất hoặc điều chỉnh input mean và sd.

Method 2:
Thay vì tạo data với phân phối chuẩn thì sẽ tạo data với phân phối nhị thức.

# B(n; p) ~ N(np; np(1−p))
# μ = np
# σ² = np(1−p) = np - np²
# np² = μ - σ² = np - (np - np²)
# p = np² / np = (μ - σ²) / μ
# n = np / p = μ / p = μ²/(μ - σ²)

x <- rbinom(200, ceiling(3.5 * 3.5 / (3.5 - 0.8)), (3.5 - 0.8) / 3.5)
mean(x)
sd(x)
hist(x, col = 'light blue')
write.csv(x, 'data.csv', row.names = F)

Nói chung chạy vài lần kiểm tra mean và sd phù hợp rồi sử dụng kết quả

2015-08-11

Build PredictionIO in Linux machine. Error: Invalid or corrupt jarfile xxx/PredictionIO/sbt/sbt-launch-0.13.7.jar

[root@labs]#git clone https://github.com/PredictionIO/PredictionIO.git
Initialized empty Git repository in /usr/local/src/prediction-io/PredictionIO/.git/
remote: Counting objects: 53508, done.
remote: Total 53508 (delta 0), reused 0 (delta 0), pack-reused 53508
Receiving objects: 100% (53508/53508), 27.82 MiB | 925 KiB/s, done.
Resolving deltas: 100% (23318/23318), done.
[root@labs]#cd PredictionIO
[root@labs]#git checkout master
Branch master set up to track remote branch master from origin.
Switched to a new branch 'master'
[root@labs]#./make-distribution.sh
Building binary distribution for PredictionIO 0.9.4...
Attempting to fetch sbt
######################################################################## 100.0%
Launching sbt from /usr/local/src/prediction-io/PredictionIO/sbt/sbt-launch-0.13.7.jar
Error: Invalid or corrupt jarfile /usr/local/src/prediction-io/PredictionIO/sbt/sbt-launch-0.13.7.jar

Walkthrough

[root@labs]#cd sbt
[root@labs]#wget http://dl.bintray.com/typesafe/ivy-releases/org.scala-sbt/sbt-launch/0.13.7/sbt-launch.jar
[root@labs]#mv sbt-launch.jar sbt-launch-0.13.7.jar
[root@labs]#./make-distribution.sh

2015-06-24

Apache Spark on Windows 7 in standalone mode


Nếu muốn xài trên Windows, install nhanh để xài tạm cho mấy course trên Udemy hay Coursera. Follow theo tut

How to run Apache Spark on Windows7 in standalone mode

tại https://nishutayaltech.blogspot.com/2015/04/how-to-run-apache-spark-on-windows7-in.html

Nếu thích thì dùng máy ảo, mình thường dùng CentOS, tự install. Không muốn mất công install thì xài CDH với VirtualBox cho nhanh.

Tóm tắt lại như sau:


  1. Install Java, set JAVA_HOME
  2. Install Scala, set SCALA_HOME, add %SCALA_HOME%\bin vào PATH
  3. Install SBT, set SBT_HOME, add %SBT_HOME%\bin vào PATH
  4. Download và install Spark prebuilt với Hadoop, untar với 7-zip (ext download về là tgz hay tar), set SPARK_HOME, add %SPARK_HOME%\bin vào PATH
  5. Chạy thử spark-shell nếu báo lỗi Your hostname, xxxxxx resolves to a loopback/non-reachab le address: xxxx:0:0:0:xxxx:xxxx:xxx:xxxx%wlan2, but we couldn't find any extern al IP address! thì dùng SPARK_LOCAL_HOSTNAME=localhost D:\dev\spark-1.6.1-bin-hadoop2.6\bin\spark-shell hoặc spark-shell -c spark.driver.host=localhost hoặc dùng Environment Variables
  6. Nếu báo lỗi java.lang.RuntimeException: java.lang.RuntimeException: The root scratch dir: /tmp/hive on HDFS should be writable. Current permissions are: rwx------ thì download winutils.exe giả sử vào D:\dev\hadoop-2.6.0\bin khi đó /tmp/hive sẽ tương ứng với D:\tmp\hive. Set HADOOP_HOME=D:\dev\hadoop-2.6.0 và chmod: D:\dev\hadoop-2.6.0\bin\winutils.exe chmod 777 D:\tmp\hive


2015-05-27

Evaluation of recommender/recommendation Systems

 Note những thứ cần chú ý khi thực hiện evaluation cho hệ thống RS

Tham khảo các tài liệu

  • Evaluating recommender systems [Zaier, Z. et al, 2008]
  • Evaluation of recommender systems- A new approach [F.H. del Olmo et al., 2008]
  • Evaluating recommender systems [Joost de Wit., 2008]
  • A survey of accuracy evaluation metrics of recommendation tasks [Gunawardana, A. & Shani, G., 2009], Microsoft Research
  • Beyond accuracy: evaluating recommender systems by coverage and serendipity [Ge M., 2010]
  • Dimensions and metrics for evaluating recommendation systems [Avazpour, I. et al., 2014]
  • Evaluation of recommender systems [Radek Pel´anek, 2015]
  • Information and recommender systems, Wiley, ISBN: 978-1-84821-754-6, [Elsa Negre, 2015]

Trên Quora
https://www.quora.com/How-do-you-measure-and-evaluate-the-quality-of-recommendation-engines

Đo lường tính chính xác (performance measures)

Đánh giá hệ thống RS là việc quan trọng để xác định các thuật toán lựa chọn là hiệu quả và phù hợp với tập dữ liệu. Việc thực hiện có thể thông qua 2 dạng thực nghiệm/thí nghiệm (experimental research) vs. nghiên cứu bằng cách quan sát (non-experimental/observational research).

Non-experimental/observational research có thể tiến hành thông qua:

  • Surveys/questionnaire
  • Case studies
  • Focus group

Phần sau chỉ focus vào experimental research.

Giả lập hành vi người dùng (simulating user behavior)

Để đánh giá thuật toán offline cần thực hiện giả lập quá trình online mà hệ thống thực hiện ra gợi ý cho tới khi người dùng sử dụng dự đoán (hay cách người dùng thực sự sử dụng recommendation). Việc này thường thực hiện bằng cách sử dụng dữ liệu quá khứ của người dùng và giấu đi một phần dữ liệu để giả lập người dùng sẽ thực hiện rate item như thế nào.

Việc sử dụng dữ liệu quá khứ ngầm giả định sở thích của user không phụ thuộc nhiều vào hệ thống RS. Có nghĩa cách người dùng thao tác với item không cần thông qua danh sách RS. Trong trường hợp chỉ đánh giá việc user thao tác thông qua danh sách recommendation list không thỏa mãn giả định này.

Trường hợp chỉ đánh giá chỉ thông recommendation list sẽ thực hiện online và sử dụng các metric như Click Through Rate (CTR). Nếu chỉ xem xét việc user chọn xem từ recommendation list sẽ có hạn chế quan trọng nhất là:

  • Phải thực hiện theo dõi user behavior trước khi đánh giá
  • Mỗi lần thay đổi RS phải thực hiện theo dõi lại và không thể dùng dữ liệu quá khứ do RS ảnh hưởng tới hành vi user.

Hold-out

Là một phương pháp thực hiện chia dataset thành 2 phần gọi là training set và test set. Những phần này có thể có tỷ lệ khác nhau. Việc thực hiện được tiến hành bằng cách chọn ngẫu nhiên một vài rating từ tất cả (hoặc một số) user. Phương pháp này còn gọi là leave-k-out.

Trong một số nghiên cứu, tỷ lệ này thường được chọn là 80% và 20%. Ví dụ dữ liệu người dùng truy cập movies trong 6 tháng sẽ được chia thành 2 phần:

  • Một phần 5 tháng dùng để chạy recommendation
  • Và data trong 1 tháng còn lại được dùng để đánh giá hệ thống.

Đôi khi tỷ lệ này được chạy từ 0.2 đến 0.95 với bước nhảy là 0.05 để tính toán cho từng trường hợp trước khi chạy trung bình để cho ra kết quả cuối cùng.

Leave-one-out

Là trường hợp của leave-k-out với k = 1. Với phương pháp này, một active user sẽ để lại một rated item. Kỹ thuật này có một số nhược điểm ví dụ như overfitting và có độ tính toán phức tạp

Overfitting có thể do training set size quá nhỏ hay/hoặc model quá phức tạp.

K-fold

Một biến thể khác của hold-out là m-fold cross-validation hay k-fold cross-validation. Với phương pháp này dataset được chia thành m fold (m phần) không chồng lấp. Với mỗi lượt một fold được chọn làm test set trong khi các fold khác dùng làm training set. Kỹ thuật này được cho là phù hợp để đánh giá hệ thống có người sử dụng mới.

Đo lường

Các chỉ số về độ chính xác theo thống kê (statistical accuracy metrics)

Đối với hệ thống dùng explicit feedback (hay rating) việc đánh giá RS có thể thực hiện sử dụng các metric liên quan đến thống kê. Hai cách đo lường sai số (độ đo sai số – error metrics) thường được sử dụng là Mean Absolute Error (MSE, tạm dịch sai số tuyệt đối trung bình) và Root Mean Squared Error (RMSE, tạm dịch sai số bình phương trung bình [căn] quân phương hay sai số quân phương).

Đây cũng là hai metric thông dụng nhất được sử dụng khi đo lường độ chính xác của dự đoán. Ví dụ hệ thống phát sinh \hat{r}_{ui} là một dự đoán rating cho test set \mathcal{T} của một cặp user-item (ui) thực sự có rating là r_{ui}.

RMSE giữa giá trị dự đoán và giá trị thực tế là:

RMSE=\sqrt{\dfrac{1}{\left|\mathcal{T}\right|}\displaystyle\sum_{(u,i)\in\mathcal{T}}{({\hat{r}}_{ui}-r_{ui})}^2}

trong khi đó

MAE=\sqrt{\dfrac{1}{\left|\mathcal{T}\right|}\displaystyle\sum_{(u,i)\in\mathcal{T}}\left|{\hat{r}}_{ui}-r_{ui}\right|}

Tuy nhiên đo lường sai số thông thường không thích hợp lắm với trường hợp dataset dạng binary nhị phân.

Khi so sánh với MAERMSE bất lợi hơn khi gặp những sai số lớn.

Ví dụ 1 hệ thống với 4 rating ẩn, RMSE sẽ ưu tiên hệ thống sai 2 point (khoảng cách lỗi là 2) trong 3 lần rating và 0 (rating đúng) hơn hệ thống cho sai 3 point và đúng trong 3 trường hợp còn lại.

Normalized RMSE (NRMSE) và Normalized MAE (NMAE) là hai phiên bản của RMSE và MAE khi được chuẩn hóa bằng khoảng cách rating (ví dụ r_{max}-r_{min}).

NRMSE=\dfrac{RMSE}{r_{max}-r_{min}}

NMAE=\dfrac{MAE}{r_{max}-r_{min}}

Average RMSE và Average MAE là điều chỉnh để dùng với test set không cân bằng. Ví dụ nếu trong test set có mặt hàng phân bố không đều. Nếu có một error thu được từ mặt hàng phổ biến thì với cách tính thông thường lỗi này sẽ ảnh hưởng nặng nề lên chỉ số. Thay vì vậy sẽ tính RMSE hay MAE riêng lẻ cho từng item và sau đó lấy trung bình. Tương tự như vậy RMSE và MAE có thể tính riêng lẻ cho từng user nếu test set có dấu hiệu phân bố người dùng không đều.

Trong một số ứng dụng ngữ nghĩa của rating không chỉ đơn thuần tác động vào sai số bằng độ lớn của chính nó. Trong một số trường hợp d(\hat{r},r) nên được biến đổi theo 1 cách phù hợp hơn là lấy bình phương của hiệu số hay trị tuyệt đối.

Ví dụ trong 1 số ứng dụng với 3 mức “thích”, “trung lập” và “không thích” thì việc giới thiệu một movie chẳng hạn mà người dùng không thích thì tồi tệ hơn việc không đề xuất. Trong trường hợp này một quy tắc như d(3,1) = 5d(2,1) = 3d(3,2) = 3d(1,2) = 1d(2,3) = 1, và d(1,3) = 2 sẽ cho kết quả hợp lý hơn.

Phản hồi ngầm định (implicit feedback context)

Đối với hệ thống RS dựa trên implicit feedback, việc đánh giá hệ thống có ngữ cảnh tương tự ngữ cảnh của hệ thống truy vấn thông tin (information retrieval) hay ngữ cảnh phân lớp (classification context). Một trong những cách thực hiện là sử dụng các metric liên quan đến precision và recall.

Precision & recall

Đối với ngữ cảnh hệ thống truy vấn thông tin

precision=\dfrac{\left|RET\cap R E L\right|}{\left|RET\right|}

recall=\dfrac{\left|RET\cap R E L\right|}{\left|REL\right|}

Đối với ngữ cảnh phân lớp

precision=\dfrac{tp}{tp+fp}

recall=\dfrac{tp}{tp+fn}

RET hay retrieved documents là kết quả do hệ thống xuất ra đối với một query cụ thể. REL hay relevant documents là danh sách tất cả các tài liệu có liên quan (trong trường hợp RS có thể ví dụ là danh sách item mà user có thao tác).

Thông thường precision lấy n kết quả đầu tiên của RET hay còn gọi là precision at n hay P@n với giả định user chỉ xem xét n kết quả đầu tiên.

Ví dụ trong trường hợp recommendation cho movies

precision=\dfrac{\left|good\ movies\ recommended\right|}{\left|all\ recommendations\right|}

F-measure

Một metric kết hợp giữa precision và recall có thể sử dụng là F-measure hay balanced F-score thông thường sử dụng là F1. Công thức F_\beta

F_\beta=\left(1+\beta^2\right)·\dfrac{precision· recall}{β2·precision+recall}

Các metric khác

Các metric khác có thể sử dụng như Receiver Operating Characteristics (ROC)

Ranking/list recommendation

Rank score

Rank score là mở rộng của recall để đo lường tính chính xác về vị trí một item trong danh sách.

rankscore=\dfrac{{rankscore}_p}{{rankscore}_{max}}

Trong đó:

  • {rankscore}_p=\displaystyle\sum_{i\in h}2^{-\frac{rank\left(i\right)-1}{\alpha}}

  • {rankscore}_{max}=\displaystyle\sum_{i=1}^{\left|T\right|}2^{-\frac{i-1}{\alpha}}

Trong đó:

  • h là tập item được gợi ý đúng (hits)
  • rank là vị trí position/rank của item
  • T là tập hợp các item cần quan tâm
  • α ranking half life một exponential reduction factor (hệ số giảm rank)

Ví dụ: \left|T\right| = 3, ranking half life α=2

RankHit?
1
2
3
4
5

{rankscore}_{p}=\dfrac{1}{2^\frac{2-1}{2}}+\frac{1}{2^\frac{3-1}{2}}+\frac{1}{2^\frac{4-1}{2}}=1.56

{rankscore}_{max}=\dfrac{1}{2^\frac{1-1}{2}}+\frac{1}{2^\frac{2-1}{2}}+\frac{1}{2^\frac{3-1}{2}}=2.21

rankscore=\dfrac{{rankscore}_p}{{rankscore}_{max}}\approx0.71

Liftindex

Giả định ranked list được chia làm 10 phần bằng nhau gọi là các deciles (mỗi phần đại diện 1/10 mẫu) với

\displaystyle\sum_{i=1}^{10}S_i=\left|h\right|

gọi là linear reduction factor với h là tập hợp correct hits.

liftindex= \begin{cases} \dfrac{1 {\times} S_1+0.9 {\times} S_2+{\dots}+0.1 {\times} S_{10}}{\sum_{i=1}^{10}S_i} &\text{:nếu } \left|h\right| > 0 \\ 0 \end{cases}

RankHit?
1
2
3
4
5

liftindex=\dfrac{0.8\times1+0.6\times1+0.4\times1}{3}=0.6

Discounted cumulative gain (DCG)

{DCG}_{pos}={rel}_1+\displaystyle\sum_{i=2}^{pos}\dfrac{{rel}_i}{\log_2{i}}

với

  • pos là vị trí cần tính từ 1 để tích lũy độ liên quan (relevance).
  • {rel}_i độ liên quan của gợi ý (recommendation) tại vị trí i.

Idealized discounted cumulative gain (IDCG) là giá trị maximum possible (ideal) của DCG

{IDCG}_{pos}={rel}_1+\displaystyle\sum_{i=2}^{\left|h\right|-1}\dfrac{{rel}_i}{\log_2{i}}

Normalized discounted cumulative gain (NDCG)

{NDCG}_{pos}=\dfrac{{DCG}_{pos}}{{IDCG}_{pos}}

Ví dụ: n = 5

RankHit?
1
2
3
4
5

{DCG}_5=\dfrac{1}{\log_2{2}}+\dfrac{1}{\log_2{3}}+\frac{1}{\log_2{4}}=2.13

{IDCG}_5=1+\dfrac{1}{\log_2{2}}+\dfrac{1}{\log_2{3}}=2.63

{NDCG}_{pos}=\dfrac{{DCG}_{pos}}{{IDCG}_{pos}}\approx0.81

Biểu diễn DCG dạng khác (xem thêm tại Kaggle)

{DCG}_k=\displaystyle\sum_{i=1}^{k}\dfrac{2^{{rel}_i}-1}{\log_2{(i+1)}}

Average precision (AP)

Độ chính xác trung bình (AP) là precision metric có xếp hạng thực hiện điều chỉnh nhấn mạnh các item được dự đoán đúng có thứ tự sắp xếp cao (high ranked).

ap@n=\displaystyle\sum_{k=1}^{n}{P(k)/\min(m,n)}

Nếu mẫu số (denominator) bằng zero thì kết quả là zero (xem thêm tại Kaggle).

Ví dụ: n = 5

RankHit?
1
2
3
4
5

AP=\left(\dfrac{1}{2}+\dfrac{2}{3}+\dfrac{3}{4}\right)·13≈0.639

Trường hợp dự đoán chính xác (hit) có vị trí đầu tiên thì score sẽ cao hơn

RankHit?
1
2
3
4
5

AP=\left(\dfrac{1}{1}+\dfrac{2}{4}+\dfrac{3}{5}\right)·13=0.7

Mean Average Precision

Thực hiện tính trung bình AP cho N user

MAP@n=\displaystyle\sum_{i=1}^{N}{ap@n_i/N}

Các metric khác

Các metric khác có thể sử dụng như sử dụng tương quan xếp hạng Spearman’s rank correlation, Kendall rank correlation.