├── .gitignore ├── README.md ├── data ├── README.txt ├── color_asm68.model ├── color_asm75.model ├── grayall_asm.model ├── grey_asm.model ├── haarcascade_frontalface_alt.xml ├── muct │ ├── README.txt │ ├── download │ │ └── README.txt │ ├── genPTS.py │ ├── muct76.def │ └── prepare.sh └── muct76.model └── src ├── CMakeLists.txt ├── Doxyfile.in ├── annotation ├── CMakeLists.txt ├── annotation.pro ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── photoview.cpp ├── photoview.h ├── pointspaint.cpp ├── pointspaint.h ├── singlephotoview.cpp └── singlephotoview.h ├── cmake └── Modules │ └── FindOpenCV.cmake ├── demo ├── CMakeLists.txt ├── XGetopt.cpp ├── XGetopt.h ├── demo.py └── main.cpp ├── lib ├── CMakeLists.txt ├── afreader.cpp ├── afreader.h ├── asmmodel.cpp ├── asmmodel.h ├── modelfile.cpp ├── modelfile.h ├── modelimage.cpp ├── modelimage.h ├── python.cpp ├── shapeinfo.cpp ├── shapeinfo.h ├── shapemodel.cpp ├── shapemodel.h ├── shapevec.cpp ├── shapevec.h ├── similaritytrans.cpp └── similaritytrans.h └── src.kdev4 /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | src/build 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | asmlib-opencv 2 | ============= 3 | 4 | This repo is a fork of https://code.google.com/p/asmlib-opencv/ to demonstrate how 3rd party C++ modules can be integrated into the Python bindings for OpenCV. This code depends on https://github.com/Itseez/opencv/pull/1571. 5 | 6 | # Build instructions 7 | The following lines are copy-and-pastable instructions to build the OpenCV branch in the Pull Request above and this repository. 8 | 9 | ## Build OpenCV branch 10 | This will download and build the code from the dependant branch and then install it into a temporary directory. 11 | 12 | ```bash 13 | git clone https://github.com/derfred/opencv.git opencv_external 14 | cd opencv_external 15 | git checkout external_python_modules 16 | mkdir build 17 | cd build 18 | cmake -D CMAKE_INSTALL_PREFIX=../../opencv_external_install .. 19 | make 20 | make install 21 | ``` 22 | 23 | On my machine (MacOS Mountain Lion with Python installed via homebrew) I had to specify the Python executable. Should you have problems building, try the following changes to the cmake line: 24 | ```bash 25 | cmake -D CMAKE_INSTALL_PREFIX=../../opencv_external_install -D PYTHON_EXECUTABLE=/usr/local/bin/python -D PYTHON_INCLUDE_DIR=/usr/local/Cellar/python/2.7.4/Frameworks/Python.framework/Headers -D PYTHON_LIBRARY=/usr/local/Cellar/python/2.7.4/Frameworks/Python.framework/Versions/2.7/lib/libpython2.7.dylib .. 26 | ``` 27 | 28 | ## Build ASMLib (this repository) 29 | ```bash 30 | git clone https://github.com/derfred/asmlib-opencv.git 31 | cd asmlib-opencv/src 32 | mkdir build 33 | cd build 34 | OpenCV_DIR=../../../opencv_external_install/share/OpenCV/ cmake .. 35 | make 36 | ``` 37 | 38 | # Run the demo 39 | If you have followed the steps above then the following will run the Python demo on your webcam, assuming your CWD is src/demo. 40 | ```bash 41 | PYTHONPATH="../build/lib:$PYTHONPATH" python demo.py -f -pc -m ../../data/muct76.model -C ../../data/haarcascade_frontalface_alt.xml 42 | ``` 43 | -------------------------------------------------------------------------------- /data/README.txt: -------------------------------------------------------------------------------- 1 | Here are several trained models. 2 | 3 | muct/ has a set of scripts that can generate training file from MUCT database, 4 | then you can train your own model by yourself. See muct/README.txt for details. 5 | 6 | If you want to train you own models with your own landmark database, please 7 | check Wiki ( http://code.google.com/p/asmlib-opencv/wiki/BuildModel ). 8 | -------------------------------------------------------------------------------- /data/muct/README.txt: -------------------------------------------------------------------------------- 1 | Here are some scripts that generate training files from MUCT landmarked face 2 | database ( http://www.milbo.org/muct ) 3 | 4 | 0. The scripts here should work on Linux/Mac. On windows, it might work 5 | with Cygwin. To create your own data set, you don't have to use Linux, 6 | check http://code.google.com/p/asmlib-opencv/wiki/BuildModel 7 | 8 | 1. Run ./prepare.sh , it will download MUCT dataset and prepare training 9 | set automatically. You need bash and Python 2/3. 10 | 11 | 2. If you have ImageMagick, uncomment commented lines in prepare.sh, you will 12 | be able to train on flipped images. 13 | 14 | 3. If succeeded, you will see some ".list" files in this directory. 15 | 16 | 4. Build the model: 17 | PATH_TO_DEMO/demo -b -m muct76.model -d muct76.def -l camA_Q.list 18 | NOTE: currently all the images are read into memory before training, so 19 | camA_all will consume 3Gb memory... So please try camA_Q if you 20 | don't have enough memory before the problem is solved... 21 | 22 | 5. Then you can view or use the model file, eg: 23 | PATH_TO_DEMO/demo -v -m muct76.model 24 | OR 25 | PATH_TO_DEMO/demo -f -m muct76.model -pc -C 26 | 27 | 6. If you have any questions, submit a issue in Google Code or contact me 28 | directly. 29 | -------------------------------------------------------------------------------- /data/muct/download/README.txt: -------------------------------------------------------------------------------- 1 | Downloaded files will be stored here. 2 | -------------------------------------------------------------------------------- /data/muct/genPTS.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | datasets = { 4 | 'camAll_all': '.*', 5 | 'camABDE_all': 'i.*[abde]-.*', 6 | 'camAB_all': 'i.*[ab]-.*', 7 | 'camAB_Q': 'i.*q[ab]-.*', 8 | 'camA_all': 'i.*a-.*', 9 | 'camA_Q': 'i.*qa-.*', 10 | 'camB_Q': 'i.*qb-.*' 11 | } 12 | ptsLists = dict() 13 | import re 14 | for iName in datasets.keys(): 15 | ptsLists[iName] = list() 16 | datasets[iName] = re.compile(datasets[iName]) 17 | 18 | import os 19 | with open("./muct-landmarks/muct76-opencv.csv", "r") as f: 20 | for l in f: 21 | fields = l.split(',') 22 | dataName = fields[0] 23 | if dataName == "name": 24 | continue 25 | n = (len(fields) - 2) / 2 26 | filename = "jpg/%s.jpg.pts" % dataName 27 | with open(filename, "w") as fPts: 28 | fPts.write("%d\n" % n) 29 | prevX = 0 30 | prevY = 0 31 | for x, y in zip(fields[2::2], fields[3::2]): 32 | if x == '0' and y == '0': 33 | # If this point doesn't exist... 34 | x = prevX 35 | y = prevY 36 | fPts.write("%.0f %.0f\n" % (float(x), float(y))) 37 | prevX = x 38 | prevY = y 39 | 40 | if os.path.exists("jpg/%s.jpg" % dataName): 41 | for iName, iReg in datasets.items(): 42 | if iReg.match(dataName): 43 | ptsLists[iName].append(filename) 44 | 45 | for iName, iList in ptsLists.items(): 46 | with open(iName + ".list", "w") as fLst: 47 | for l in iList: 48 | fLst.write("%s\n" % l); 49 | 50 | -------------------------------------------------------------------------------- /data/muct/muct76.def: -------------------------------------------------------------------------------- 1 | #Number of Mark points 2 | 76 3 | # number of paths 4 | 1 5 | # face contour 6 | 76 1 7 | 8 | #r.y -= r.height*? 9 | 0.1 10 | #r.x -= r.width*? 11 | 0.1 12 | #r.width *= ? 13 | 1.2 14 | #r.height *= ? 15 | 1.45 16 | 17 | #step?*100/sqrt(area) 18 | 1.0 19 | 20 | # init scale ratio when searching 21 | 0.65 22 | # init X offset when searching 23 | 0 24 | # init Y offset when searching 25 | 0.25 26 | -------------------------------------------------------------------------------- /data/muct/prepare.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Downloading files if necessary..." 4 | cd download 5 | wget -nc http://muct.googlecode.com/files/muct-landmarks-v1.tar.gz 6 | wget -nc http://muct.googlecode.com/files/muct-{a,b,c,d,e}-jpg-v1.tar.gz 7 | cd .. 8 | 9 | for i in download/muct-*.tar.gz; do 10 | echo "Extracting $i..." 11 | tar xfzk $i 12 | done 13 | 14 | #echo "Flipping images (imagemagick needed, slow)..." 15 | #for i in `find jpg -regex '.*i[0-9].*\.jpg'`; do 16 | #mainname=`basename $i` 17 | #newname=`dirname $i`/ir${mainname##*i} 18 | #convert -flip $i $newname 19 | #done 20 | 21 | ./genPTS.py 22 | 23 | echo "Training files are ready. You can run something like: " 24 | echo " PATH_TO_DEMO/demo -b -m muct76.model -d muct76.def -l camA_Q.list" 25 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED (VERSION 2.6) 2 | PROJECT( asmlib-opencv-proj ) 3 | FIND_PACKAGE( OpenCV REQUIRED ) 4 | SET(LIB_SRC_LIST modelfile.cpp asmmodel.cpp shapevec.cpp similaritytrans.cpp afreader.cpp modelimage.cpp shapemodel.cpp shapeinfo.cpp) 5 | 6 | #INCLUDE_DIRECTORIES( ${OPENCV_INCLUDE_DIR} ) 7 | OPTION(ENABLE_ANNOTATOR "Compile the GUI point annotator (Qt Needed)" OFF) 8 | 9 | IF (ENABLE_ANNOTATOR) 10 | ADD_SUBDIRECTORY (annotation) 11 | ENDIF (ENABLE_ANNOTATOR) 12 | 13 | FIND_PACKAGE( Doxygen ) 14 | IF (DOXYGEN_FOUND) 15 | CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) 16 | ADD_CUSTOM_TARGET(doc 17 | ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile 18 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 19 | COMMENT "Generating API and source documentation with Doxygen" VERBATIM 20 | ) 21 | ENDIF(DOXYGEN_FOUND) 22 | 23 | ADD_SUBDIRECTORY(demo) 24 | ADD_SUBDIRECTORY(lib) 25 | -------------------------------------------------------------------------------- /src/annotation/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PROJECT( img-annotator ) 2 | 3 | FIND_PACKAGE(Qt4 REQUIRED) 4 | FIND_PACKAGE(OpenCV REQUIRED) 5 | INCLUDE(${QT_USE_FILE}) 6 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) 7 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) 8 | QT4_WRAP_CPP(annotator_MOCS mainwindow.h singlephotoview.h pointspaint.h photoview.h) 9 | QT4_WRAP_UI(annotator_UIS mainwindow.ui) 10 | ADD_EXECUTABLE(annotator main.cpp mainwindow.cpp singlephotoview.cpp pointspaint.cpp photoview.cpp ${annotator_MOCS} ${annotator_UIS}) 11 | TARGET_LINK_LIBRARIES(annotator ${QT_LIBRARIES} ${OpenCV_LIBS}) 12 | -------------------------------------------------------------------------------- /src/annotation/annotation.pro: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------- 2 | # Project created by QtCreator 2010-02-23T09:42:00 3 | # ------------------------------------------------- 4 | TARGET = annotation 5 | TEMPLATE = app 6 | INCLUDEPATH += /usr/include/opencv 7 | LIBS += -lcxcore \ 8 | -lcv \ 9 | -lhighgui \ 10 | -lcvaux \ 11 | -lml 12 | SOURCES += main.cpp \ 13 | mainwindow.cpp \ 14 | singlephotoview.cpp \ 15 | pointspaint.cpp \ 16 | photoview.cpp 17 | HEADERS += mainwindow.h \ 18 | singlephotoview.h \ 19 | pointspaint.h \ 20 | photoview.h 21 | FORMS += mainwindow.ui 22 | -------------------------------------------------------------------------------- /src/annotation/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mainwindow.h" 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | MainWindow w; 8 | w.show(); 9 | return a.exec(); 10 | } 11 | -------------------------------------------------------------------------------- /src/annotation/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | #include 4 | #include "cv.h" 5 | #include "highgui.h" 6 | 7 | MainWindow::MainWindow(QWidget *parent) : 8 | QMainWindow(parent), 9 | ui(new Ui::MainWindow) 10 | { 11 | ui->setupUi(this); 12 | 13 | ui->graphicsView->allowAddPoints = false; 14 | } 15 | 16 | MainWindow::~MainWindow() 17 | { 18 | delete ui; 19 | } 20 | 21 | void MainWindow::changeEvent(QEvent *e) 22 | { 23 | QMainWindow::changeEvent(e); 24 | switch (e->type()) { 25 | case QEvent::LanguageChange: 26 | ui->retranslateUi(this); 27 | break; 28 | default: 29 | break; 30 | } 31 | } 32 | 33 | void MainWindow::updateImg() 34 | { 35 | QString ptsName = fileList[curFile]; 36 | QString imgName = ptsName.left(ptsName.lastIndexOf('.')); 37 | // cv::Mat m = cv::imread(imgName.toStdString()); 38 | // qDebug(imgName.toStdString().c_str()); 39 | // imshow("m", m); 40 | // cvWaitKey(0); 41 | // imwrite("tmp.jpg", m); 42 | ui->lblFilename->setText(imgName); 43 | QImage p(imgName); 44 | //qDebug("c: %d", p.isGrayscale()); 45 | ui->graphicsView->setImage(QPixmap::fromImage(p)); 46 | ui->viewEdit->setImage(imgName); 47 | //ui->graphicsView->setImage(imgName); 48 | ui->chkUse->setChecked(blUse[curFile]); 49 | ui->chkColor->setChecked(this->blColored[curFile]); 50 | 51 | FILE *fp=fopen(ptsName.toLocal8Bit(), "r"); 52 | QList ql; 53 | if (fp){ 54 | int n; 55 | int a,b; 56 | fscanf(fp, "%d", &n); 57 | for (int i=0;igraphicsView->setPointList(QList(), ql); 64 | ui->viewEdit->setPointList(ql); 65 | 66 | QString s; 67 | ui->editCur->setText(s.setNum(curFile)); 68 | ui->lblTotal->setText(s.setNum(fileList.size())); 69 | } 70 | 71 | void MainWindow::on_actOpenFile_triggered() 72 | { 73 | QString fileName; 74 | fileName = QFileDialog::getOpenFileName(this, 75 | tr("Open List file"), "./", tr("List Files (*.txt);;All Files (*.*)")); 76 | FILE *fp = fopen(fileName.toStdString().data(), "r"); 77 | FILE *fp2 = fopen((fileName.left(fileName.lastIndexOf('.'))+".use").toStdString().data(), "r"); 78 | FILE *flist = fopen((fileName.left(fileName.lastIndexOf('.'))+".list").toStdString().data(), "w"); 79 | char s[200]; 80 | while (!feof(fp)){ 81 | fscanf(fp, "%s\n", s); 82 | this->fileList.push_back(fileName.left(fileName.lastIndexOf('/')+1)+s); 83 | if (fp2){ 84 | char c[8]; 85 | fscanf(fp2, "%c", c); 86 | this->blUse.push_back(c[0]=='y'); 87 | this->blColored.push_back(false); 88 | 89 | if (c[0]=='y' && fileList.size()<241) 90 | fprintf(flist, "%s\n", fileList.back().toStdString().data()); 91 | } 92 | else { 93 | this->blUse.push_back(true); 94 | this->blColored.push_back(false); 95 | } 96 | } 97 | fclose(flist); 98 | if (fp2) 99 | fclose(fp2); 100 | fclose(fp); 101 | this->curFile = 0; 102 | ui->btnPrev->setEnabled(false); 103 | if (curFile==fileList.size()-1) ui->btnNext->setEnabled(false); 104 | updateImg(); 105 | } 106 | 107 | void MainWindow::on_btnPrev_clicked() 108 | { 109 | curFile--; 110 | if (curFile<0) curFile=0; 111 | if (curFile==0) ui->btnPrev->setEnabled(false); 112 | if (curFilebtnNext->setEnabled(true); 113 | updateImg(); 114 | } 115 | 116 | void MainWindow::on_btnNext_clicked() 117 | { 118 | curFile++; 119 | if (curFile>=fileList.size()) curFile=fileList.size()-1; 120 | if (curFile>0) ui->btnPrev->setEnabled(true); 121 | if (curFile==fileList.size()-1) ui->btnNext->setEnabled(false); 122 | updateImg(); 123 | } 124 | 125 | void MainWindow::on_chkColor_clicked(bool checked) 126 | { 127 | this->blColored[curFile] = checked; 128 | } 129 | 130 | void MainWindow::on_chkUse_clicked(bool checked) 131 | { 132 | blUse[curFile] = checked; 133 | } 134 | 135 | void MainWindow::on_actSaveFile_triggered() 136 | { 137 | QString fileName; 138 | fileName = QFileDialog::getSaveFileName(this, 139 | tr("Save List file"), "./", tr("use Files (*.use);;All Files (*.*)")); 140 | FILE *fp = fopen(fileName.toStdString().data(), "w"); 141 | if (fp){ 142 | for (int i=0; i &ql = ui->graphicsView->dstPointPaint.getPointList(); 155 | int n = ql.size(); 156 | fprintf(fp, "%d\n", n); 157 | for (int i=0;ion_btnNext_clicked(); 162 | } 163 | 164 | void MainWindow::on_editCur_editingFinished() 165 | { 166 | int a = ui->editCur->text().toInt(); 167 | if (a>=0 && aupdateImg(); 170 | } 171 | 172 | void MainWindow::on_btnCmpSaveNext_clicked() 173 | { 174 | QString ptsName = fileList[curFile]; 175 | 176 | FILE *fp=fopen(ptsName.toLocal8Bit(), "w"); 177 | const QList &ql = ui->viewEdit->getPointList(); 178 | int n = ql.size(); 179 | fprintf(fp, "%d\n", n); 180 | for (int i=0;ion_btnNext_clicked(); 185 | } 186 | -------------------------------------------------------------------------------- /src/annotation/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace Ui { 9 | class MainWindow; 10 | } 11 | 12 | class MainWindow : public QMainWindow { 13 | Q_OBJECT 14 | public: 15 | MainWindow(QWidget *parent = 0); 16 | ~MainWindow(); 17 | 18 | protected: 19 | void changeEvent(QEvent *e); 20 | 21 | private: 22 | Ui::MainWindow *ui; 23 | QList< QString > fileList; 24 | QList< bool > blUse; 25 | QList< bool > blColored; 26 | int curFile; 27 | 28 | void updateImg(); 29 | 30 | private slots: 31 | void on_btnCmpSaveNext_clicked(); 32 | void on_editCur_editingFinished(); 33 | void on_btnSaveNext_clicked(); 34 | void on_actSaveFile_triggered(); 35 | void on_chkUse_clicked(bool checked); 36 | void on_chkColor_clicked(bool checked); 37 | void on_btnNext_clicked(); 38 | void on_btnPrev_clicked(); 39 | void on_actOpenFile_triggered(); 40 | }; 41 | 42 | #endif // MAINWINDOW_H 43 | -------------------------------------------------------------------------------- /src/annotation/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 600 10 | 400 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 21 | 上一个 22 | 23 | 24 | 25 | 26 | 27 | 28 | TextLabel 29 | 30 | 31 | 32 | 33 | 34 | 35 | 下一个 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | TextLabel 46 | 47 | 48 | 49 | 50 | 51 | 52 | 0 53 | 54 | 55 | 56 | Single 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | QFrame::StyledPanel 66 | 67 | 68 | QFrame::Raised 69 | 70 | 71 | 72 | 73 | 74 | Color Image 75 | 76 | 77 | 78 | 79 | 80 | 81 | Use this PTS 82 | 83 | 84 | 85 | 86 | 87 | 88 | Save and Next 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | Compare 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | Save and Next 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 0 125 | 0 126 | 600 127 | 25 128 | 129 | 130 | 131 | 132 | 文件 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | TopToolBarArea 142 | 143 | 144 | false 145 | 146 | 147 | 148 | 149 | 150 | 打开 151 | 152 | 153 | 154 | 155 | Save 156 | 157 | 158 | 159 | 160 | 161 | 162 | SinglePhotoView 163 | QGraphicsView 164 |
singlephotoview.h
165 |
166 | 167 | PhotoView 168 | QGraphicsView 169 |
photoview.h
170 | 171 | selectedPointUpdated(int) 172 | setMirrorPoint(int) 173 | 174 |
175 |
176 | 177 | 178 | 179 | viewRef 180 | selectedPointUpdated(int) 181 | viewEdit 182 | setMirrorPoint(int) 183 | 184 | 185 | 230 186 | 165 187 | 188 | 189 | 404 190 | 167 191 | 192 | 193 | 194 | 195 | viewEdit 196 | selectedPointUpdated(int) 197 | viewRef 198 | setMirrorPoint(int) 199 | 200 | 201 | 353 202 | 257 203 | 204 | 205 | 202 206 | 257 207 | 208 | 209 | 210 | 211 |
212 | -------------------------------------------------------------------------------- /src/annotation/photoview.cpp: -------------------------------------------------------------------------------- 1 | #include "photoview.h" 2 | #include 3 | #include 4 | #include 5 | using std::cout; 6 | using std::endl; 7 | 8 | PhotoView::PhotoView(QWidget *parent) 9 | : QGraphicsView(parent){ 10 | this->setMouseTracking(true); 11 | setScene(&gScene); 12 | //gScene.addRect(0, 0, 90, 90); 13 | scaleFactor = 1; 14 | pixItem = NULL; 15 | dragging = false; 16 | allowAddPoints = true; 17 | //pointPaint.ql = &selectedPoints; 18 | 19 | 20 | // this->scale(scaleFactor, scaleFactor); 21 | this->setRenderHint(QPainter::Antialiasing); 22 | this->setRenderHint(QPainter::SmoothPixmapTransform); 23 | 24 | gScene.addItem(&pointPaint); 25 | pointPaint.setRatio(this->mapToScene(1, 0).x()-this->mapToScene(0, 0).x()); 26 | 27 | //this->fitInView(QRect(QPoint(0, 0), img->size())); 28 | //this->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); 29 | } 30 | 31 | void PhotoView::zoomIn(){ 32 | scaleFactor *= 1.2; 33 | this->scale(1.2, 1.2); 34 | this->setRenderHint(QPainter::SmoothPixmapTransform); 35 | pointPaint.setRatio(this->mapToScene(1, 0).x()-this->mapToScene(0, 0).x()); 36 | this->update(); 37 | } 38 | 39 | void PhotoView::zoomOut(){ 40 | scaleFactor /= 1.2; 41 | this->scale(1/1.2, 1/1.2); 42 | pointPaint.setRatio(this->mapToScene(1, 0).x()-this->mapToScene(0, 0).x()); 43 | this->update(); 44 | } 45 | 46 | void PhotoView::mousePressEvent(QMouseEvent *event){ 47 | QPointF qp = this->mapToScene(event->x(),event->y()); 48 | int tI = pointPaint.pickPointByMouse(qp); 49 | if (tI != -1){ 50 | pointPaint.selectPoint(tI); 51 | emit selectedPointUpdated(tI); 52 | dragging = true; 53 | } 54 | else if (allowAddPoints) { 55 | pointPaint.addPoint(qp); 56 | pointPaint.selectPoint(-1); 57 | emit selectedPointUpdated(pointPaint.getSize()); 58 | } 59 | 60 | //QGraphicsView::mousePressEvent(event); 61 | } 62 | 63 | void PhotoView::setImage(const QString & imgFileName) 64 | { 65 | // imageFileName = imgFileName; 66 | // QImage *img = new QImage(imgFileName); 67 | // this->setBackgroundBrush(*img); 68 | 69 | if (pixItem != NULL){ 70 | gScene.removeItem(pixItem); 71 | delete pixItem; 72 | } 73 | 74 | pixData = QPixmap(imgFileName);//, NULL, Qt::SmoothTransformation); 75 | pixItem = gScene.addPixmap(pixData); 76 | pixItem->setTransformationMode(Qt::SmoothTransformation); 77 | // this->setSceneRect(QRect(QPoint(0, 0), img->size())); 78 | // gScene.setSceneRect(QRect(QPoint(0, 0), img->size())); 79 | // 80 | this->setSceneRect(QRect(QPoint(0, 0), pixData.size())); 81 | gScene.setSceneRect(QRect(QPoint(0, 0), pixData.size())); 82 | this->update(); 83 | } 84 | 85 | void PhotoView::mouseReleaseEvent(QMouseEvent *event) 86 | { 87 | dragging = false; 88 | } 89 | 90 | void PhotoView::setMirrorPoint(int pId) 91 | { 92 | pointPaint.setMirrorPoint(pId); 93 | } 94 | 95 | void PhotoView::mouseMoveEvent(QMouseEvent *event) 96 | { 97 | if ( dragging ){ 98 | pointPaint.updateSelectedPoint(this->mapToScene(event->pos())); 99 | emit pointsUpdated(); 100 | return; 101 | } 102 | QPointF qp = this->mapToScene(event->x(),event->y()); 103 | int tI = pointPaint.pickPointByMouse(qp); 104 | pointPaint.highLight(tI); 105 | QGraphicsView::mouseMoveEvent(event); 106 | } 107 | 108 | void PhotoView::setPointList(const QList< QPoint > &ql){ 109 | pointPaint.clear(); 110 | for (int i=0;i 27 | #include "pointspaint.h" 28 | #include 29 | #include 30 | #include 31 | 32 | class PhotoView : public QGraphicsView 33 | { 34 | Q_OBJECT 35 | 36 | public: 37 | PhotoView(QWidget *parent = 0); 38 | 39 | void zoomIn(); 40 | void zoomOut(); 41 | 42 | void setImage(const QString & imgFileName); 43 | 44 | QList< QPoint > getPointList(){ return pointPaint.getPointList(); } 45 | void setPointList(const QList< QPoint > &ql); 46 | 47 | bool allowAddPoints; 48 | public slots: 49 | void setMirrorPoint(int pId); 50 | 51 | signals: 52 | void selectedPointUpdated(int pId); 53 | void pointsUpdated(); 54 | 55 | protected: 56 | void mousePressEvent(QMouseEvent *event); 57 | void mouseReleaseEvent(QMouseEvent *event); 58 | void mouseMoveEvent(QMouseEvent *event); 59 | 60 | QGraphicsScene gScene; 61 | PointsPaint pointPaint; 62 | 63 | // QList< QPoint > selectedPoints; 64 | 65 | double scaleFactor; 66 | 67 | private: 68 | bool dragging; 69 | QString imageFileName; 70 | 71 | QGraphicsPixmapItem *pixItem; 72 | QPixmap pixData; 73 | }; 74 | 75 | #endif // PHOTOVIEW_H 76 | -------------------------------------------------------------------------------- /src/annotation/pointspaint.cpp: -------------------------------------------------------------------------------- 1 | #include "pointspaint.h" 2 | #include 3 | 4 | PointsPaint::PointsPaint() 5 | { 6 | mouseHighlightPoint = -1; 7 | selectedPoint = -1; 8 | boundRect.setCoords(0, 0, 0, 0); 9 | setZValue(1); 10 | setColorScheme(0); 11 | } 12 | 13 | void PointsPaint::setColorScheme(int schemeId) 14 | { 15 | if (schemeId == 0){ 16 | colorBrush = Qt::magenta; 17 | colorSelectedPen = Qt::yellow; 18 | colorSelectedBrush = Qt::darkYellow; 19 | colorHighlightPen = Qt::blue; 20 | colorMirrorPen = Qt::darkRed; 21 | colorMirrorBrush = Qt::red; 22 | colorPen = Qt::black; 23 | } 24 | else { 25 | colorBrush = Qt::gray; 26 | colorSelectedPen = Qt::green; 27 | colorSelectedBrush = Qt::darkGreen; 28 | colorHighlightPen = Qt::red; 29 | colorMirrorPen = Qt::darkRed; 30 | colorMirrorBrush = Qt::red; 31 | colorPen = Qt::darkBlue; 32 | } 33 | } 34 | 35 | QRectF PointsPaint::boundingRect() const{ 36 | /* 37 | QList< QPoint >::iterator ci; 38 | for (ci = ql->begin(); ci != ql->end(); ci++){ 39 | if ( ci->x() > boundRect.right() ) 40 | boundRect.setRight(ci->x()); 41 | if ( ci->x() < boundRect.left() ) 42 | boundRect.setLeft(ci->x()); 43 | if ( ci->y() > boundRect.bottom() ) 44 | boundRect.setBottom(ci->y()); 45 | if ( ci->y() < boundRect.top() ) 46 | boundRect.setTop(ci->y()); 47 | } 48 | qDebug("%d",boundRect.top());//<<" "<setBrush(dBrush); 60 | 61 | //double ratio = this->(1,0).x()-this->mapFromScene(0, 0).x(); 62 | //qDebug("%lf\n",ratio); 63 | 64 | for (i = 0; i < ql.size(); i++){ 65 | if (selectedPoint == i){ 66 | dPen.setColor(colorSelectedPen); 67 | painter->setPen(dPen); 68 | dBrush.setColor(colorSelectedBrush); 69 | painter->setBrush(dBrush); 70 | painter->drawEllipse(QPointF(ql[i]), 4.*ratio, 4.*ratio); 71 | dBrush.setColor(colorBrush); 72 | painter->setBrush(dBrush); 73 | } 74 | else if (mouseHighlightPoint == i){ 75 | dPen.setColor(colorHighlightPen); 76 | painter->setPen(dPen); 77 | painter->drawEllipse(QPointF(ql[i]), 4.*ratio, 4.*ratio); 78 | } 79 | else if (mirrorPoint == i){ 80 | dPen.setColor(colorMirrorPen); 81 | painter->setPen(dPen); 82 | dBrush.setColor(colorMirrorBrush); 83 | painter->setBrush(dBrush); 84 | painter->drawEllipse(QPointF(ql[i]), 4.*ratio, 4.*ratio); 85 | dBrush.setColor(colorBrush); 86 | painter->setBrush(dBrush); 87 | } 88 | else { 89 | dPen.setColor(colorPen); 90 | painter->setPen(dPen); 91 | painter->drawEllipse(QPointF(ql[i]), 3.*ratio, 3.*ratio); 92 | } 93 | } 94 | } 95 | 96 | // Pick a point by mouse. mouseP is in item coordination system. 97 | int PointsPaint::pickPointByMouse(const QPointF &mouseP) const{ 98 | double minD=1e10, dist; 99 | int i, minI; 100 | 101 | for (i = 0; i < ql.size(); i++){ 102 | dist = (mouseP.x() - ql[i].x())*(mouseP.x() - ql[i].x()) + 103 | (mouseP.y() - ql[i].y())*(mouseP.y() - ql[i].y()); 104 | if ( distupdate(); 119 | } 120 | } 121 | 122 | void PointsPaint::selectPoint(int pId){ 123 | if (selectedPoint!=pId){ 124 | selectedPoint = pId; 125 | mirrorPoint = -1; 126 | this->update(); 127 | } 128 | } 129 | 130 | 131 | void PointsPaint::setMirrorPoint(int pId){ 132 | if (mirrorPoint != pId){ 133 | mirrorPoint = pId; 134 | selectedPoint = -1; 135 | this->update(); 136 | } 137 | } 138 | 139 | void PointsPaint::updateSelectedPoint(const QPointF &mouseP){ 140 | QPoint qp = mouseP.toPoint(); 141 | if (qp.x() != ql[selectedPoint].x() || qp.y() != ql[selectedPoint].y()){ 142 | ql[selectedPoint] = qp; 143 | testUpdateRect(qp); 144 | } 145 | this->update(); 146 | } 147 | 148 | // Pick a point by mouse. mouseP is in item coordination system. 149 | void PointsPaint::addPoint(const QPointF &mouseP) { 150 | ql.append(mouseP.toPoint()); 151 | testUpdateRect(mouseP.toPoint()); 152 | this->update(); 153 | } 154 | 155 | void PointsPaint::testUpdateRect(const QPoint &qp){ 156 | bool needUpdate = false; 157 | if (qp.y()+3>boundRect.bottom()) 158 | boundRect.setBottom(qp.y()+3), needUpdate=true; 159 | if (qp.x()+3>boundRect.right()) 160 | boundRect.setRight(qp.x()+3), needUpdate=true; 161 | if (needUpdate) 162 | this->prepareGeometryChange(); 163 | } 164 | 165 | void PointsPaint::clear() 166 | { 167 | ql.clear(); 168 | mouseHighlightPoint = -1; 169 | selectedPoint = -1; 170 | boundRect.setCoords(0, 0, 0, 0); 171 | setZValue(1); 172 | } 173 | -------------------------------------------------------------------------------- /src/annotation/pointspaint.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** GNU Lesser General Public License Usage 3 | ** Alternatively, this file may be used under the terms of the GNU Lesser 4 | ** General Public License version 2.1 as published by the Free Software 5 | ** Foundation and appearing in the file LICENSE.LGPL included in the 6 | ** packaging of this file. Please review the following information to 7 | ** ensure the GNU Lesser General Public License version 2.1 requirements 8 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 9 | ** 10 | ** 11 | ** GNU General Public License Usage 12 | ** Alternatively, this file may be used under the terms of the GNU 13 | ** General Public License version 3.0 as published by the Free Software 14 | ** Foundation and appearing in the file LICENSE.GPL included in the 15 | ** packaging of this file. Please review the following information to 16 | ** ensure the GNU General Public License version 3.0 requirements will be 17 | ** met: http://www.gnu.org/copyleft/gpl.html. 18 | ** 19 | ** If you have questions regarding the use of this file, please contact 20 | ** Chen Xing at cxcxcxcx@gmail.com . 21 | */ 22 | 23 | #ifndef POINTSSHOW_H 24 | #define POINTSSHOW_H 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | class PointsPaint : public QObject, public QGraphicsItem 32 | { 33 | Q_OBJECT 34 | public: 35 | PointsPaint(); 36 | 37 | QRectF boundingRect() const; 38 | // QPainterPath shape() const; 39 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, 40 | QWidget *widget); 41 | 42 | 43 | int pickPointByMouse(const QPointF &mouseP) const; 44 | int getSize() const{ return ql.size(); } 45 | void addPoint(const QPointF &mouseP); 46 | void updateSelectedPoint(const QPointF &mouseP); 47 | 48 | void highLight(int pId); 49 | void selectPoint(int pId); 50 | void setMirrorPoint(int pId); 51 | 52 | void setRatio(double r){ ratio = r; } 53 | 54 | void clear(); 55 | void setColorScheme(int schemeId); 56 | 57 | QList< QPoint > getPointList() const { return ql; } 58 | 59 | private: 60 | int selectedPoint; 61 | int mirrorPoint; 62 | int mouseHighlightPoint; 63 | 64 | QRect boundRect; 65 | 66 | void testUpdateRect(const QPoint &qp); 67 | 68 | QList< QPoint > ql; 69 | 70 | // Colors 71 | QColor colorHighlightPen, colorHighlightBrush, 72 | colorSelectedPen, colorSelectedBrush, 73 | colorMirrorPen, colorMirrorBrush, 74 | colorPen, colorBrush; 75 | 76 | double ratio; 77 | }; 78 | 79 | #endif // POINTSSHOW_H 80 | -------------------------------------------------------------------------------- /src/annotation/singlephotoview.cpp: -------------------------------------------------------------------------------- 1 | #include "singlephotoview.h" 2 | #include "cv.h" 3 | #include 4 | 5 | SinglePhotoView::SinglePhotoView(QWidget *parent) : 6 | QGraphicsView(parent) 7 | { 8 | this->setMouseTracking(true); 9 | setScene(&gScene); 10 | //gScene.addRect(0, 0, 90, 90); 11 | scaleFactor = 1; 12 | pixItem = NULL; 13 | dragging = false; 14 | allowAddPoints = true; 15 | //pointPaint.ql = &selectedPoints; 16 | 17 | // this->scale(scaleFactor, scaleFactor); 18 | this->setRenderHint(QPainter::Antialiasing); 19 | this->setRenderHint(QPainter::SmoothPixmapTransform); 20 | 21 | srcPointPaint.setColorScheme(0); 22 | dstPointPaint.setColorScheme(1); 23 | gScene.addItem(&srcPointPaint); 24 | gScene.addItem(&dstPointPaint); 25 | srcPointPaint.setRatio(this->mapToScene(1, 0).x()-this->mapToScene(0, 0).x()); 26 | dstPointPaint.setRatio(this->mapToScene(1, 0).x()-this->mapToScene(0, 0).x()); 27 | } 28 | 29 | SinglePhotoView::~SinglePhotoView() 30 | { 31 | gScene.removeItem(&srcPointPaint); 32 | gScene.removeItem(&dstPointPaint); 33 | } 34 | 35 | void SinglePhotoView::zoomIn(){ 36 | scaleFactor *= 1.2; 37 | this->scale(1.2, 1.2); 38 | this->setRenderHint(QPainter::SmoothPixmapTransform); 39 | srcPointPaint.setRatio(this->mapToScene(1, 0).x()-this->mapToScene(0, 0).x()); 40 | dstPointPaint.setRatio(this->mapToScene(1, 0).x()-this->mapToScene(0, 0).x()); 41 | this->update(); 42 | } 43 | 44 | void SinglePhotoView::zoomOut(){ 45 | scaleFactor /= 1.2; 46 | this->scale(1/1.2, 1/1.2); 47 | srcPointPaint.setRatio(this->mapToScene(1, 0).x()-this->mapToScene(0, 0).x()); 48 | dstPointPaint.setRatio(this->mapToScene(1, 0).x()-this->mapToScene(0, 0).x()); 49 | this->update(); 50 | } 51 | 52 | void SinglePhotoView::mousePressEvent(QMouseEvent *event){ 53 | QPointF qp = this->mapToScene(event->x(),event->y()); 54 | 55 | if (event->button()==Qt::LeftButton) 56 | pppCur = &dstPointPaint, pppOther = &srcPointPaint; 57 | else 58 | pppCur = &srcPointPaint, pppOther = &dstPointPaint; 59 | int tI = pppCur->pickPointByMouse(qp); 60 | if (tI != -1){ 61 | pppCur->selectPoint(tI); 62 | pppOther->setMirrorPoint(tI); 63 | //emit selectedPointUpdated(tI); 64 | dragging = true; 65 | //qDebug("Emit: %d",tI); 66 | } 67 | else if (allowAddPoints) { 68 | pppCur->addPoint(qp); 69 | pppCur->selectPoint(-1); 70 | pppOther->setMirrorPoint(pppCur->getSize()); 71 | emit pointsUpdated(); 72 | // emit selectedPointUpdated(pointPaint.getSize()); 73 | } 74 | // selectedPoints.append(qp.toPoint()); 75 | //cout<<"x: "<update(); 79 | 80 | //QGraphicsView::mousePressEvent(event); 81 | } 82 | 83 | void SinglePhotoView::setImage(const QString & imgFileName) 84 | { 85 | setImage(QPixmap(imgFileName)); 86 | } 87 | 88 | void SinglePhotoView::setImage(const QPixmap &img) 89 | { 90 | if (pixItem != NULL){ 91 | gScene.removeItem(pixItem); 92 | delete pixItem; 93 | } 94 | 95 | pixData = img;//, NULL, Qt::SmoothTransformation); 96 | pixItem = gScene.addPixmap(pixData); 97 | pixItem->setTransformationMode(Qt::SmoothTransformation); 98 | 99 | this->setSceneRect(QRect(QPoint(0, 0), pixData.size())); 100 | gScene.setSceneRect(QRect(QPoint(0, 0), pixData.size())); 101 | this->update(); 102 | } 103 | 104 | void SinglePhotoView::mouseReleaseEvent(QMouseEvent *event) 105 | { 106 | dragging = false; 107 | } 108 | 109 | void SinglePhotoView::mouseMoveEvent(QMouseEvent *event) 110 | { 111 | if ( dragging ){ 112 | pppCur->updateSelectedPoint(this->mapToScene(event->pos())); 113 | emit pointsUpdated(); 114 | return; 115 | } 116 | QPointF qp = this->mapToScene(event->x(),event->y()); 117 | int tI; 118 | tI = srcPointPaint.pickPointByMouse(qp); 119 | srcPointPaint.highLight(tI); 120 | tI = dstPointPaint.pickPointByMouse(qp); 121 | dstPointPaint.highLight(tI); 122 | QGraphicsView::mouseMoveEvent(event); 123 | } 124 | 125 | void SinglePhotoView::setPointList( 126 | const QList< QPoint > &qlSrc, 127 | const QList< QPoint > &qlDst){ 128 | srcPointPaint.clear(); 129 | dstPointPaint.clear(); 130 | int i; 131 | for (i=0;i qtemp = srcPointPaint.getPointList(); 142 | for (int i=0;iupdate(); 146 | } 147 | 148 | void SinglePhotoView::clearAllPoints() 149 | { 150 | dstPointPaint.clear(); 151 | srcPointPaint.clear(); 152 | emit pointsUpdated(); 153 | this->update(); 154 | } 155 | -------------------------------------------------------------------------------- /src/annotation/singlephotoview.h: -------------------------------------------------------------------------------- 1 | #ifndef SINGLEPHOTOVIEW_H 2 | #define SINGLEPHOTOVIEW_H 3 | 4 | #include 5 | #include "pointspaint.h" 6 | 7 | class SinglePhotoView : public QGraphicsView 8 | { 9 | Q_OBJECT 10 | public: 11 | explicit SinglePhotoView(QWidget *parent = 0); 12 | ~SinglePhotoView(); 13 | 14 | void zoomIn(); 15 | void zoomOut(); 16 | void setImage(const QString & imgFileName); 17 | void setImage(const QPixmap & img); 18 | void setPointList( 19 | const QList< QPoint > &qlSrc, 20 | const QList< QPoint > &qlDst); 21 | void copySrcPointsToDst(); 22 | void clearAllPoints(); 23 | PointsPaint srcPointPaint, dstPointPaint; 24 | bool allowAddPoints; 25 | signals: 26 | void pointsUpdated(); 27 | 28 | public slots: 29 | 30 | protected: 31 | void mousePressEvent(QMouseEvent *event); 32 | void mouseReleaseEvent(QMouseEvent *event); 33 | void mouseMoveEvent(QMouseEvent *event); 34 | 35 | QGraphicsScene gScene; 36 | 37 | // QList< QPoint > selectedPoints; 38 | 39 | double scaleFactor; 40 | 41 | private: 42 | bool dragging; 43 | PointsPaint *pppCur, *pppOther; 44 | 45 | QString imageFileName; 46 | 47 | QGraphicsPixmapItem *pixItem; 48 | QPixmap pixData; 49 | }; 50 | 51 | #endif // SINGLEPHOTOVIEW_H 52 | -------------------------------------------------------------------------------- /src/cmake/Modules/FindOpenCV.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find OpenCV library installation 2 | # See http://sourceforge.net/projects/opencvlibrary/ 3 | # 4 | # The follwoing variables are optionally searched for defaults 5 | # OpenCV_ROOT_DIR: Base directory of OpenCv tree to use. 6 | # OpenCV_FIND_REQUIRED_COMPONENTS : FIND_PACKAGE(OpenCV COMPONENTS ..) 7 | # compatible interface. typically CV CXCORE CVAUX HIGHGUI CVCAM .. etc. 8 | # 9 | # The following are set after configuration is done: 10 | # OpenCV_FOUND 11 | # OpenCV_INCLUDE_DIR 12 | # OpenCV_LIBRARIES 13 | # OpenCV_LINK_DIRECTORIES 14 | # 15 | # deprecated: 16 | # OPENCV_* uppercase replaced by case sensitive OpenCV_* 17 | # OPENCV_EXE_LINKER_FLAGS 18 | # OPENCV_INCLUDE_DIR : replaced by plural *_DIRS 19 | # 20 | # 2004/05 Jan Woetzel, Friso, Daniel Grest 21 | # 2006/01 complete rewrite by Jan Woetzel 22 | # 1006/09 2nd rewrite introducing ROOT_DIR and PATH_SUFFIXES 23 | # to handle multiple installed versions gracefully by Jan Woetzel 24 | # 25 | # tested with: 26 | # -OpenCV 0.97 (beta5a): MSVS 7.1, gcc 3.3, gcc 4.1 27 | # -OpenCV 0.99 (1.0rc1): MSVS 7.1 28 | # 29 | # www.mip.informatik.uni-kiel.de/~jw 30 | # -------------------------------- 31 | 32 | 33 | MACRO(DBG_MSG _MSG) 34 | # MESSAGE(STATUS "${CMAKE_CURRENT_LIST_FILE}(${CMAKE_CURRENT_LIST_LINE}):\n${_MSG}") 35 | ENDMACRO(DBG_MSG) 36 | 37 | 38 | 39 | # required cv components with header and library if COMPONENTS unspecified 40 | IF (NOT OpenCV_FIND_COMPONENTS) 41 | # default 42 | SET(OpenCV_FIND_REQUIRED_COMPONENTS CV CXCORE CVAUX HIGHGUI ) 43 | IF (WIN32) 44 | LIST(APPEND OpenCV_FIND_REQUIRED_COMPONENTS CVCAM ) # WIN32 only actually 45 | ENDIF(WIN32) 46 | ENDIF (NOT OpenCV_FIND_COMPONENTS) 47 | 48 | 49 | # typical root dirs of installations, exactly one of them is used 50 | SET (OpenCV_POSSIBLE_ROOT_DIRS 51 | "${OpenCV_ROOT_DIR}" 52 | "$ENV{OpenCV_ROOT_DIR}" 53 | "$ENV{OPENCV_DIR}" # only for backward compatibility deprecated by ROOT_DIR 54 | "$ENV{OPENCV_HOME}" # only for backward compatibility 55 | "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Intel(R) Open Source Computer Vision Library_is1;Inno Setup: App Path]" 56 | "$ENV{ProgramFiles}/OpenCV" 57 | /usr/local 58 | /usr 59 | ) 60 | 61 | 62 | # MIP Uni Kiel /opt/net network installation 63 | # get correct prefix for current gcc compiler version for gcc 3.x 4.x 64 | IF (${CMAKE_COMPILER_IS_GNUCXX}) 65 | IF (NOT OpenCV_FIND_QUIETLY) 66 | MESSAGE(STATUS "Checking GNUCXX version 3/4 to determine OpenCV /opt/net/ path") 67 | ENDIF (NOT OpenCV_FIND_QUIETLY) 68 | EXEC_PROGRAM(${CMAKE_CXX_COMPILER} ARGS --version OUTPUT_VARIABLE CXX_COMPILER_VERSION) 69 | IF (CXX_COMPILER_VERSION MATCHES ".*3\\.[0-9].*") 70 | SET(IS_GNUCXX3 TRUE) 71 | LIST(APPEND OpenCV_POSSIBLE_ROOT_DIRS /opt/net/gcc33/OpenCV ) 72 | ENDIF(CXX_COMPILER_VERSION MATCHES ".*3\\.[0-9].*") 73 | IF (CXX_COMPILER_VERSION MATCHES ".*4\\.[0-9].*") 74 | SET(IS_GNUCXX4 TRUE) 75 | LIST(APPEND OpenCV_POSSIBLE_ROOT_DIRS /opt/net/gcc41/OpenCV ) 76 | ENDIF(CXX_COMPILER_VERSION MATCHES ".*4\\.[0-9].*") 77 | ENDIF (${CMAKE_COMPILER_IS_GNUCXX}) 78 | 79 | #DBG_MSG("DBG (OpenCV_POSSIBLE_ROOT_DIRS=${OpenCV_POSSIBLE_ROOT_DIRS}") 80 | 81 | # 82 | # select exactly ONE OpenCV base directory/tree 83 | # to avoid mixing different version headers and libs 84 | # 85 | FIND_PATH(OpenCV_ROOT_DIR 86 | NAMES 87 | cv/include/cv.h # windows 88 | include/opencv/cv.h # linux /opt/net 89 | include/cv/cv.h 90 | include/cv.h 91 | PATHS ${OpenCV_POSSIBLE_ROOT_DIRS}) 92 | DBG_MSG("OpenCV_ROOT_DIR=${OpenCV_ROOT_DIR}") 93 | 94 | 95 | # header include dir suffixes appended to OpenCV_ROOT_DIR 96 | SET(OpenCV_INCDIR_SUFFIXES 97 | include 98 | include/cv 99 | include/opencv 100 | cv/include 101 | cxcore/include 102 | cvaux/include 103 | otherlibs/cvcam/include 104 | otherlibs/highgui 105 | otherlibs/highgui/include 106 | otherlibs/_graphics/include 107 | ) 108 | 109 | # library linkdir suffixes appended to OpenCV_ROOT_DIR 110 | SET(OpenCV_LIBDIR_SUFFIXES 111 | lib 112 | OpenCV/lib 113 | otherlibs/_graphics/lib 114 | ) 115 | #DBG_MSG("OpenCV_LIBDIR_SUFFIXES=${OpenCV_LIBDIR_SUFFIXES}") 116 | 117 | 118 | # 119 | # find incdir for each lib 120 | # 121 | FIND_PATH(OpenCV_CV_INCLUDE_DIR 122 | NAMES cv.h 123 | PATHS ${OpenCV_ROOT_DIR} 124 | PATH_SUFFIXES ${OpenCV_INCDIR_SUFFIXES} ) 125 | FIND_PATH(OpenCV_CXCORE_INCLUDE_DIR 126 | NAMES cxcore.h 127 | PATHS ${OpenCV_ROOT_DIR} 128 | PATH_SUFFIXES ${OpenCV_INCDIR_SUFFIXES} ) 129 | FIND_PATH(OpenCV_CVAUX_INCLUDE_DIR 130 | NAMES cvaux.h 131 | PATHS ${OpenCV_ROOT_DIR} 132 | PATH_SUFFIXES ${OpenCV_INCDIR_SUFFIXES} ) 133 | FIND_PATH(OpenCV_HIGHGUI_INCLUDE_DIR 134 | NAMES highgui.h 135 | PATHS ${OpenCV_ROOT_DIR} 136 | PATH_SUFFIXES ${OpenCV_INCDIR_SUFFIXES} ) 137 | FIND_PATH(OpenCV_CVCAM_INCLUDE_DIR 138 | NAMES cvcam.h 139 | PATHS ${OpenCV_ROOT_DIR} 140 | PATH_SUFFIXES ${OpenCV_INCDIR_SUFFIXES} ) 141 | 142 | # 143 | # find sbsolute path to all libraries 144 | # some are optionally, some may not exist on Linux 145 | # 146 | FIND_LIBRARY(OpenCV_CV_LIBRARY 147 | NAMES cv opencv 148 | PATHS ${OpenCV_ROOT_DIR} 149 | PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES} ) 150 | FIND_LIBRARY(OpenCV_CVAUX_LIBRARY 151 | NAMES cvaux 152 | PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES} ) 153 | FIND_LIBRARY(OpenCV_CVCAM_LIBRARY 154 | NAMES cvcam 155 | PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES} ) 156 | FIND_LIBRARY(OpenCV_CVHAARTRAINING_LIBRARY 157 | NAMES cvhaartraining 158 | PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES} ) 159 | FIND_LIBRARY(OpenCV_CXCORE_LIBRARY 160 | NAMES cxcore 161 | PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES} ) 162 | FIND_LIBRARY(OpenCV_CXTS_LIBRARY 163 | NAMES cxts 164 | PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES} ) 165 | FIND_LIBRARY(OpenCV_HIGHGUI_LIBRARY 166 | NAMES highgui 167 | PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES} ) 168 | FIND_LIBRARY(OpenCV_ML_LIBRARY 169 | NAMES ml 170 | PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES} ) 171 | FIND_LIBRARY(OpenCV_TRS_LIBRARY 172 | NAMES trs 173 | PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES} ) 174 | 175 | 176 | 177 | # 178 | # Logic selecting required libs and headers 179 | # 180 | SET(OpenCV_FOUND ON) 181 | DBG_MSG("OpenCV_FIND_REQUIRED_COMPONENTS=${OpenCV_FIND_REQUIRED_COMPONENTS}") 182 | FOREACH(NAME ${OpenCV_FIND_REQUIRED_COMPONENTS} ) 183 | 184 | # only good if header and library both found 185 | IF (OpenCV_${NAME}_INCLUDE_DIR AND OpenCV_${NAME}_LIBRARY) 186 | LIST(APPEND OpenCV_INCLUDE_DIRS ${OpenCV_${NAME}_INCLUDE_DIR} ) 187 | LIST(APPEND OpenCV_LIBRARIES ${OpenCV_${NAME}_LIBRARY} ) 188 | #DBG_MSG("appending for NAME=${NAME} ${OpenCV_${NAME}_INCLUDE_DIR} and ${OpenCV_${NAME}_LIBRARY}" ) 189 | ELSE (OpenCV_${NAME}_INCLUDE_DIR AND OpenCV_${NAME}_LIBRARY) 190 | DBG_MSG("OpenCV component NAME=${NAME} not found! " 191 | "\nOpenCV_${NAME}_INCLUDE_DIR=${OpenCV_${NAME}_INCLUDE_DIR} " 192 | "\nOpenCV_${NAME}_LIBRARY=${OpenCV_${NAME}_LIBRARY} ") 193 | SET(OpenCV_FOUND OFF) 194 | ENDIF (OpenCV_${NAME}_INCLUDE_DIR AND OpenCV_${NAME}_LIBRARY) 195 | 196 | ENDFOREACH(NAME) 197 | 198 | DBG_MSG("OpenCV_INCLUDE_DIRS=${OpenCV_INCLUDE_DIRS}") 199 | DBG_MSG("OpenCV_LIBRARIES=${OpenCV_LIBRARIES}") 200 | 201 | # get the link directory for rpath to be used with LINK_DIRECTORIES: 202 | IF (OpenCV_CV_LIBRARY) 203 | GET_FILENAME_COMPONENT(OpenCV_LINK_DIRECTORIES ${OpenCV_CV_LIBRARY} PATH) 204 | ENDIF (OpenCV_CV_LIBRARY) 205 | 206 | MARK_AS_ADVANCED( 207 | OpenCV_ROOT_DIR 208 | OpenCV_INCLUDE_DIRS 209 | OpenCV_CV_INCLUDE_DIR 210 | OpenCV_CXCORE_INCLUDE_DIR 211 | OpenCV_CVAUX_INCLUDE_DIR 212 | OpenCV_CVCAM_INCLUDE_DIR 213 | OpenCV_HIGHGUI_INCLUDE_DIR 214 | OpenCV_LIBRARIES 215 | OpenCV_CV_LIBRARY 216 | OpenCV_CXCORE_LIBRARY 217 | OpenCV_CVAUX_LIBRARY 218 | OpenCV_CVCAM_LIBRARY 219 | OpenCV_CVHAARTRAINING_LIBRARY 220 | OpenCV_CXTS_LIBRARY 221 | OpenCV_HIGHGUI_LIBRARY 222 | OpenCV_ML_LIBRARY 223 | OpenCV_TRS_LIBRARY 224 | ) 225 | 226 | 227 | # be backward compatible: 228 | SET(OPENCV_LIBRARIES ${OpenCV_LIBRARIES} ) 229 | SET(OPENCV_INCLUDE_DIR ${OpenCV_INCLUDE_DIRS} ) 230 | SET(OPENCV_FOUND ${OpenCV_FOUND}) 231 | 232 | 233 | 234 | # display help message 235 | IF(NOT OpenCV_FOUND) 236 | # make FIND_PACKAGE friendly 237 | IF(NOT OpenCV_FIND_QUIETLY) 238 | IF(OpenCV_FIND_REQUIRED) 239 | MESSAGE(FATAL_ERROR 240 | "OpenCV required but some headers or libs not found. Please specify it's location with OpenCV_ROOT_DIR env. variable.") 241 | ELSE(OpenCV_FIND_REQUIRED) 242 | MESSAGE(STATUS 243 | "ERROR: OpenCV was not found.") 244 | ENDIF(OpenCV_FIND_REQUIRED) 245 | ENDIF(NOT OpenCV_FIND_QUIETLY) 246 | ENDIF(NOT OpenCV_FOUND) 247 | -------------------------------------------------------------------------------- /src/demo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PROJECT( asmlib-opencv-demo ) 2 | FIND_PACKAGE( OpenCV REQUIRED ) 3 | SET(TEST_SRC main.cpp XGetopt.cpp) 4 | 5 | INCLUDE_DIRECTORIES( ../lib/ ${OpenCV_INCLUDE_DIRS} ) 6 | 7 | ADD_EXECUTABLE( demo ${TEST_SRC}) 8 | TARGET_LINK_LIBRARIES( demo asm ${OpenCV_LIBS}) 9 | -------------------------------------------------------------------------------- /src/demo/XGetopt.cpp: -------------------------------------------------------------------------------- 1 | // XGetopt.cpp Version 1.2 2 | // 3 | // Author: Hans Dietrich 4 | // hdietrich2@hotmail.com 5 | // 6 | // Description: 7 | // XGetopt.cpp implements getopt(), a function to parse command lines. 8 | // 9 | // History 10 | // Version 1.2 - 2003 May 17 11 | // - Added Unicode support 12 | // 13 | // Version 1.1 - 2002 March 10 14 | // - Added example to XGetopt.cpp module header 15 | // 16 | // This software is released into the public domain. 17 | // You are free to use it in any way you like. 18 | // 19 | // This software is provided "as is" with no expressed 20 | // or implied warranty. I accept no liability for any 21 | // damage or loss of business that this software may cause. 22 | // 23 | /////////////////////////////////////////////////////////////////////////////// 24 | 25 | 26 | /////////////////////////////////////////////////////////////////////////////// 27 | // if you are using precompiled headers then include this line: 28 | //#include "stdafx.h" 29 | /////////////////////////////////////////////////////////////////////////////// 30 | 31 | 32 | /////////////////////////////////////////////////////////////////////////////// 33 | // if you are not using precompiled headers then include these lines: 34 | #ifndef __GNUG__ 35 | #include 36 | #include 37 | //#include 38 | /////////////////////////////////////////////////////////////////////////////// 39 | 40 | 41 | #include "XGetopt.h" 42 | 43 | 44 | /////////////////////////////////////////////////////////////////////////////// 45 | // 46 | // X G e t o p t . c p p 47 | // 48 | // 49 | // NAME 50 | // getopt -- parse command line options 51 | // 52 | // SYNOPSIS 53 | // int getopt(int argc, char *argv[], char *optstring) 54 | // 55 | // extern char *optarg; 56 | // extern int optind; 57 | // 58 | // DESCRIPTION 59 | // The getopt() function parses the command line arguments. Its 60 | // arguments argc and argv are the argument count and array as 61 | // passed into the application on program invocation. In the case 62 | // of Visual C++ programs, argc and argv are available via the 63 | // variables __argc and __argv (double underscores), respectively. 64 | // getopt returns the next option letter in argv that matches a 65 | // letter in optstring. (Note: Unicode programs should use 66 | // __targv instead of __argv. Also, all character and string 67 | // literals should be enclosed in _T( ) ). 68 | // 69 | // optstring is a string of recognized option letters; if a letter 70 | // is followed by a colon, the option is expected to have an argument 71 | // that may or may not be separated from it by white space. optarg 72 | // is set to point to the start of the option argument on return from 73 | // getopt. 74 | // 75 | // Option letters may be combined, e.g., "-ab" is equivalent to 76 | // "-a -b". Option letters are case sensitive. 77 | // 78 | // getopt places in the external variable optind the argv index 79 | // of the next argument to be processed. optind is initialized 80 | // to 0 before the first call to getopt. 81 | // 82 | // When all options have been processed (i.e., up to the first 83 | // non-option argument), getopt returns EOF, optarg will point 84 | // to the argument, and optind will be set to the argv index of 85 | // the argument. If there are no non-option arguments, optarg 86 | // will be set to NULL. 87 | // 88 | // The special option "--" may be used to delimit the end of the 89 | // options; EOF will be returned, and "--" (and everything after it) 90 | // will be skipped. 91 | // 92 | // RETURN VALUE 93 | // For option letters contained in the string optstring, getopt 94 | // will return the option letter. getopt returns a question mark (?) 95 | // when it encounters an option letter not included in optstring. 96 | // EOF is returned when processing is finished. 97 | // 98 | // BUGS 99 | // 1) Long options are not supported. 100 | // 2) The GNU double-colon extension is not supported. 101 | // 3) The environment variable POSIXLY_CORRECT is not supported. 102 | // 4) The + syntax is not supported. 103 | // 5) The automatic permutation of arguments is not supported. 104 | // 6) This implementation of getopt() returns EOF if an error is 105 | // encountered, instead of -1 as the latest standard requires. 106 | // 107 | // EXAMPLE 108 | // BOOL CMyApp::ProcessCommandLine(int argc, char *argv[]) 109 | // { 110 | // int c; 111 | // 112 | // while ((c = getopt(argc, argv, _T("aBn:"))) != EOF) 113 | // { 114 | // switch (c) 115 | // { 116 | // case _T('a'): 117 | // TRACE(_T("option a\n")); 118 | // // 119 | // // set some flag here 120 | // // 121 | // break; 122 | // 123 | // case _T('B'): 124 | // TRACE( _T("option B\n")); 125 | // // 126 | // // set some other flag here 127 | // // 128 | // break; 129 | // 130 | // case _T('n'): 131 | // TRACE(_T("option n: value=%d\n"), atoi(optarg)); 132 | // // 133 | // // do something with value here 134 | // // 135 | // break; 136 | // 137 | // case _T('?'): 138 | // TRACE(_T("ERROR: illegal option %s\n"), argv[optind-1]); 139 | // return FALSE; 140 | // break; 141 | // 142 | // default: 143 | // TRACE(_T("WARNING: no handler for option %c\n"), c); 144 | // return FALSE; 145 | // break; 146 | // } 147 | // } 148 | // // 149 | // // check for non-option args here 150 | // // 151 | // return TRUE; 152 | // } 153 | // 154 | /////////////////////////////////////////////////////////////////////////////// 155 | 156 | char *optarg; // global argument pointer 157 | int opterr; 158 | int optind = 0; // global argv index 159 | 160 | int getopt(int argc, char *argv[], char *optstring) 161 | { 162 | static char *next = NULL; 163 | if (optind == 0) 164 | next = NULL; 165 | 166 | optarg = NULL; 167 | 168 | if (next == NULL || *next == '\0') 169 | { 170 | if (optind == 0) 171 | optind++; 172 | 173 | if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') 174 | { 175 | optarg = NULL; 176 | if (optind < argc) 177 | optarg = argv[optind]; 178 | return EOF; 179 | } 180 | 181 | if (strcmp(argv[optind], "--") == 0) 182 | { 183 | optind++; 184 | optarg = NULL; 185 | if (optind < argc) 186 | optarg = argv[optind]; 187 | return EOF; 188 | } 189 | 190 | next = argv[optind]; 191 | next++; // skip past - 192 | optind++; 193 | } 194 | 195 | char c = *next++; 196 | char *cp = strchr(optstring, c); 197 | 198 | if (cp == NULL || c == ':') 199 | return '?'; 200 | 201 | cp++; 202 | if (*cp == ':') 203 | { 204 | if (*next != '\0') 205 | { 206 | optarg = next; 207 | next = NULL; 208 | } 209 | else if (optind < argc) 210 | { 211 | optarg = argv[optind]; 212 | optind++; 213 | } 214 | else 215 | { 216 | return '?'; 217 | } 218 | } 219 | 220 | return c; 221 | } 222 | #endif -------------------------------------------------------------------------------- /src/demo/XGetopt.h: -------------------------------------------------------------------------------- 1 | // XGetopt.h Version 1.2 2 | // 3 | // Author: Hans Dietrich 4 | // hdietrich2@hotmail.com 5 | // 6 | // This software is released into the public domain. 7 | // You are free to use it in any way you like. 8 | // 9 | // This software is provided "as is" with no expressed 10 | // or implied warranty. I accept no liability for any 11 | // damage or loss of business that this software may cause. 12 | // 13 | /////////////////////////////////////////////////////////////////////////////// 14 | 15 | #ifndef XGETOPT_H 16 | #define XGETOPT_H 17 | 18 | extern int optind; 19 | extern int opterr; 20 | extern char *optarg; 21 | 22 | int getopt(int argc, char *argv[], char *optstring); 23 | 24 | #endif //XGETOPT_H 25 | -------------------------------------------------------------------------------- /src/demo/demo.py: -------------------------------------------------------------------------------- 1 | import cv2, asmlib, argparse, sys 2 | import cv2, argparse, sys 3 | 4 | # import argparse 5 | 6 | def loadModel(model_file): 7 | model = asmlib.ASMModel() 8 | model.loadFromFile(model_file) 9 | return model 10 | 11 | def fitAndShow(classifier, model, img, mode, verbose): 12 | if mode == 'all': 13 | flags = cv2.CASCADE_SCALE_IMAGE 14 | else: 15 | flags = cv2.CASCADE_FIND_BIGGEST_OBJECT|cv2.CASCADE_SCALE_IMAGE 16 | 17 | faces = classifier.detectMultiScale(img, scaleFactor=1.2, minNeighbors=2, minSize=(60, 60), flags=flags) 18 | # the rects come back as numpy.int32, this cant be handled by the pyopencv_from functions 19 | faces = map(lambda t: tuple(map(int, t)), faces) 20 | result = model.fitAll(img, faces, verbose) 21 | model.showResult(img, result) 22 | 23 | def buildModel(model_file, def_file, list_file): 24 | model = asmlib.ASMModel() 25 | model.buildModel(def_file, list_file) 26 | model.saveToFile(model_file) 27 | 28 | def viewModel(model): 29 | model.viewShapeModel() 30 | cv2.waitKey() 31 | 32 | def fitModelToWebcam(classifier, model, verbose): 33 | capture = cv2.VideoCapture() 34 | capture.open(0) 35 | if not capture.isOpened(): 36 | print "could not open webcam" 37 | sys.exit(1) 38 | 39 | while cv2.waitKey(5) == -1: 40 | retval, img = capture.read() 41 | img2 = cv2.flip(img, 1) 42 | fitAndShow(classifier, model, img2, 'biggest', verbose) 43 | 44 | def fitModelToImage(classifier, model, image_file, verbose): 45 | img = cv2.imread(image_file) 46 | if img.empty(): 47 | print "could not load image" 48 | sys.exit(1) 49 | 50 | fitAndShow(classifier, model, img, 'all', verbose) 51 | cv2.waitKey() 52 | 53 | 54 | if __name__ == "__main__": 55 | parser = argparse.ArgumentParser(epilog="For details and examples, please check: http://code.google.com/p/asmlib-opencv/wiki/RunningDemo") 56 | 57 | # Tasks 58 | parser.add_argument('-b', dest='build', action='store_true', help='Build a model') 59 | parser.add_argument('-v', dest='view', action='store_true', help='View a model') 60 | parser.add_argument('-f', dest='fit', action='store_true', help='Fit images to a model') 61 | 62 | # General options 63 | parser.add_argument('-m', dest='model_file', required=True, help="Path to the model file") 64 | parser.add_argument('-V', dest='verbose', type=int, default=0, help="Verbosity level") 65 | 66 | # Build specific options 67 | parser.add_argument('-d', dest='def_file', help="Model definition file, see wiki") 68 | parser.add_argument('-l', dest='list_file', help="List of labelled points, see wiki") 69 | 70 | # Fitting specific options 71 | parser.add_argument('-C', dest='detector_file', help="Face/Object detector XML (OpenCV)") 72 | parser.add_argument('-p', dest='image_file', help="Path to an image") 73 | parser.add_argument('-pc', dest='webcam', action='store_true', help="Run on your webcam") 74 | 75 | args = parser.parse_args() 76 | 77 | if args.build: 78 | if not args.def_file: 79 | print "you must specify the -d (def_file) option" 80 | sys.exit(1) 81 | if not args.list_file: 82 | print "you must specify the -d (def_file) option" 83 | sys.exit(1) 84 | 85 | buildModel(args.model_file, args.def_file, args.list_file) 86 | elif args.view: 87 | viewModel(load_model(args.model_file)) 88 | elif args.fit: 89 | if not args.image_file and not args.webcam: 90 | print "you must specify either the -p (image_file) option or the -pc (webcam) option" 91 | sys.exit(1) 92 | 93 | classifier = cv2.CascadeClassifier() 94 | if not classifier.load(args.detector_file): 95 | print "loading the cascade classifier failed" 96 | sys.exit(1) 97 | 98 | if args.webcam: 99 | fitModelToWebcam(classifier, loadModel(args.model_file), args.verbose) 100 | else: 101 | fitModelToImage(classifier, loadModel(args.model_file), args.image_file, args.verbose) 102 | else: 103 | print 'you must specify either the -b (build), -v (view) or -f (fit) parameter' 104 | -------------------------------------------------------------------------------- /src/demo/main.cpp: -------------------------------------------------------------------------------- 1 | // vim: ci:ts=4:sw=4:et 2 | #include "asmmodel.h" 3 | #include "modelfile.h" 4 | #ifdef __GNUG__ 5 | #include 6 | #else 7 | #include "XGetopt.h" 8 | #endif 9 | 10 | #include 11 | #include 12 | #include "opencv2/highgui.hpp" 13 | #include "opencv2/objdetect.hpp" 14 | using std::string; 15 | using cv::imshow; 16 | using std::cerr; 17 | using std::endl; 18 | 19 | using namespace ASMLib; 20 | 21 | //! Build an ASM model. 22 | void buildASMModel(ASMModel & asmModel, 23 | string shapeDef, string ptsList, string modelPath) 24 | { 25 | asmModel.buildModel(shapeDef, ptsList); 26 | asmModel.saveToFile(modelPath); 27 | } 28 | 29 | //! Load an ASM model. 30 | void readASMModel(ASMModel & asmModel, string modelPath) 31 | { 32 | asmModel.loadFromFile(modelPath); 33 | } 34 | 35 | //! Run OpenCV object detection and do ASM fitting on each detected object. 36 | void searchAndFit( 37 | ASMModel & asmModel, 38 | cv::CascadeClassifier &objCascadeClassfifier, 39 | const string & picPath, 40 | int verboseL) { 41 | // Load image. 42 | Mat img = cv::imread(picPath); 43 | if (img.empty()) { 44 | cerr << "Load image '" << picPath << "' failed." << endl; 45 | exit(2); 46 | } 47 | 48 | // Face detection. 49 | vector< cv::Rect > faces; 50 | objCascadeClassfifier.detectMultiScale( 51 | img, faces, 52 | 1.2, 2, CV_HAAR_SCALE_IMAGE, Size(60, 60) ); 53 | 54 | // Fit to ASM! 55 | vector < ASMFitResult > fitResult = asmModel.fitAll(img, faces, verboseL); 56 | asmModel.showResult(img, fitResult); 57 | cvWaitKey(); 58 | } 59 | 60 | //! ASM on webcam stream! 61 | void asmOnWebCam( 62 | ASMModel & asmModel, 63 | cv::CascadeClassifier &objCascadeClassfifier, 64 | int verboseL) { 65 | Mat img, imgT; 66 | cv::VideoCapture capture; 67 | capture.open(0); 68 | if (!capture.isOpened()) { 69 | cerr << "Failed to open your webcam." << endl; 70 | exit(2); 71 | } 72 | while (cvWaitKey(5) == -1) { 73 | capture >> imgT; 74 | cv::flip(imgT, img, 1); 75 | 76 | vector< cv::Rect > faces; 77 | // Do face detection first. 78 | // Note: ONLY the largest face is processed. 79 | // (otherwise face detection would be too slow) 80 | objCascadeClassfifier.detectMultiScale( img, faces, 81 | 1.2, 2, CV_HAAR_FIND_BIGGEST_OBJECT 82 | //|CV_HAAR_DO_ROUGH_SEARCH 83 | |CV_HAAR_SCALE_IMAGE 84 | , Size(60, 60) ); 85 | 86 | // Fit to ASM! 87 | vector < ASMFitResult > fitResult = asmModel.fitAll(img, faces, verboseL); 88 | asmModel.showResult(img, fitResult); 89 | } 90 | } 91 | 92 | void showHelp() 93 | { 94 | cerr << "Arguments: " << endl 95 | << "===========" << endl 96 | << "Tasks: " << endl 97 | << " -b : Build a model!" << endl 98 | << " -v : View a model." << endl 99 | << " -f : Fit images to a model." << endl 100 | << endl 101 | << "General options:" << endl 102 | << " -m : Path to the model file." << endl 103 | << " -V : Verbose level." << endl 104 | << endl 105 | << "Build specific options:" << endl 106 | << " -d : Model definition file, see wiki." << endl 107 | << " -l : List of labelled points, see wiki." << endl 108 | << endl 109 | << "Fitting specific options: " << endl 110 | << " -C : Face/Object detector XML (OpenCV)." << endl 111 | << " -p : Path to an image" << endl 112 | << " -pc : Run on your webcam!" << endl 113 | << endl 114 | << "For details and examples, please check: " << endl 115 | << " http://code.google.com/p/asmlib-opencv/wiki/RunningDemo" << endl 116 | << endl; 117 | } 118 | 119 | int main(int argc, char *argv[]) 120 | { 121 | string modelFile, modelType, action; 122 | string ptsDefFile, ptsListFile, picPath; 123 | string faceCascadePath = "haarcascade_frontalface_alt.xml"; 124 | int verboseL = 0; 125 | int ch; 126 | opterr = 0; 127 | while ((ch = getopt(argc, argv, "cV:p:C:m:t:d:l:a:vbf?")) != EOF) { 128 | switch (ch) { 129 | // General options: 130 | case 'm': 131 | // The path of model file 132 | modelFile = optarg; 133 | break; 134 | case 'V': 135 | // Verbose level. 136 | verboseL = strlen(optarg); 137 | break; 138 | 139 | // Actions: 140 | case 'v': 141 | // View a model. 142 | action = "view"; 143 | break; 144 | case 'b': 145 | // Build a model. 146 | action = "build"; 147 | break; 148 | case 'f': 149 | // Use the model to fit images. 150 | action = "fit"; 151 | break; 152 | 153 | // Options related to build a model: 154 | case 'd': 155 | // Points definition file 156 | ptsDefFile = optarg; 157 | break; 158 | case 'l': 159 | // A list of PTS files. 160 | ptsListFile = optarg; 161 | break; 162 | 163 | 164 | // Options to fit the model to an image: 165 | case 'C': 166 | // Face detector xml 167 | faceCascadePath = optarg; 168 | break; 169 | case 'p': 170 | // Path for the picture to fit 171 | picPath = optarg; 172 | break; 173 | } 174 | } 175 | 176 | if (action == "") { 177 | showHelp(); 178 | cerr << "!!! You have to specify a task!" << endl << endl; 179 | exit(1); 180 | } 181 | 182 | if (modelFile == "") { 183 | showHelp(); 184 | cerr << "!!! You have to specify a model file!" << endl << endl; 185 | exit(1); 186 | } 187 | 188 | ASMModel asmModel; 189 | if (action == "build") { 190 | if (ptsDefFile == "") { 191 | showHelp(); 192 | cerr << "!!! def file is necessary for building a model!" << endl; 193 | exit(1); 194 | } 195 | if (ptsListFile == "") { 196 | showHelp(); 197 | cerr << "!!! list file is necessary for building a model!" << endl; 198 | exit(1); 199 | } 200 | buildASMModel(asmModel, ptsDefFile, ptsListFile, modelFile); 201 | } else { 202 | readASMModel(asmModel, modelFile); 203 | if (action == "view") { 204 | asmModel.viewShapeModel(); 205 | cvWaitKey(); 206 | } else if (action == "fit") { 207 | if (picPath == "") { 208 | showHelp(); 209 | cerr << "You need to either specify an image or webcam mode" 210 | << endl; 211 | exit(1); 212 | } 213 | 214 | Mat img, imgT; 215 | cv::CascadeClassifier faceCascade; 216 | if (!faceCascade.load(faceCascadePath)) { 217 | showHelp(); 218 | cerr << "Now, a (face) detector is needed. " 219 | << "Please check the URL above." 220 | << endl; 221 | exit(1); 222 | } 223 | if (picPath == "c") { 224 | asmOnWebCam(asmModel, faceCascade, verboseL); 225 | } else { 226 | searchAndFit(asmModel, faceCascade, picPath, verboseL); 227 | } 228 | } 229 | } 230 | return 0; 231 | } 232 | -------------------------------------------------------------------------------- /src/lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED (VERSION 2.6) 2 | PROJECT( asmlib-opencv ) 3 | FIND_PACKAGE( OpenCV REQUIRED ) 4 | SET(LIB_SRC_LIST modelfile.cpp asmmodel.cpp shapevec.cpp similaritytrans.cpp afreader.cpp modelimage.cpp shapemodel.cpp shapeinfo.cpp) 5 | 6 | #INCLUDE_DIRECTORIES( ${OPENCV_INCLUDE_DIR} ) 7 | 8 | ADD_LIBRARY( asm STATIC ${LIB_SRC_LIST} ) 9 | 10 | # --------------------------------------------------------- 11 | # Python interface 12 | # --------------------------------------------------------- 13 | 14 | set(asmlib_opencv_hdrs 15 | "${CMAKE_CURRENT_SOURCE_DIR}/shapemodel.h" 16 | "${CMAKE_CURRENT_SOURCE_DIR}/shapevec.h" 17 | "${CMAKE_CURRENT_SOURCE_DIR}/asmmodel.h") 18 | 19 | set(asmlib_cv2_generated_hdrs 20 | "${CMAKE_CURRENT_BINARY_DIR}/ASMLib_generated_funcs.h" 21 | "${CMAKE_CURRENT_BINARY_DIR}/ASMLib_generated_func_tab.h" 22 | "${CMAKE_CURRENT_BINARY_DIR}/ASMLib_generated_types.h" 23 | "${CMAKE_CURRENT_BINARY_DIR}/ASMLib_generated_type_reg.h" 24 | "${CMAKE_CURRENT_BINARY_DIR}/ASMLib_generated_const_reg.h") 25 | 26 | add_custom_command( 27 | OUTPUT ${asmlib_cv2_generated_hdrs} 28 | COMMAND ${PYTHON_EXECUTABLE} "${OpenCV_CONFIG_PATH}/gen2.py" ASMLib ${CMAKE_CURRENT_BINARY_DIR} ${asmlib_opencv_hdrs} 29 | DEPENDS ${asmlib_opencv_hdrs}) 30 | 31 | FIND_PACKAGE(PythonInterp) 32 | FIND_PACKAGE(PythonLibs) 33 | 34 | if(NOT PYTHON_NUMPY_INCLUDE_DIRS) 35 | # Attempt to discover the NumPy include directory. If this succeeds, then build python API with NumPy 36 | execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c 37 | "import os; os.environ['DISTUTILS_USE_SDK']='1'; import numpy.distutils; print(os.pathsep.join(numpy.distutils.misc_util.get_numpy_include_dirs()))" 38 | RESULT_VARIABLE PYTHON_NUMPY_PROCESS 39 | OUTPUT_VARIABLE PYTHON_NUMPY_INCLUDE_DIRS 40 | OUTPUT_STRIP_TRAILING_WHITESPACE) 41 | 42 | if(PYTHON_NUMPY_PROCESS EQUAL 0) 43 | file(TO_CMAKE_PATH "${PYTHON_NUMPY_INCLUDE_DIRS}" _PYTHON_NUMPY_INCLUDE_DIRS) 44 | set(PYTHON_NUMPY_INCLUDE_DIRS "${_PYTHON_NUMPY_INCLUDE_DIRS}" CACHE PATH "Path to numpy headers") 45 | endif() 46 | endif() 47 | 48 | if(PYTHON_NUMPY_INCLUDE_DIRS) 49 | execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "import numpy; print(numpy.version.version)" 50 | OUTPUT_VARIABLE PYTHON_NUMPY_VERSION 51 | OUTPUT_STRIP_TRAILING_WHITESPACE) 52 | endif() 53 | 54 | INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_PATH}) 55 | INCLUDE_DIRECTORIES(${PYTHON_NUMPY_INCLUDE_DIRS}) 56 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) 57 | INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS}) 58 | 59 | add_library(asmlib SHARED python.cpp ${asmlib_cv2_generated_hdrs}) 60 | target_link_libraries(asmlib ${PYTHON_LIBRARIES}) 61 | target_link_libraries(asmlib asm) 62 | target_link_libraries(asmlib ${OpenCV_LIBS}) 63 | 64 | set_target_properties(asmlib PROPERTIES 65 | PREFIX "" 66 | OUTPUT_NAME asmlib 67 | SUFFIX ".so") 68 | 69 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/asmlib.so" DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/python2.7/site-packages" COMPONENT main) 70 | -------------------------------------------------------------------------------- /src/lib/afreader.cpp: -------------------------------------------------------------------------------- 1 | #include "afreader.h" 2 | 3 | namespace ASMLib { 4 | //! Constructor. Opens the file. 5 | /** 6 | 7 | @author Mikkel B. Stegmann 8 | @version 4-17-2000 9 | 10 | 11 | @param filename The file to open. 12 | 13 | @return Nothing. 14 | 15 | */ 16 | AFReader::AFReader( const char *filename ): 17 | CR(0x0a), LF(0x0d), COMMENT_CHAR('#') { 18 | fh = fopen( filename, "rb" ); 19 | } 20 | 21 | //! Destructor. Closes the file. 22 | /** 23 | 24 | @author Mikkel B. Stegmann 25 | @version 4-17-2000 26 | @return Nothing. 27 | 28 | */ 29 | AFReader::~AFReader() { 30 | // puts("Closing file"); 31 | if (fh) fclose(fh); 32 | } 33 | 34 | //! Increments the file pointer beyond any white space. 35 | /** 36 | 37 | @author Mikkel B. Stegmann 38 | @version 4-17-2000 39 | @return Nothing. 40 | 41 | */ 42 | void AFReader::SkipWhiteSpace() { 43 | int ch; 44 | do { 45 | ch=fgetc( fh ); 46 | } while( ch==' ' || ch=='\t' || ch==CR || ch==LF ); 47 | ungetc( ch, fh ); 48 | } 49 | 50 | //! Increments the file pointer to the start of the next line. 51 | /** 52 | @author Mikkel B. Stegmann 53 | @version 4-17-2000 54 | @return Nothing. 55 | 56 | */ 57 | void AFReader::SkipRestOfLine() { 58 | int ch; 59 | do { 60 | ch=fgetc( fh ); 61 | } while( ch!=EOF && ch!=CR && ch!=LF ); 62 | ungetc( ch, fh ); 63 | 64 | SkipWhiteSpace(); 65 | } 66 | 67 | //! Returns true if more white space is present on the current line. 68 | /** 69 | 70 | @author Mikkel B. Stegmann 71 | @version 4-17-2000 72 | 73 | @return Nothing. 74 | 75 | */ 76 | bool AFReader::MoreNonWhiteSpaceOnLine() { 77 | char buf[256]; 78 | int ch, n_gets = 0; 79 | bool non_white = false; 80 | 81 | do { 82 | ch=fgetc( fh ); 83 | buf[n_gets++] = ch; 84 | if ( ch!='\t' && ch!=' ' && ch!=CR && ch!=LF && ch!=EOF) { 85 | non_white = true; break; 86 | } 87 | } while( ch!=EOF && ch!=CR && ch!=LF ); 88 | 89 | for(int i=0;i 5 | 6 | namespace ASMLib { 7 | //! File reader with support to omit # starting lines 8 | /** 9 | 10 | @author Mikkel B. Stegmann, Chen Xing 11 | @version 2010-01-26 12 | */ 13 | class AFReader 14 | { 15 | public: 16 | AFReader( const char *filename ); 17 | ~AFReader(); 18 | 19 | bool MoreNonWhiteSpaceOnLine(); 20 | 21 | /// Skips whitespace and any commments preceeding the current file position. 22 | void Sync() { SkipWhiteSpace(); SkipComments(); } 23 | 24 | /// Returns true if the file is valid. 25 | bool IsValid() { return fh!=NULL; } 26 | 27 | /// Current open file. 28 | FILE *FH() { return fh; } 29 | private: 30 | FILE *fh; 31 | void SkipRestOfLine(); 32 | void SkipComments(); 33 | void SkipWhiteSpace(); 34 | const char CR; 35 | const char LF; 36 | const char COMMENT_CHAR; 37 | }; 38 | } 39 | #endif // AFREADER_H 40 | -------------------------------------------------------------------------------- /src/lib/asmmodel.cpp: -------------------------------------------------------------------------------- 1 | #include "asmmodel.h" 2 | #include 3 | 4 | namespace ASMLib { 5 | 6 | void ASMModel::buildModel(const string& shapeDefFile, const string& ptsListFile) 7 | { 8 | ShapeModel::buildModel(shapeDefFile, ptsListFile); 9 | 10 | printf("(II) Building active model...\n"); 11 | buildLocalDiffStructure(); 12 | } 13 | 14 | void ASMModel::buildLocalDiffStructure() 15 | { 16 | int i, j, l; 17 | // First, we have find a proper "step" based on the size of face 18 | int xMin, xMax, yMin, yMax; 19 | vector< double > myStep; 20 | myStep.resize(nTrain); 21 | for (i=0; i xMax) 30 | xMax = imageSet[i].points[j].x; 31 | if (imageSet[i].points[j].y > yMax) 32 | yMax = imageSet[i].points[j].y; 33 | } 34 | myStep[i] = 1.3* sqrt( (xMax-xMin)*(yMax-yMin) / 10000.); 35 | // printf("step: %f\n", myStep[i]); 36 | } 37 | 38 | Mat_< double > *tCovar, *tMean; 39 | Mat_< double > datMat(2*localFeatureRad+1, nTrain); 40 | meanG.resize(this->pyramidLevel + 1); 41 | iCovarG.resize(this->pyramidLevel + 1); 42 | for (l = 0; l <= pyramidLevel; l++){ 43 | for (i = 0; i; 45 | tMean = new Mat_< double >; 46 | for (j = 0; j M; 48 | M = datMat.col(j); 49 | imageSet[j].getLocalStruct(i,localFeatureRad,l, myStep[j]).copyTo(M); 50 | } 51 | cv::calcCovarMatrix(datMat, *tCovar, *tMean, 52 | CV_COVAR_NORMAL|CV_COVAR_COLS); 53 | *tCovar = tCovar->inv(cv::DECOMP_SVD); 54 | this->iCovarG[l].push_back(*tCovar); 55 | this->meanG[l].push_back(*tMean); 56 | delete tMean; 57 | delete tCovar; 58 | } 59 | } 60 | } 61 | 62 | void ASMModel::findParamForShapeBTSM(const ShapeVec &Y, const ShapeVec &Y_old, 63 | ASMFitResult & fitResult, ASMFitResult &b_old, int l) 64 | { 65 | //const double c[3] = {0.005, 0.005, 0.001}; 66 | const double c[3] = {0.0005, 0.0005, 0.0005}; 67 | 68 | double rho2, delta2, x2; 69 | double p; 70 | ShapeVec y_r, y_rpr, xFromParam, xFromY, x; 71 | 72 | ShapeVec yt = Y_old; 73 | yt -= Y; 74 | rho2 = c[l] * yt.dot(yt); 75 | 76 | SimilarityTrans curTrans = b_old.transformation; 77 | Mat_< double > curParam, tmpFullParam, lastParam; 78 | 79 | curParam.create(pcaPyr[l].eigenvalues.rows, 1); 80 | for (int i=0; ipcaPyr[l].backProject(curParam, xFromParam); 101 | 102 | pcaFullShape->project(y_r, tmpFullParam); 103 | pcaFullShape->backProject(tmpFullParam, y_rpr); 104 | x = p*y_rpr + (1-p) * xFromParam; 105 | x2 = x.dot(x) + (x.rows - 4) * delta2; 106 | // printf("p: %g, rho2/s2: %g, sigma2: %g, delta2: %g, x.x: %g, x2: %g\n", 107 | // p, rho2/(s * s), sigma2Pyr[l], delta2, x.dot(x), x2); 108 | 109 | // Maximization Step 110 | pcaPyr[l].project(x, curParam); 111 | 112 | for (int i=0; ieigenvalues.at(i, 0))/ 114 | (pcaShape->eigenvalues.at(i, 0)+sigma2Pyr[l]); 115 | int nP = x.rows / 2; 116 | curTrans.a = Y.dot(x) / x2; 117 | curTrans.b = 0; 118 | for (int i=0; i1e-4 && ii<20); 127 | fitResult.params = curParam; 128 | fitResult.transformation = curTrans; 129 | } 130 | 131 | void ASMModel::findParamForShape(const ShapeVec &Y, ASMFitResult & fitResult){ 132 | ShapeVec x, y; 133 | 134 | fitResult.setModel(this); 135 | // Step 1: Init to zeros 136 | fitResult.params = Mat_::zeros(nShapeParams, 1); 137 | SimilarityTrans &st = fitResult.transformation; 138 | 139 | Mat_ resOld; 140 | do { 141 | resOld = fitResult.params.clone(); 142 | // Step 2: Project to x 143 | projectParamToShape(fitResult.params, x); 144 | // Step 3: Align x to Y 145 | st.setTransformByAlign(x, Y); 146 | // Step 4: Invert transform Y to y 147 | st.invTransform(Y, y); 148 | // Step 5: Align to mean shape 149 | y.alignTo(meanShape); 150 | 151 | // Step 6: Update parameters 152 | projectShapeToParam(y, fitResult.params); 153 | 154 | // Step 7: 155 | clampParamVec(fitResult.params); 156 | } while (norm(resOld-fitResult.params)>1e-3); 157 | } 158 | 159 | vector< ASMFitResult > ASMModel::fitAll( 160 | const Mat & img, 161 | const vector< cv::Rect > & detectedObjs, 162 | int verbose) { 163 | vector< ASMFitResult > fitResultV; 164 | for (uint i=0; iimg.cols) r.width = img.cols-r.x; 173 | if (r.y+r.height>img.rows) r.height = img.rows-r.y; 174 | 175 | // Fit it! 176 | ASMFitResult fitResult = fit(img(r), verbose); 177 | SimilarityTrans s2; 178 | s2.Xt = r.x; 179 | s2.Yt = r.y; 180 | s2.a = 1; 181 | fitResult.transformation = s2 * fitResult.transformation; 182 | fitResultV.push_back(fitResult); 183 | } 184 | return fitResultV; 185 | } 186 | 187 | void ASMModel::showResult(Mat& img, const vector< ASMFitResult >& res) 188 | { 189 | Mat mb; 190 | if (img.channels()==1) 191 | cv::cvtColor(img, mb, CV_GRAY2RGB); 192 | else 193 | mb = img.clone(); 194 | 195 | ShapeVec sv; 196 | for (uint i=0; i > V; 198 | res[i].toPointList(V); 199 | shapeInfo.drawMarkPointsOnImg(mb, V, true); 200 | } 201 | 202 | if (!img.empty()) 203 | imshow("hoho", mb); 204 | } 205 | 206 | ASMFitResult ASMModel::fit(const cv::Mat& img, int verbose) 207 | { 208 | ASMFitResult fitResult(this); 209 | // Step 2: Ensure it is a grayscale image 210 | Mat grayImg; 211 | if (img.channels() == 3){ 212 | cv::cvtColor(img, grayImg, CV_BGR2GRAY); 213 | } 214 | else 215 | grayImg = img; 216 | 217 | // Step 3: Resize each face image 218 | Mat resizedImg; 219 | // Resize the image to proper size 220 | double ratio; 221 | ratio = sqrt( double(40000) / (grayImg.rows * grayImg.cols)); 222 | cv::resize(grayImg, resizedImg, Size(grayImg.cols*ratio, grayImg.rows*ratio)); 223 | 224 | ModelImage curSearch; 225 | curSearch.setShapeInfo( &shapeInfo ); 226 | curSearch.loadTrainImage(resizedImg); 227 | 228 | fitResult.params = Mat_::zeros(nShapeParams, 1); 229 | 230 | ShapeVec &sv = curSearch.shapeVec; 231 | ShapeVec shape_old; 232 | 233 | projectParamToShape(fitResult.params, sv); 234 | SimilarityTrans st = sv.getShapeTransformFitingSize(resizedImg.size(), 235 | searchScaleRatio, 236 | searchInitXOffset, 237 | searchInitYOffset); 238 | fitResult.transformation = st; 239 | curSearch.buildFromShapeVec(st); 240 | 241 | pyramidLevel = 2; 242 | int k=localFeatureRad; 243 | 244 | ns=4; 245 | 246 | // sum of offsets of current iteration. 247 | int totalOffset; 248 | if (verbose >= ASM_FIT_VERBOSE_AT_LEVEL) 249 | curSearch.show(); 250 | // Update each point 251 | vector< Point_< int > > V; 252 | for (int l=this->pyramidLevel; l>=0; l--){ 253 | if (verbose >= ASM_FIT_VERBOSE_AT_LEVEL) 254 | printf("Level %d\n", l); 255 | Mat_ img=curSearch.getDerivImage(l); 256 | // printf("Image size: %dx%d\n", img.cols, img.rows); 257 | // at most 5 iterations for each level 258 | int runT; 259 | double avgMov; 260 | for (runT=0; runT<10; runT++){ 261 | // Backup current shape 262 | shape_old.fromPointList(curSearch.points); 263 | 264 | totalOffset = 0; 265 | vector< Point_< int > > bestEP(nMarkPoints); 266 | for (int i=0; inMarkPoints; i++){ 267 | if (verbose >= ASM_FIT_VERBOSE_AT_POINT) 268 | printf("Dealing point %d...\n", i); 269 | 270 | Mat_< double > nrmV(2*k+1, 1); 271 | double curBest=-1, ct; 272 | int bestI = 0; 273 | double absSum; 274 | for (int e=ns; e>=-ns; e--){ 275 | curSearch.getPointsOnNorm(i, k, l, V, 2*searchStepAreaRatio, e); 276 | 277 | absSum = 0; 278 | for (int j=-k;j<=k;j++){ 279 | nrmV(j+k, 0) = img(V[j+k]); 280 | absSum += fabs(nrmV(j+k, 0)); 281 | } 282 | nrmV *= 1/absSum; 283 | ct = cv::Mahalanobis(nrmV, this->meanG[l][i], this->iCovarG[l][i]); 284 | // printf("absSum: %lf, ct: %lf\n", absSum, ct); 285 | if (verbose >= ASM_FIT_VERBOSE_AT_POINT) 286 | curSearch.show(l, i, true, e); 287 | 288 | if (ct= ASM_FIT_VERBOSE_AT_POINT) 299 | curSearch.show(l, i, true, bestI); 300 | } 301 | for (int i=0;i0) curSearch.points[i].x += (1<<(l-1)); 305 | curSearch.points[i].y <<= l; 306 | if (l>0) curSearch.points[i].y += (1<<(l-1)); 307 | } 308 | curSearch.shapeVec.fromPointList(curSearch.points); 309 | 310 | if (verbose >= ASM_FIT_VERBOSE_AT_ITERATION) 311 | curSearch.show(l); 312 | 313 | // Project to PCA model and then back 314 | //findParamForShape(curSearch.shapeVec, fitResult); 315 | findParamForShapeBTSM(curSearch.shapeVec, shape_old, fitResult, fitResult, l); 316 | 317 | pcaPyr[l].backProject(fitResult.params, sv); 318 | 319 | // Reconstruct new shape 320 | curSearch.buildFromShapeVec(fitResult.transformation); 321 | 322 | avgMov = (double)totalOffset/nMarkPoints; 323 | if (verbose >= ASM_FIT_VERBOSE_AT_ITERATION){ 324 | printf("Iter %d: Average offset: %.3f\n", runT+1, avgMov); 325 | curSearch.show(l); 326 | } 327 | 328 | if (avgMov < 1.3){ 329 | runT++; 330 | break; 331 | } 332 | } 333 | if (verbose == ASM_FIT_VERBOSE_AT_LEVEL){ 334 | printf("%d iterations. average offset for last iter: %.3f\n", runT, avgMov); 335 | curSearch.show(l); 336 | } 337 | } 338 | 339 | SimilarityTrans s2; 340 | s2.a = 1/ratio; 341 | fitResult.transformation = s2 * fitResult.transformation; 342 | return fitResult; 343 | } 344 | 345 | void ASMModel::loadFromFile(ModelFile& file) 346 | { 347 | ShapeModel::loadFromFile(file); 348 | printf("Loading ASM model from file...\n"); 349 | 350 | file.readInt(localFeatureRad); 351 | file.readInt(ns); 352 | 353 | int i,j; 354 | int rows, cols; 355 | file.readInt(rows); 356 | file.readInt(cols); 357 | iCovarG.resize(pyramidLevel+1); 358 | for (i=0;i<=pyramidLevel;i++){ 359 | iCovarG[i].resize(nMarkPoints); 360 | for (j=0;jeigenvalues.rows; i++){ 384 | curSigma2 += pcaFullShape->eigenvalues.at(i, 0); 385 | } 386 | 387 | // Layer 2, 5 parameter 388 | for (i=0; i<5 && ieigenvalues.rows; i++){ 389 | curSigma2 -= pcaFullShape->eigenvalues.at(i, 0); 390 | } 391 | sigma2Pyr[2] = curSigma2 / (nMarkPoints*2-4); 392 | pcaPyr[2].eigenvalues = pcaFullShape->eigenvalues.rowRange(0, i); 393 | pcaPyr[2].eigenvectors = pcaFullShape->eigenvectors.rowRange(0, i); 394 | pcaPyr[2].mean = pcaFullShape->mean; 395 | 396 | // Layer 1, 20 parameter 397 | for (; i<20 && ieigenvalues.rows; i++){ 398 | curSigma2 -= pcaFullShape->eigenvalues.at(i, 0); 399 | } 400 | sigma2Pyr[1] = curSigma2 / (nMarkPoints*2-4); 401 | pcaPyr[1].eigenvalues = pcaFullShape->eigenvalues.rowRange(0, i); 402 | pcaPyr[1].eigenvectors = pcaFullShape->eigenvectors.rowRange(0, i); 403 | pcaPyr[1].mean = pcaFullShape->mean; 404 | 405 | /*sigma2Pyr[2] = sigma2Pyr[1] = */sigma2Pyr[0] = sigma2; 406 | /*pcaPyr[2] = pcaPyr[1]= */pcaPyr[0] = *pcaShape; 407 | } 408 | 409 | void ASMModel::saveToFile(ModelFile& file) 410 | { 411 | ShapeModel::saveToFile(file); 412 | 413 | file.writeInt(localFeatureRad); 414 | file.writeInt(ns); 415 | 416 | int i,j; 417 | int rows, cols; 418 | file.writeInt(rows = iCovarG[0][0].rows); 419 | file.writeInt(cols = iCovarG[0][0].cols); 420 | for (i=0;i<=pyramidLevel;i++){ 421 | for (j=0;j > &pV) const { 457 | ShapeVec sv; 458 | asmModel->projectParamToShape(params, sv); 459 | sv.restoreToPointList(pV, transformation); 460 | } 461 | 462 | } // Namespace -------------------------------------------------------------------------------- /src/lib/asmmodel.h: -------------------------------------------------------------------------------- 1 | #ifndef ASMMODEL_H 2 | #define ASMMODEL_H 3 | #include "shapemodel.h" 4 | #include "opencv2/imgproc.hpp" 5 | 6 | /*! \mainpage asmlib-opencv Documentation 7 | * 8 | * \section intro_sec Introduction 9 | * 10 | * An open source Active Shape Model implementation written by C++ using 11 | * OpenCV 2.0 (or above), no other dependencies. 12 | * 13 | * The library has been successfully compiled in Linux (both 32 and 64 bits), 14 | * Windows(both VC and MinGW) and Mac OS X. Both training and fitting codes 15 | * are provided. For Windows users, a binary demo is available for download. 16 | * 17 | * This doc is for codes and APIs. For general info, see: 18 | * http://code.google.com/p/asmlib-opencv/ 19 | * 20 | * For compiling the codes, running demo and building models, see the 21 | * Wiki Pages. 22 | * 23 | * For APIs, see \ref API "API". 24 | * 25 | */ 26 | 27 | /** 28 | * \todo Change the magic numbers to configurable variables. 29 | * \todo Smaller memory consumption while training. 30 | */ 31 | 32 | //! Namespace for all ASM related stuffs. 33 | namespace ASMLib { 34 | 35 | using cv::Point_; 36 | using cv::Mat_; 37 | 38 | #define ASM_FIT_VERBOSE_NOVERBOSE 0 39 | #define ASM_FIT_VERBOSE_AT_LEVEL 1 40 | #define ASM_FIT_VERBOSE_AT_ITERATION 2 41 | #define ASM_FIT_VERBOSE_AT_POINT 3 42 | 43 | class ASMModel; 44 | 45 | /** 46 | * \defgroup API APIs. 47 | */ 48 | 49 | //! Result for ASM fitting. 50 | /** 51 | * \ingroup API 52 | */ 53 | class CV_EXPORTS_W_SIMPLE ASMFitResult : public FitResult { 54 | private: 55 | ASMModel *asmModel; 56 | public: 57 | CV_WRAP ASMFitResult() : asmModel(NULL) {} 58 | CV_WRAP ASMFitResult(ASMModel *model): asmModel(model) {} 59 | 60 | void setModel(ASMModel *model) { asmModel = model; } 61 | 62 | //! Get the result in landmark point list form. 63 | void toPointList(vector< Point_ > &pV) const; 64 | }; 65 | 66 | //! Active Shape Model 67 | /** 68 | * \ingroup API 69 | */ 70 | class CV_EXPORTS_W ASMModel : public ShapeModel 71 | { 72 | private: 73 | //! Inverted Covariance matrix pyramids for each landmark point 74 | vector< vector< Mat_< double > > > iCovarG; 75 | 76 | //! Mean vector pyramids for each landmark point 77 | vector< vector< Mat_< double > > > meanG; 78 | 79 | //! parameter k for ASM 80 | int localFeatureRad; 81 | 82 | //! parameter ns for ASM 83 | int ns; 84 | 85 | //! Build statistic models for local difference at each landmark point. 86 | /// \note This is used when calculating Mahalanobis distance 87 | void buildLocalDiffStructure(); 88 | 89 | PCA pcaPyr[3]; 90 | double sigma2Pyr[3]; 91 | 92 | public: 93 | //! Empty ASM model. 94 | CV_WRAP ASMModel():localFeatureRad(4), ns(6){} 95 | 96 | //! Initialize by a file. 97 | CV_WRAP ASMModel(const string& filename) { loadFromFile(filename); } 98 | 99 | //! Search for objects (e.g. faces) in image, and run ASM on each object. 100 | /*! 101 | * This runs OpenCV object detection and fit ASM model on each detected 102 | * region. 103 | * 104 | \param img Input image 105 | \param detectedObjs Bounding boxes for all objects. (Usually it can be 106 | the result of OpenCV object detection) 107 | \param verbose Verbosity level. 108 | \return A vector of FitResult's. 109 | */ 110 | CV_WRAP vector< ASMFitResult > fitAll( 111 | const cv::Mat & img, 112 | const std::vector< cv::Rect > & detectedObjs, 113 | int verbose=0); 114 | 115 | //! Do ASM fitting on an image patch, return the fitted vector 116 | /*! 117 | * I'd recommend use ASMModel::fitAll in most cases. 118 | \param img An image patch (cropped by object detection). 119 | \param verbose Verbosity level. 120 | \return Result of ASM fitting. 121 | */ 122 | CV_WRAP ASMFitResult fit(const cv::Mat & img, int verbose=0); 123 | 124 | //! Build the model 125 | CV_WRAP void buildModel(const string& shapeDefFile, const string& ptsListFile); 126 | 127 | //! Save the model into a file 128 | CV_WRAP void saveToFile(const string& filename); 129 | 130 | //! Load the model from a file 131 | CV_WRAP void loadFromFile(const string& filename); 132 | 133 | //! Show the result in the image 134 | CV_WRAP void showResult(Mat &img, const vector< ASMFitResult > &res); 135 | 136 | //! Find the best parameter and transformation for the given shape. 137 | /*! 138 | \param ShapeVec a shape vector 139 | \param fitResult a ASM result. 140 | */ 141 | void findParamForShape(const ShapeVec &Y, ASMFitResult & fitResult); 142 | 143 | private: 144 | //! Use Bayesian Tangent Shape Model to find best parameter and transformation for the given shape 145 | /*! 146 | \param l level in image pyramid. 147 | */ 148 | void findParamForShapeBTSM(const ShapeVec &Y, const ShapeVec &Y_old, 149 | ASMFitResult & fitResult, ASMFitResult &b_old, int l); 150 | 151 | //! Save the model into a file 152 | void saveToFile(ModelFile &file); 153 | 154 | //! Load the model from a file 155 | void loadFromFile(ModelFile &file); 156 | }; 157 | 158 | } // Namespace 159 | #endif // ASMMODEL_H 160 | -------------------------------------------------------------------------------- /src/lib/modelfile.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This library is free software; you can redistribute it and/or 3 | modify it under the terms of the GNU Library General Public 4 | License version 2 as published by the Free Software Foundation. 5 | 6 | This library is distributed in the hope that it will be useful, 7 | but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 9 | Library General Public License for more details. 10 | 11 | You should have received a copy of the GNU Library General Public License 12 | along with this library; see the file COPYING.LIB. If not, write to 13 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 14 | Boston, MA 02110-1301, USA. 15 | */ 16 | 17 | #include "modelfile.h" 18 | 19 | namespace ASMLib { 20 | 21 | void ModelFile::writePCA(const PCA* p) 22 | { 23 | int i, j; 24 | int rows, cols; 25 | rows = p->eigenvectors.rows; 26 | cols = p->eigenvectors.cols; 27 | writeInt(rows); 28 | writeInt(cols); 29 | for (i=0;ieigenvectors.at(i, j)); 32 | for (i=0;ieigenvalues.at(i, 0)); 34 | 35 | for (i=0;imean.at(i, 0)); 37 | } 38 | 39 | PCA * ModelFile::readPCA(PCA * &p) 40 | { 41 | int i, j; 42 | p = new PCA(); 43 | int rows, cols; 44 | readInt(rows); 45 | readInt(cols); 46 | p->eigenvectors = Mat_::zeros(rows, cols); 47 | for (i=0;ieigenvectors.at(i, j)); 50 | p->eigenvalues = Mat_::zeros(rows, 1); 51 | for (i=0;ieigenvalues.at(i, 0)); 53 | 54 | p->mean = Mat_::zeros(cols, 1); 55 | for (i=0;imean.at(i, 0)); 57 | } 58 | return p; 59 | } 60 | 61 | } // Namespace 62 | -------------------------------------------------------------------------------- /src/lib/modelfile.h: -------------------------------------------------------------------------------- 1 | /* 2 | This library is free software; you can redistribute it and/or 3 | modify it under the terms of the GNU Library General Public 4 | License version 2 as published by the Free Software Foundation. 5 | 6 | This library is distributed in the hope that it will be useful, 7 | but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 9 | Library General Public License for more details. 10 | 11 | You should have received a copy of the GNU Library General Public License 12 | along with this library; see the file COPYING.LIB. If not, write to 13 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 14 | Boston, MA 02110-1301, USA. 15 | */ 16 | 17 | #ifndef MODELFILE_H 18 | #define MODELFILE_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include "opencv/cv.h" 24 | using cv::PCA; 25 | using cv::Mat_; 26 | using std::cin; 27 | using std::cout; 28 | using std::endl; 29 | using std::fstream; 30 | 31 | namespace ASMLib { 32 | // class ModelFile 33 | // { 34 | // public: 35 | // virtual void writeInt(int i){ fwrite(&i, sizeof(int), 1, fp); } 36 | // virtual int readInt(int &i) { fread(&i, sizeof(int), 1, fp); return i; } 37 | // 38 | // virtual void writeBool(bool b){ fwrite(&b, sizeof(bool), 1, fp); } 39 | // virtual int readBool(bool &b) { fread(&b, sizeof(bool), 1, fp); return b; } 40 | // 41 | // virtual void writeReal(double d) { fwrite(&d, sizeof(double), 1, fp); } 42 | // virtual double readReal(double &d) { fread(&d, sizeof(double), 1, fp); return d; } 43 | // 44 | // void writePCA(const PCA *p); 45 | // PCA * readPCA(PCA * &p); 46 | // 47 | // template < class T > 48 | // virtual void writeMat(const Mat_ &m){ 49 | // writeInt(m.rows); 50 | // writeInt(m.cols); 51 | // for (int i=0;i 57 | // Mat_ & readMat(Mat_ &m){ 58 | // int r,c; 59 | // readInt(r); 60 | // readInt(c); 61 | // m.create(r, c); 62 | // for (int i=0;i>i; return i; } 89 | 90 | void writeBool(bool b){ fs<>b; return b; } 92 | 93 | void writeReal(double d) { fs<>d; return d; } 95 | 96 | void writePCA(const PCA *p); 97 | PCA * readPCA(PCA * &p); 98 | 99 | template < class T > 100 | void writeMat(const Mat_ &m){ 101 | writeInt(m.rows); 102 | writeInt(m.cols); 103 | for (int i=0;i 110 | Mat_ & readMat(Mat_ &m){ 111 | int r,c; 112 | readInt(r); 113 | readInt(c); 114 | m.create(r, c); 115 | for (int i=0;i>m(i,j); 118 | return m; 119 | } 120 | 121 | void openFile(const char *fName, const char *mode){ 122 | if (mode[0]=='r') 123 | fs.open(fName, std::ios_base::in); 124 | else 125 | fs.open(fName, std::ios_base::out); 126 | if (!fs){ 127 | printf("Model file \"%s\" not found!!\n", fName); 128 | throw(""); 129 | } 130 | } 131 | void closeFile(){ fs.close(); } 132 | 133 | //ModelFile(){ } 134 | ~ModelFile(){ if (fs) fs.close(); } 135 | private: 136 | fstream fs; 137 | }; 138 | 139 | typedef ModelFile ModelFileAscii; 140 | } // Namespace 141 | #endif // MODELFILE_H 142 | -------------------------------------------------------------------------------- /src/lib/modelimage.cpp: -------------------------------------------------------------------------------- 1 | #include "modelimage.h" 2 | #include "afreader.h" 3 | using std::cerr; 4 | 5 | namespace ASMLib { 6 | 7 | ModelImage::ModelImage() 8 | { 9 | imgLoaded = false; 10 | this->shapeInfo = NULL; 11 | } 12 | 13 | bool ModelImage::readPTS( const char * filename ) 14 | { 15 | AFReader r(filename); 16 | 17 | if ( !r.IsValid() ) { 18 | printf("File %s not found!\n"); 19 | throw ""; 20 | return false; 21 | } 22 | 23 | int npoints; 24 | 25 | // read nb points 26 | r.Sync(); 27 | int c; 28 | do { 29 | c=fgetc(r.FH()); 30 | }while (c<'0'||c>'9'); 31 | ungetc(c, r.FH()); 32 | fscanf( r.FH(), "%i", &npoints ); 33 | fgetc(r.FH()); 34 | 35 | this->nMarkPoints = npoints; 36 | 37 | // resize this shape 38 | this->points.resize(npoints); 39 | 40 | // read all point data 41 | r.Sync(); 42 | 43 | // read point data 44 | int ix, iy; 45 | int i; 46 | for(i=0;ihostImageName = sFname.substr(0, sFname.rfind('.'))+".jpg"; 59 | FILE *fp = fopen(hostImageName.data(),"rb"); 60 | if (!fp) 61 | this->hostImageName = sFname.substr(0, sFname.rfind('.')); 62 | else 63 | fclose(fp); 64 | 65 | return true; 66 | } 67 | 68 | void ModelImage::initPointsByVector(const std::vector< cv::Point2i >& V) 69 | { 70 | this->nMarkPoints = V.size(); 71 | 72 | this->points = V; 73 | this->shapeVec.fromPointList(V); 74 | } 75 | 76 | 77 | bool ModelImage::loadTrainImage() 78 | { 79 | if (!imgLoaded){ 80 | Mat img = imread(this->hostImageName); 81 | if (img.empty()) { 82 | cerr << "(EE) Loading image " + this->hostImageName + " failed!" << endl; 83 | throw; 84 | } 85 | loadTrainImage(img); 86 | } 87 | return imgLoaded; 88 | } 89 | 90 | void ModelImage::buildFromShapeVec(SimilarityTrans& trans) 91 | { 92 | nMarkPoints = shapeVec.nPoints(); 93 | shapeVec.restoreToPointList(points, trans); 94 | } 95 | 96 | 97 | bool ModelImage::loadTrainImage(const Mat &img) 98 | { 99 | imgdata = img; 100 | // imshow("a", img); 101 | // cvWaitKey(0); 102 | cv::buildPyramid(imgdata, this->imgPyramid, 3); 103 | if (imgdata.channels() == 3){ 104 | this->imgPyrGray.resize(4); 105 | for (int i=0; i<=3; i++) 106 | cv::cvtColor(imgPyramid[i], imgPyrGray[i], CV_BGR2GRAY); 107 | } 108 | else { 109 | this->imgPyrGray.resize(4); 110 | for (int i=0; i<=3; i++) 111 | imgPyrGray[i] = imgPyramid[i]; 112 | } 113 | imgPyrGrayDeriv.resize(4); 114 | for (int i=0; i<=3; i++){ 115 | imgPyrGray[i].convertTo(imgPyrGrayDeriv[i], CV_64F); 116 | cv::Sobel(imgPyrGrayDeriv[i], imgPyrGrayDeriv[i], CV_64F, 1, 1); 117 | } 118 | this->imgLoaded = true; 119 | return imgLoaded; 120 | } 121 | 122 | bool ModelImage::releaseTrainImage() 123 | { 124 | if (imgLoaded){ 125 | imgdata.release(); 126 | for (int i=0; i<=3; i++){ 127 | imgPyramid[i].release(); 128 | imgPyrGray[i].release(); 129 | imgPyrGrayDeriv[i].release(); 130 | } 131 | imgLoaded = false; 132 | } 133 | return !imgLoaded; 134 | } 135 | 136 | Mat_< double > ModelImage::getLocalStruct(int pId, int k, int level, double step) 137 | { 138 | this->loadTrainImage(); 139 | vector< Point_< int > > pV; 140 | this->getPointsOnNorm(pId, k, level, pV, step); 141 | Mat_< double > diffV(2*k+1, 1); 142 | double absSum = 0; 143 | for (int i=k;i>=-k;i--){ 144 | diffV(i+k, 0) = imgPyrGrayDeriv[level](pV[i+k]); 145 | absSum += fabs(diffV(i+k, 0)); 146 | } 147 | if (absSum==0){ 148 | printf("Warning: absSum=0....Level: %d, pID: %d\n", level, pId); 149 | show(level, pId); 150 | } 151 | else 152 | diffV *= 1/absSum; 153 | 154 | return diffV; 155 | } 156 | 157 | Mat & ModelImage::getTrainImage(int level, bool gray){ 158 | if (gray) 159 | return imgPyrGray[level]; 160 | else 161 | return this->imgPyramid[level]; 162 | } 163 | 164 | void ModelImage::getPointsOnNorm(int pId, int k, int level, 165 | vector< Point_< int > > &V, double step, 166 | int pOffset) 167 | { 168 | // Find the norm direction 169 | Point_< double > va, vb, vDirection; 170 | va = points[shapeInfo->pointInfo[pId].connectFrom] - points[pId]; 171 | vb = points[shapeInfo->pointInfo[pId].connectTo] - points[pId]; 172 | double td; 173 | td = norm(va); 174 | if (td>1e-10) va *= 1 / td; 175 | td = norm(vb); 176 | if (td>1e-10) vb *= 1 / td; 177 | vDirection.x = - va.y + vb.y; 178 | vDirection.y = va.x - vb.x; 179 | if (norm(vDirection)<1e-10){ 180 | if (norm(va)>1e-10) 181 | vDirection = - va; 182 | else 183 | vDirection.x = 1, vDirection.y = 0; 184 | } 185 | else 186 | vDirection *= 1 / norm(vDirection); 187 | 188 | int i, j; 189 | int nx, ny; 190 | int offsetX, offsetY; 191 | int prevX, prevY; 192 | 193 | // Find the center point, here step===1 194 | prevX = 0; 195 | prevY = 0; 196 | nx = ny = 0; 197 | j = 1; 198 | for (i=1; i<=abs(pOffset);i++){ 199 | do { 200 | nx = cvRound(j*vDirection.x); 201 | ny = cvRound(j*vDirection.y); 202 | j++; 203 | } while (nx == prevX && ny == prevY); 204 | 205 | prevX = nx; 206 | prevY = ny; 207 | } 208 | j--; 209 | // printf("%d, %d\n", nx, ny); 210 | if (pOffset>0) 211 | offsetX = nx, offsetY = ny; 212 | else 213 | offsetX = -nx, offsetY = -ny; 214 | // offsetX = offsetY = 0; 215 | 216 | // Apply the "step", and find points 217 | vDirection *= step; 218 | prevX = 0; 219 | prevY = 0; 220 | nx = ny = 0; 221 | 222 | // Test best j 223 | j = 1; 224 | for (i=1; i<=k;i++){ 225 | do { 226 | nx = cvRound(j*vDirection.x); 227 | ny = cvRound(j*vDirection.y); 228 | j++; 229 | } while (nx == prevX && ny == prevY); 230 | 231 | prevX = nx; 232 | prevY = ny; 233 | } 234 | j--; 235 | 236 | V.resize(2*k+1); 237 | int rX, rY; 238 | for (i=k;i>=-k;i--){ 239 | rX = (points[pId].x>>level) + nx + offsetX; 240 | rY = (points[pId].y>>level) + ny + offsetY; 241 | if (rX<0) rX = 0; 242 | if (rY<0) rY = 0; 243 | if (rX >= (imgdata.cols>>level)) rX = (imgdata.cols>>level) - 1; 244 | if (rY >= (imgdata.rows>>level)) rY = (imgdata.rows>>level) - 1; 245 | V[i+k] = Point_< int >(rX, rY); 246 | //v(i+(k+1), 0) = this->imgdata.at< char >(nx, ny); 247 | do { 248 | nx = cvRound(j*vDirection.x); 249 | ny = cvRound(j*vDirection.y); 250 | j--; 251 | } while (nx == prevX && ny == prevY); 252 | prevX = nx; 253 | prevY = ny; 254 | } 255 | } 256 | 257 | Mat ModelImage::show(int l, int pId, bool showInWin, int highLight) 258 | { 259 | Mat mb; 260 | if (imgPyramid[0].channels()==1) 261 | cv::cvtColor(imgPyramid[0], mb, CV_GRAY2RGB); 262 | else 263 | mb = imgPyramid[0].clone(); 264 | 265 | shapeInfo->drawMarkPointsOnImg(mb, points, true); 266 | 267 | for (int i=0;i > pV; 271 | getPointsOnNorm(i, 4, l, pV, 2, highLight); 272 | for (int j=0; j<9; j++) 273 | if (highLight==100/*j-3*/){ 274 | cv::circle(mb, Point_< int >(pV[j].x<(pV[j].x< 4 | #include 5 | #include "opencv/cv.h" 6 | #include "opencv/highgui.h" 7 | #include "shapevec.h" 8 | #include "shapeinfo.h" 9 | using std::string; 10 | using std::vector; 11 | using cv::Point_; 12 | using cv::Mat_; 13 | using cv::Mat; 14 | using cv::imread; 15 | using cv::Size; 16 | 17 | namespace ASMLib { 18 | 19 | //! Image and image related operations. 20 | class ModelImage 21 | { 22 | protected: 23 | /// The number of landmark points. 24 | int nMarkPoints; 25 | 26 | /// Optional 'host image' filename including full path. 27 | string hostImageName; 28 | 29 | /// the training image 30 | Mat imgdata; 31 | 32 | /// Image pyramid 33 | vector< Mat > imgPyramid; 34 | vector< Mat_ > imgPyrGrayDeriv; 35 | vector< Mat_ > imgPyrGray; 36 | 37 | /// Is the image loaded? 38 | bool imgLoaded; 39 | 40 | /// Information about shapes and paths. 41 | ShapeInfo *shapeInfo; 42 | 43 | public: 44 | /// Landmark points 45 | vector< Point_< int > > points; 46 | 47 | /// Shape vectors 48 | ShapeVec shapeVec; 49 | 50 | /// Load training image from saved host image 51 | bool loadTrainImage(); 52 | 53 | /// Load training image 54 | bool loadTrainImage(const Mat &img); 55 | 56 | void setShapeInfo(ShapeInfo *si){ shapeInfo = si; } 57 | 58 | 59 | /// Release Training Image 60 | bool releaseTrainImage(); 61 | 62 | /// Get local structure 63 | /*! 64 | \param pId id of the point 65 | \param k how many points to get on either direction 66 | \param level position in the pyramid level 67 | \param step VERY IMPORTANT, for a image with area of 10000, 1.0 may be a good choice 68 | */ 69 | Mat_< double > getLocalStruct(int pId, int k, int level, double step); 70 | 71 | /// Get the coordinates of points at normal direction of a landmark point 72 | /*! 73 | \param pId id of the point 74 | \param k how many points to get on either direction 75 | \param level position in the pyramid level 76 | \param V the vector that save results. 77 | \param step VERY IMPORTANT, for a image with area of 10000, 1.0 may be a good choice 78 | \param pOffset when searching for best points, use the p'th point along the profile as the center 79 | */ 80 | void getPointsOnNorm(int pId, int k, int level, 81 | vector< Point_< int > > &V, 82 | double step, int pOffset=0); 83 | 84 | //! Get the image saved at specified level 85 | Mat & getTrainImage(int level=0, bool gray=false); 86 | 87 | /// Return the derivative image at specified level; 88 | Mat & getDerivImage(int level){ return imgPyrGrayDeriv[level]; } 89 | 90 | /// Host image (if any). 91 | inline const string &HostImage() const { return hostImageName; } 92 | 93 | void buildFromShapeVec(SimilarityTrans &trans); 94 | 95 | /// Set the host image. 96 | void setHostImage( const char * hostImageFilename ) { 97 | hostImageName = hostImageFilename; 98 | } 99 | 100 | /// The number of shape points. 101 | inline int NPoints() const { return nMarkPoints; } 102 | 103 | /// Read mark points information from a PTS file 104 | bool readPTS( const char * filename ); 105 | 106 | /// Set mark points information from a vector of points 107 | void initPointsByVector( const vector< cv::Point2i > &V); 108 | 109 | //! Show the image interactively 110 | Mat show(int level = 0, int pId = -1, bool showInWin = true, int highLight = 0); 111 | 112 | ModelImage(); 113 | }; 114 | 115 | } // Namespace 116 | #endif // MODELIMAGE_H 117 | -------------------------------------------------------------------------------- /src/lib/python.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MODULESTR "asmlib" 4 | 5 | #include "asmmodel.h" 6 | 7 | #include "opencv2/python/cv2.hpp" 8 | #include "opencv2/python/cv2support.cpp" 9 | 10 | using namespace cv; 11 | using namespace ASMLib; 12 | 13 | typedef std::vector vector_ASMFitResult; 14 | 15 | #include "ASMLib_generated_types.h" 16 | 17 | template<> struct pyopencvVecConverter 18 | { 19 | static bool to(PyObject* obj, std::vector& value, const ArgInfo info) 20 | { 21 | return pyopencv_to_generic_vec(obj, value, info); 22 | } 23 | 24 | static PyObject* from(const std::vector& value) 25 | { 26 | return pyopencv_from_generic_vec(value); 27 | } 28 | }; 29 | 30 | template<> bool pyopencv_to(PyObject* src, ASMLib::ASMModel& dst, const ArgInfo info) 31 | { 32 | if( src == NULL || src == Py_None ) 33 | return true; 34 | if(!PyObject_TypeCheck(src, &pyopencv_ASMModel_Type)) 35 | { 36 | failmsg("Expected ASMModel for argument '%%s'", info.name); 37 | return false; 38 | } 39 | dst = *((pyopencv_ASMModel_t*)src)->v; 40 | return true; 41 | } 42 | 43 | #include "ASMLib_generated_funcs.h" 44 | 45 | 46 | static PyMethodDef methods[] = { 47 | #include "ASMLib_generated_func_tab.h" 48 | {NULL, NULL, 0, NULL}, 49 | }; 50 | 51 | extern "C" CV_EXPORTS void initasmlib(); 52 | 53 | void initasmlib() { 54 | import_array(); 55 | 56 | #include "ASMLib_generated_type_reg.h" 57 | 58 | PyObject* m = Py_InitModule(MODULESTR, methods); 59 | PyObject* d = PyModule_GetDict(m); 60 | 61 | opencv_error = PyErr_NewException((char*)MODULESTR".error", NULL, NULL); 62 | PyDict_SetItemString(d, "error", opencv_error); 63 | 64 | #include "ASMLib_generated_const_reg.h" 65 | } 66 | -------------------------------------------------------------------------------- /src/lib/shapeinfo.cpp: -------------------------------------------------------------------------------- 1 | #include "shapeinfo.h" 2 | 3 | namespace ASMLib { 4 | void ShapeInfo::writeToFile(ModelFile &f) const { 5 | f.writeInt(nContours); 6 | for (int i=0;i& vP, bool drawInPlace) const 39 | { 40 | Mat mb; 41 | if (drawInPlace) 42 | mb = img; 43 | else 44 | mb = img.clone(); 45 | 46 | for (uint i=0;i contourStartInd; 25 | 26 | /*! 27 | * \brief If a contour is closed or not. 28 | * 29 | * A contour can be either open (face contour) or closed (eye contour). 30 | */ 31 | vector< int > contourIsClosed; 32 | 33 | //! The number of contours in a shape. 34 | int nContours; 35 | 36 | public: 37 | //! Point in shape definition. 38 | struct PointInfo 39 | { 40 | int type; 41 | int pathId; 42 | int connectFrom; 43 | int connectTo; 44 | }; 45 | 46 | //! Information for each landmark point. 47 | vector< PointInfo > pointInfo; 48 | 49 | //! Dump info to a model file. 50 | void writeToFile(ModelFile &f) const; 51 | 52 | //! Load shape info from a model file. 53 | void readFromFile(ModelFile &f); 54 | 55 | //! Load from shape description file, return the number landmark points. 56 | int loadFromShapeDescFile(AFReader &shapeDefFile); 57 | 58 | //! Draw a list of points on an image. 59 | Mat drawMarkPointsOnImg( 60 | Mat &img, const vector< Point > &vP, bool drawInPlace = false) const; 61 | }; 62 | } // Namespace 63 | #endif -------------------------------------------------------------------------------- /src/lib/shapemodel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "shapemodel.h" 3 | #include "asmmodel.h" 4 | #include "afreader.h" 5 | 6 | namespace ASMLib { 7 | 8 | ShapeModel::ShapeModel() 9 | { 10 | /// \todo Make the "3" here a constant or a configurable variable. 11 | pyramidLevel = 3; 12 | } 13 | 14 | void ShapeModel::loadShapeInfo(const char* shapeFileName) 15 | { 16 | printf("Loading shape info from %s\n", shapeFileName); 17 | 18 | AFReader shapeDefFile(shapeFileName); 19 | FILE *fp = shapeDefFile.FH(); 20 | 21 | nMarkPoints = shapeInfo.loadFromShapeDescFile(shapeDefFile); 22 | 23 | // r.y -= r.height*? 24 | shapeDefFile.Sync(); 25 | fscanf(fp, "%lf", &searchYOffset); 26 | // r.x -= r.width*? 27 | shapeDefFile.Sync(); 28 | fscanf(fp, "%lf", &searchXOffset); 29 | // r.width *= ? 30 | shapeDefFile.Sync(); 31 | fscanf(fp, "%lf", &searchWScale); 32 | // r.height *= ? 33 | shapeDefFile.Sync(); 34 | fscanf(fp, "%lf", &searchHScale); 35 | // step: ?*100/sqrt(area) 36 | shapeDefFile.Sync(); 37 | fscanf(fp, "%lf", &searchStepAreaRatio); 38 | 39 | // init scale ratio when searching 40 | shapeDefFile.Sync(); 41 | fscanf(fp, "%lf", &searchScaleRatio); 42 | // init X offset when searching 43 | shapeDefFile.Sync(); 44 | fscanf(fp, "%lf", &searchInitXOffset); 45 | // init Y offset when searching 46 | shapeDefFile.Sync(); 47 | fscanf(fp, "%lf", &searchInitYOffset); 48 | 49 | } 50 | 51 | void ShapeModel::readTrainData(const char *listFileName) 52 | { 53 | // Find the directory of the list file 54 | string sName(listFileName), listDir; 55 | int posD; 56 | posD = sName.find_last_of("/\\"); 57 | if (posD != string::npos) 58 | listDir = sName.substr(0, posD+1); 59 | else 60 | listDir = "./"; 61 | 62 | FILE *fp = fopen(listFileName,"r"); 63 | if (fp==NULL){ 64 | printf("ERROR! list file %s not found!!", listFileName); 65 | throw("ERROR! list file not found!!"); 66 | } 67 | printf("Reading data from %s...\n", listFileName); 68 | ModelImage *ss; 69 | char sBuf[300]; 70 | int l; 71 | string ptsPath; 72 | while (!feof(fp)){ 73 | char * nk=fgets(sBuf,300,fp); 74 | l=strlen(sBuf); 75 | if (nk>0 && sBuf[l-1]=='\n') 76 | sBuf[l-1]=0; 77 | if (nk==0 || sBuf[0]==0) 78 | continue; 79 | if (sBuf[0]=='/') 80 | ptsPath = sBuf; 81 | else 82 | ptsPath = listDir + sBuf; 83 | 84 | ss = new ModelImage(); 85 | ss->readPTS(ptsPath.data()); 86 | ss->setShapeInfo( &shapeInfo ); 87 | this->imageSet.push_back(*ss); 88 | delete ss; 89 | } 90 | this->nTrain = imageSet.size(); 91 | this->nMarkPoints = imageSet[0].NPoints(); 92 | fclose(fp); 93 | } 94 | 95 | void ShapeModel::buildModel(const string& shapeDefFile, const string& ptsListFile) 96 | { 97 | loadShapeInfo(shapeDefFile.c_str()); 98 | readTrainData(ptsListFile.c_str()); 99 | 100 | this->alignShapes(); 101 | this->buildPCA(); 102 | //preparePatterns(); 103 | } 104 | 105 | void ShapeModel::alignShapes() 106 | { 107 | int i, nss; 108 | nss = imageSet.size(); 109 | 110 | // Align 111 | for (i=0;i1e-10); 130 | 131 | meanShape = curMean; 132 | } 133 | 134 | void ShapeModel::buildPCA() 135 | { 136 | // PCA 137 | int i, j; 138 | int vD = imageSet[0].shapeVec.rows; 139 | Mat_ pca_data; 140 | pca_data.create(vD, nTrain); 141 | for (i=0;i(), CV_PCA_DATA_AS_COL, 0); 147 | double eigValueSum, sCur; 148 | eigValueSum = cv::sum(pcaShape->eigenvalues)[0]; 149 | sCur = 0; 150 | printf("(II) PCA Rows: %d, Var: %lf\n", 151 | pcaShape->eigenvalues.rows, eigValueSum); 152 | 153 | // printf("Total Data: %d\n", pcaShape->eigenvalues.rows); 154 | // for (i=0; ieigenvalues.rows; i++){ 155 | // printf("%d: %g\n", i, pcaShape->eigenvalues.at(i, 0)); 156 | // } 157 | 158 | 159 | 160 | for (i=0;ieigenvalues.rows && i<40;i++){ 161 | sCur += pcaShape->eigenvalues.at(i, 0); 162 | // printf("%d: %g\n", i, pcaShape->eigenvalues.at(i, 0)); 163 | if (sCur>eigValueSum*0.98) 164 | break; 165 | } 166 | 167 | // Prepare for the BTSM 168 | this->sigma2 = (eigValueSum - sCur)/(vD - 4); 169 | printf("sssiggg: %g\n", sigma2); 170 | this->pcaFullShape = new PCA(); 171 | pcaFullShape->eigenvalues = pcaShape->eigenvalues.clone(); 172 | pcaFullShape->eigenvectors = pcaShape->eigenvectors.clone(); 173 | pcaFullShape->mean = pcaShape->mean.clone(); 174 | 175 | if (ieigenvalues.rows) 176 | nShapeParams = i+1; 177 | else 178 | nShapeParams = i; 179 | pcaShape->eigenvalues = pcaShape->eigenvalues.rowRange(0, nShapeParams); 180 | pcaShape->eigenvectors = pcaShape->eigenvectors.rowRange(0, nShapeParams); 181 | printf("(II) Shape Model: reserved parameters:%d, variance: %.2f%%\n", nShapeParams, 100*sCur/eigValueSum); 182 | } 183 | 184 | void ShapeModel::preparePatterns() 185 | { 186 | // Fat or thin? determined by width/height 187 | cv::Rect_ r; 188 | double ratio; 189 | Mat_ cParam, pt; 190 | Mat_ sumParam; 191 | double sumW; 192 | 193 | sumW = 0; 194 | sumParam = Mat_::zeros(nShapeParams, 1); 195 | 196 | for (int i=0; i0.7){ 202 | pt = cParam * (ratio-0.7); 203 | sumParam += pt; 204 | sumW += ratio-0.7; 205 | } 206 | } 207 | sumParam /= sumW; 208 | sumParam = normalizeParam(sumParam); 209 | printf("Fat & Thin: "); 210 | for (int i=0; i::zeros(nShapeParams, 1); 217 | Point_ v; 218 | double l1, l2; 219 | for (int i=0; i350){ 228 | pt = cParam * (ratio-350); 229 | sumParam += pt; 230 | sumW += ratio-350; 231 | } 232 | } 233 | sumParam /= sumW; 234 | sumParam = normalizeParam(sumParam); 235 | printf("eyeSize: "); 236 | for (int i=0; i & paramVec, 302 | ShapeVec & shapeVec) 303 | { 304 | this->pcaShape->backProject(paramVec, shapeVec); 305 | } 306 | 307 | void ShapeModel::projectShapeToParam(const ShapeVec & shapeVec, 308 | Mat_ & paramVec) 309 | { 310 | this->pcaShape->project(shapeVec, paramVec); 311 | } 312 | 313 | void ShapeModel::clampParamVec( Mat_< double > ¶mVec ) 314 | { 315 | /// \Todo: Change "3" to a configurable variable. 316 | for (int i=0;inShapeParams;i++){ 317 | double ei = sqrt(pcaShape->eigenvalues.at(i, 0)); 318 | if (paramVec(i, 0) > 3*ei) 319 | paramVec(i, 0) = 3*ei; 320 | else if (paramVec(i, 0) < -3*ei) 321 | paramVec(i, 0) = -3*ei; 322 | } 323 | } 324 | 325 | using cv::namedWindow; 326 | using cv::createTrackbar; 327 | using cv::setTrackbarPos; 328 | 329 | //! Callback function for updating value. 330 | void viewShapeUpdateValue(int pos, void *data) 331 | { 332 | ShapeModel::ModelViewInfo *pInfo = (ShapeModel::ModelViewInfo *)data; 333 | pInfo->vList[pInfo->curParam] = pos; 334 | ((ShapeModel *)(pInfo->pModel))->viewShapeModelUpdate(pInfo); 335 | } 336 | 337 | //! Callback function for choosing a different parameter. 338 | void viewShapeUpdateCurParam(int pos, void *data) 339 | { 340 | ShapeModel::ModelViewInfo *pInfo = (ShapeModel::ModelViewInfo *)data; 341 | pInfo->curParam = pos; 342 | cvSetTrackbarPos("param value", "Viewing Shape Model", 343 | pInfo->vList[pos]); 344 | } 345 | 346 | void ShapeModel::viewShapeModelUpdate(ModelViewInfo *pInfo) 347 | { 348 | Mat_< double > paramV; 349 | paramV.create(this->nShapeParams, 1); 350 | for (int i=0;ivList[i]/30.0 - 0.5)*6* 352 | sqrt(pcaShape->eigenvalues.at(i, 0)); 353 | } 354 | Mat_ img; 355 | 356 | ModelImage s; 357 | s.setShapeInfo(&shapeInfo); 358 | s.loadTrainImage(Mat_::ones(190*2, 160*2)*255); 359 | projectParamToShape(paramV, s.shapeVec); 360 | SimilarityTrans st = s.shapeVec.getShapeTransformFitingSize( 361 | Size(320, 380)); 362 | s.buildFromShapeVec(st); 363 | img = s.show(0, -1, false); 364 | imshow("Viewing Shape Model", img); 365 | } 366 | 367 | void ShapeModel::viewShapeModel() 368 | { 369 | int q1, q2; 370 | static ModelViewInfo vData; 371 | vData.vList.resize(this->nShapeParams, 30/2); 372 | vData.pModel = this; 373 | vData.curParam = 0; 374 | viewShapeModelUpdate(&vData); 375 | q1 = 15; 376 | q2 = 0; 377 | namedWindow("Viewing Shape Model", CV_WINDOW_AUTOSIZE); 378 | createTrackbar("param value", "Viewing Shape Model", 379 | &q1, 30, &viewShapeUpdateValue, &vData); 380 | createTrackbar("which param", "Viewing Shape Model", 381 | &q2, nShapeParams-1, &viewShapeUpdateCurParam, &vData); 382 | } 383 | 384 | } // Namespace -------------------------------------------------------------------------------- /src/lib/shapemodel.h: -------------------------------------------------------------------------------- 1 | #ifndef SHAPEMODEL_H 2 | #define SHAPEMODEL_H 3 | 4 | #include "modelimage.h" 5 | #include "opencv/cv.h" 6 | #include "modelfile.h" 7 | #include 8 | using std::vector; 9 | using cv::PCA; 10 | 11 | namespace ASMLib { 12 | 13 | //! Base class for ASM/AAM fitting result 14 | struct CV_EXPORTS_W FitResult{ 15 | //! Parameters for the model 16 | Mat_< double > params; 17 | 18 | //! The similarity transformation needed to recover shape 19 | SimilarityTrans transformation; 20 | }; 21 | 22 | //! The Statistical Shape model for training sets. 23 | /** 24 | * After all the training shapes are aligned, a PCA model is built so that a 25 | * shape can be roughly described by a few parameters (PCA coefficients). 26 | * 27 | * This class is responsible for building the statistical shape model and 28 | * provides some helper functions. 29 | */ 30 | class CV_EXPORTS_W ShapeModel 31 | { 32 | public: 33 | CV_WRAP ShapeModel(); 34 | 35 | //! Save the model into a file 36 | virtual void saveToFile(ModelFile &file); 37 | 38 | //! Load the model from a file 39 | virtual void loadFromFile(ModelFile &file); 40 | 41 | //! File names are stored in the list file 42 | void readTrainData(const char *listFileName); 43 | 44 | //! Load shape information 45 | void loadShapeInfo(const char *shapeFileName); 46 | 47 | //! Set the level for the image pyramid 48 | /*! 49 | \param l Image from level 0 to l will be considered during training 50 | and searching. 51 | */ 52 | void setPyramidLevel(int l){ pyramidLevel = l; } 53 | 54 | //! Project a parameter vector to a shape 55 | /*! 56 | \param paramVec parameter vector. 57 | \param shapeVec the shape corresponding to the parameter vector 58 | */ 59 | void projectParamToShape(const Mat_ & paramVec, ShapeVec &shapeVec); 60 | 61 | //! Project a shape to a parameter vector 62 | /*! 63 | \param shapeVec the shape corresponding to the parameter vector 64 | \param paramVec parameter vector. 65 | */ 66 | void projectShapeToParam(const ShapeVec & shapeVec, Mat_ ¶mVec); 67 | 68 | ShapeInfo & getShapeInfo(){ return shapeInfo;} 69 | 70 | //! Normalize an parameter vector(0..1) 71 | Mat_< double > normalizeParam(const Mat_ &p){ 72 | Mat_ n = p.clone(); 73 | for (int i=0; ieigenvalues.at(i, 0)); 75 | return n; 76 | } 77 | 78 | //! Reconstruct parameter vector from normalized vector 79 | Mat_< double > reConFromNorm(const Mat_ &p){ 80 | Mat_ n = p.clone(); 81 | for (int i=0; ieigenvalues.at(i, 0)); 83 | return n; 84 | } 85 | 86 | 87 | // For viewing the model 88 | //! Used for viewing model 89 | struct ModelViewInfo 90 | { 91 | vector< int > vList; 92 | int curParam; 93 | void *pModel; 94 | }; 95 | 96 | //! An interactive UI for viewing the statistical model. 97 | void viewShapeModel(); 98 | 99 | //! Update viewing UI with new parameters. (called by callbacks) 100 | void viewShapeModelUpdate(ModelViewInfo *pInfo); 101 | protected: 102 | //! level for the image pyramid. 103 | int pyramidLevel; 104 | 105 | //! Number of landmark points in a image. 106 | int nMarkPoints; 107 | 108 | //! Number of training images. 109 | int nTrain; 110 | 111 | //! All the images, with labelled markpoints. 112 | vector imageSet; 113 | 114 | //! Path info for shapes; 115 | ShapeInfo shapeInfo; 116 | 117 | //! Mean shape after aligning 118 | ShapeVec meanShape; 119 | 120 | //! Refine a parameter vector by clamping. 121 | void clampParamVec( Mat_< double > ¶mVec ); 122 | 123 | //! PCA model for shapes. 124 | PCA *pcaShape; 125 | //! Number of eigen vectors reserved for shape model. 126 | int nShapeParams; 127 | 128 | //! Data for BTSM: \f$\sigma^2\f$ 129 | double sigma2; 130 | 131 | //! Data for BTSM: Full \f$\phi\f$ 132 | PCA *pcaFullShape; 133 | 134 | //! Align the shapes and build a model. 135 | /** 136 | * \param shapeDefFile Shape definition file. 137 | * \param ptsListFile File containting a list of pts files. 138 | */ 139 | void buildModel(const string& shapeDefFile, const string& ptsListFile); 140 | private: 141 | //! Build PCA model for shapes 142 | void buildPCA(); 143 | 144 | //! Align shapes iteratively 145 | void alignShapes(); 146 | 147 | //! Find patterns~ 148 | void preparePatterns(); 149 | 150 | protected: 151 | //! r.y -= r.height*? 152 | double searchYOffset; 153 | 154 | //! r.x -= r.width*? 155 | double searchXOffset; 156 | 157 | //! r.width *= ? 158 | double searchWScale; 159 | //! r.height *= ? 160 | double searchHScale; 161 | 162 | //! step: ?*100/sqrt(area) 163 | double searchStepAreaRatio; 164 | 165 | //! init scale ratio when searching 166 | double searchScaleRatio; 167 | //! init X offset when searching 168 | double searchInitXOffset; 169 | //! init Y offset when searching 170 | double searchInitYOffset; 171 | }; 172 | } // Namespace 173 | #endif // SHAPEMODEL_H 174 | -------------------------------------------------------------------------------- /src/lib/shapevec.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This library is free software; you can redistribute it and/or 3 | modify it under the terms of the GNU Library General Public 4 | License version 2 as published by the Free Software Foundation. 5 | 6 | This library is distributed in the hope that it will be useful, 7 | but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 9 | Library General Public License for more details. 10 | 11 | You should have received a copy of the GNU Library General Public License 12 | along with this library; see the file COPYING.LIB. If not, write to 13 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 14 | Boston, MA 02110-1301, USA. 15 | */ 16 | 17 | #include "shapevec.h" 18 | 19 | namespace ASMLib { 20 | void ShapeVec::zeroGravity() 21 | { 22 | doTranslate(-getXMean(), -getYMean()); 23 | } 24 | 25 | void ShapeVec::scaleToOne() 26 | { 27 | doScale(1/norm(*this)); 28 | } 29 | 30 | void ShapeVec::alignTo( const ShapeVec &ref ) 31 | { 32 | // Find best scale factor and theta. 33 | static SimilarityTrans st; 34 | st.setTransformByAlign(*this, ref); 35 | st.transform(*this, *this); 36 | // double b = 0; 37 | // int n = rows>>1; 38 | // for (int i=0; idot(ref)); 42 | } 43 | 44 | void ShapeVec::doTranslate(double vX, double vY) 45 | { 46 | for (int i=0; i >& points, 63 | const SimilarityTrans& st) 64 | { 65 | // resize this shape 66 | points.resize(this->nPoints()); 67 | ShapeVec sv; 68 | st.transform(*this, sv); 69 | for (int i=0;i ShapeVec::getBoundRect() 76 | { 77 | // Estimate scale 78 | int nP = nPoints(); 79 | double minX=1e10, minY=1e10, maxX=-1e10, maxY=-1e10; 80 | double x, y; 81 | for (int i=0;imaxX) maxX = x; 86 | if (ymaxY) maxY = y; 88 | } 89 | return cv::Rect_< double >( 90 | cv::Point_(minX, minY), 91 | cv::Size_(maxX-minX, maxY-minY)); 92 | } 93 | 94 | SimilarityTrans ShapeVec::getShapeTransformFitingSize( 95 | const cv::Size &rect, 96 | double scaleRatio, double xOffset, double yOffset) 97 | { 98 | // Estimate scale and translate 99 | cv::Rect_ bdRect=getBoundRect(); 100 | // printf("minX: %g, minY: %g, maxX: %g, maxY: %g\n", 101 | // minX, minY, maxX, maxY); 102 | double ratioX, ratioY, ratio; 103 | ratioX = rect.width/bdRect.width; 104 | ratioY = rect.height/bdRect.height; 105 | if (ratioX < ratioY) 106 | ratio = ratioX; 107 | else 108 | ratio = ratioY; 109 | double transX, transY; 110 | 111 | ratio *= scaleRatio; 112 | transX = bdRect.x - bdRect.width*(ratioX/ratio-1 + xOffset)/2; 113 | transY = bdRect.y - bdRect.height*(ratioY/ratio-1 + yOffset)/2; 114 | 115 | SimilarityTrans st; 116 | st.a = ratio; 117 | st.b = 0; 118 | st.Xt = -transX*ratio; 119 | st.Yt = -transY*ratio; 120 | // printf("%lf, %lf\n", st.Xt, st.Yt); 121 | return st; 122 | } 123 | 124 | void ShapeVec::fromPointList(const std::vector< cv::Point2i >& v) 125 | { 126 | this->create(v.size()*2, 1); 127 | for (int i=0; i<(rows>>1); i++){ 128 | (*this)(i, 0) = v[i].x; 129 | (*this)(i+(rows>>1), 0) = v[i].y; 130 | // shapeVec.setTransform(1, 0, 0); 131 | } 132 | } 133 | 134 | } // Namespace -------------------------------------------------------------------------------- /src/lib/shapevec.h: -------------------------------------------------------------------------------- 1 | #ifndef SHAPEVEC_H 2 | #define SHAPEVEC_H 3 | 4 | #include "similaritytrans.h" 5 | 6 | #include "opencv2/core.hpp" 7 | #include "opencv2/highgui.hpp" 8 | #include 9 | using std::vector; 10 | using cv::Point_; 11 | using cv::Mat_; 12 | using cv::Mat; 13 | using cv::imread; 14 | using cv::mean; 15 | typedef unsigned int uint; 16 | 17 | namespace ASMLib { 18 | 19 | //! Shape vector, presented as a 1-D OpenCV matrix. 20 | class ShapeVec : public Mat_< double > { 21 | public: 22 | //! Construct by a matrix. 23 | ShapeVec(const Mat_< double > &a):Mat_< double >(a){} 24 | 25 | //! Assign shape vector by a Mat. 26 | ShapeVec & operator =(const Mat_< double > &a) { 27 | Mat_< double >::operator=(a); 28 | return *this; 29 | } 30 | 31 | //! Default constructor does nothing. 32 | ShapeVec(){} 33 | 34 | //! Align to another shape vector 35 | void alignTo(const ShapeVec & ref); 36 | 37 | //! Move the center of gravity to origin. X and Y are moved seperately. 38 | void zeroGravity(); 39 | 40 | //! Scale the vector's norm to 1. 41 | void scaleToOne(); 42 | 43 | void doTranslate(double vX, double vY); 44 | void doScale(double r); 45 | 46 | double getXMean() const {return mean(rowRange(0, rows / 2))[0];} 47 | double getYMean() const {return mean(rowRange(rows / 2, rows))[0];} 48 | 49 | double X(int i) const { return (*this)(i, 0); } 50 | double & X(int i) { return (*this)(i, 0); } 51 | double Y(int i) const { return (*this)(i + (rows >> 1), 0); } 52 | double & Y(int i) { return (*this)(i+(rows>>1), 0); } 53 | 54 | cv::Rect_ getBoundRect(); 55 | 56 | //! Do a similarity transform and restore it into a list of points. 57 | void restoreToPointList( 58 | vector< Point_< int > > &v, 59 | const SimilarityTrans &st 60 | ); 61 | 62 | //! Flatten a list of points to the current shape vector. 63 | void fromPointList(const vector< cv::Point2i > &v); 64 | 65 | //! Number of points for this Shape 66 | int nPoints() const { return (rows >> 1); } 67 | 68 | //! Given a target size, find a proper transformation to rescale the shape. 69 | SimilarityTrans getShapeTransformFitingSize( 70 | const cv::Size &rect, 71 | double scaleRatio=0.9, double xOffset=0, double yOffset=0); 72 | }; 73 | } // Namespace 74 | #endif // SHAPEVEC_H 75 | -------------------------------------------------------------------------------- /src/lib/similaritytrans.cpp: -------------------------------------------------------------------------------- 1 | #include "similaritytrans.h" 2 | #include "shapevec.h" 3 | 4 | namespace ASMLib { 5 | void SimilarityTrans::invTransform(const ShapeVec &src, ShapeVec &dst) const{ 6 | int nP = src.nPoints(); 7 | double x11, x12, x21, x22; 8 | x11 = a/(a*a+b*b); 9 | x12 = b/(a*a+b*b); 10 | x21 = -b/(a*a+b*b); 11 | x22 = a/(a*a+b*b); 12 | 13 | dst.create(nP<<1, 1); 14 | double xt, yt; 15 | for (int i=0; i < nP; i++) { 16 | xt = src.X(i) - Xt; 17 | yt = src.Y(i) - Yt; 18 | dst.X(i) = x11 * xt + x12 * yt; 19 | dst.Y(i) = x21 * xt + x22 * yt; 20 | } 21 | } 22 | 23 | void SimilarityTrans::transform(const ShapeVec &src, ShapeVec &dst) const{ 24 | int nP = src.nPoints(); 25 | dst.create(nP<<1, 1); 26 | double xt, yt; 27 | for (int i=0; i < nP; i++) { 28 | xt = src.X(i); 29 | yt = src.Y(i); 30 | dst.X(i) = a * xt - b * yt + Xt; 31 | dst.Y(i) = b * xt + a * yt + Yt; 32 | } 33 | } 34 | 35 | void SimilarityTrans::setTransformByAlign(const ShapeVec &x, const ShapeVec &xp) { 36 | int nP = x.rows / 2; 37 | a = xp.dot(x) / x.dot(x); 38 | b = 0; 39 | for (int i=0; i M(2, 3); 52 | M<< a, -b, Xt, 53 | b, a, Yt; 54 | cv::warpAffine(imgSrc, imgDst, M, imgSrc.size(), cv::INTER_LINEAR); 55 | } 56 | 57 | void SimilarityTrans::warpImgBack(const cv::Mat& imgSrc, Mat& imgDst, bool useDstSize) const 58 | { 59 | 60 | Mat_< double > M(2, 3), mV; 61 | M<< a, -b, Xt, 62 | b, a, Yt; 63 | if (useDstSize) 64 | cv::warpAffine(imgSrc, imgDst, M, imgDst.size(), cv::INTER_LINEAR|cv::WARP_INVERSE_MAP); 65 | else 66 | cv::warpAffine(imgSrc, imgDst, M, imgSrc.size(), cv::INTER_LINEAR|cv::WARP_INVERSE_MAP); 67 | } 68 | } // Namespace -------------------------------------------------------------------------------- /src/lib/similaritytrans.h: -------------------------------------------------------------------------------- 1 | #ifndef SIMILARITYTRANS_H 2 | #define SIMILARITYTRANS_H 3 | 4 | #include "opencv2/core.hpp" 5 | #include "opencv2/imgproc.hpp" 6 | using cv::Mat; 7 | 8 | namespace ASMLib { 9 | 10 | class ShapeVec; 11 | 12 | //! A similarity transformation 13 | class SimilarityTrans{ 14 | public: 15 | //! Inverted transformation 16 | /*! \note src and dst can be the same vector 17 | */ 18 | void invTransform(const ShapeVec &src, ShapeVec &dst) const; 19 | 20 | //! Transform a shape 21 | /*! \note src and dst can be the same vector 22 | */ 23 | void transform(const ShapeVec &src, ShapeVec &dst) const; 24 | 25 | //! find the transformation that best align x to xp 26 | void setTransformByAlign(const ShapeVec &x, const ShapeVec &xp); 27 | 28 | //! Warp an image by this similarity transform. 29 | void warpImage(const Mat &imgSrc, Mat &imgDst) const; 30 | 31 | //! Warp an image by the inverted similarity transform. 32 | void warpImgBack(const Mat &imgSrc, Mat &imgDst, bool useDstSize=false) const; 33 | 34 | //! Get the scale factor 35 | double getS() const { return sqrt(a*a+b*b); } 36 | 37 | SimilarityTrans():Xt(0), Yt(0), a(1), b(0){} 38 | 39 | //! The multiplication of two transformations. 40 | SimilarityTrans operator *(const SimilarityTrans & s2) const { 41 | SimilarityTrans ns; 42 | ns.a = a*s2.a-b*s2.b; 43 | ns.b = s2.a*b+s2.b*a; 44 | ns.Xt = a*s2.Xt - b*s2.Yt + Xt; 45 | ns.Yt = b*s2.Xt + a*s2.Yt + Yt; 46 | return ns; 47 | } 48 | 49 | //! X translate 50 | double Xt; 51 | //! Y translate 52 | double Yt; 53 | 54 | //! a in similarity transformation matrix 55 | double a; 56 | //! b in similarity transformation matrix 57 | double b; 58 | }; 59 | 60 | } // Namespace 61 | #endif // SIMILARITYTRANS_H 62 | -------------------------------------------------------------------------------- /src/src.kdev4: -------------------------------------------------------------------------------- 1 | [Project] 2 | Manager=KDevCMakeManager 3 | Name=src 4 | --------------------------------------------------------------------------------