wkdhkr/dedupper

View on GitHub
src/services/db/FacePPDbService.js

Summary

Maintainability
F
6 days
Test Coverage
/* eslint-disable no-plusplus */
/* eslint-disable camelcase */
// @flow
import typeof { Logger } from "log4js";
import { STATE_ACCEPTED, STATE_KEEPING } from "../../types/FileStates";
import SQLiteService from "./SQLiteService";
import { TYPE_IMAGE } from "../../types/ClassifyTypes";
import DeepLearningHelper from "../../helpers/DeepLearningHelper";
import DbHelper from "../../helpers/DbHelper";
import type { Database } from "./SQLiteService";
import type {
  FacePPCoordinate,
  FacePPLandmark,
  FacePPResult,
  FacePPFace
} from "../../types/DeepLearningTypes";
import type { Config, FileInfo, FacePPRow } from "../../types";

export default class FacePPDbService {
  log: Logger;

  config: Config;

  ss: SQLiteService;

  constructor(config: Config) {
    this.log = config.getLogger(this);
    this.config = config;
    this.ss = new SQLiteService(config);
  }

  isInsertNeedless: (fileInfo: FileInfo) => Promise<boolean> = async (
    fileInfo: FileInfo
  ): Promise<boolean> => {
    if ([STATE_ACCEPTED, STATE_KEEPING].includes(fileInfo.state)) {
      const hitRows = await this.queryByHash(fileInfo);
      if (hitRows.length) {
        return true;
      }
      if (
        DeepLearningHelper.getFacePPResult(fileInfo.hash) ||
        fileInfo.facePP
      ) {
        return false;
      }
    }
    return true;
  };

  prepareTable: (db: Database<FacePPRow>) => Promise<void> = async (
    db: Database<FacePPRow>
  ) =>
    this.ss.prepareTable(
      db,
      this.config.deepLearningConfig.facePPDbCreateTableSql,
      this.config.deepLearningConfig.facePPDbCreateIndexSqls
    );

  createValueMap: (
    face: FacePPFace,
    $version: number,
    $hash: string,
    $iamge_id: string,
    $face_num: number
  ) => any = (
    face: FacePPFace,
    $version: number,
    $hash: string,
    $image_id: string,
    $face_num: number
  ) => {
    const a = face.attributes;
    const obj = {
      $landmark: FacePPDbService.encodeLandmark(face.landmark),
      $hash,
      $image_id,
      $face_token: face.face_token,
      $face_num,
      $version,
      $emotion_sadness: a.emotion.sadness,
      $emotion_neutral: a.emotion.neutral,
      $emotion_disgust: a.emotion.disgust,
      $emotion_anger: a.emotion.anger,
      $emotion_surprise: a.emotion.surprise,
      $emotion_fear: a.emotion.fear,
      $emotion_happiness: a.emotion.happiness,
      $beauty_female_score: a.beauty.female_score,
      $beauty_male_score: a.beauty.male_score,
      $gender: a.gender.value,
      $age: a.age.value,
      $mouth_close: a.mouthstatus.close,
      $mouth_surgical_mask_or_respirator:
        a.mouthstatus.surgical_mask_or_respirator,
      $mouth_open: a.mouthstatus.open,
      $mouth_other_occlusion: a.mouthstatus.other_occlusion,
      $glass: a.glass.value,
      $skin_dark_circle: a.skinstatus.dark_circle,
      $skin_stain: a.skinstatus.stain,
      $skin_acne: a.skinstatus.acne,
      $skin_health: a.skinstatus.health,
      $headpose_yaw_angle: a.headpose.yaw_angle,
      $headpose_pitch_angle: a.headpose.pitch_angle,
      $headpose_roll_angle: a.headpose.roll_angle,
      $gaussianblur: a.blur.gaussianblur.value,
      $motionblur: a.blur.motionblur.value,
      $blurness: a.blur.blurness.value,
      $smile: a.smile.value,
      $eye_status_left_normal_glass_eye_open:
        a.eyestatus.left_eye_status.normal_glass_eye_open,
      $eye_status_left_normal_glass_eye_close:
        a.eyestatus.left_eye_status.normal_glass_eye_close,
      $eye_status_left_no_glass_eye_close:
        a.eyestatus.left_eye_status.no_glass_eye_close,
      $eye_status_left_no_glass_eye_open:
        a.eyestatus.left_eye_status.no_glass_eye_open,
      $eye_status_left_occlusion: a.eyestatus.left_eye_status.occlusion,
      $eye_status_left_dark_glasses: a.eyestatus.left_eye_status.dark_glasses,
      $eye_status_right_normal_glass_eye_open:
        a.eyestatus.right_eye_status.normal_glass_eye_open,
      $eye_status_right_normal_glass_eye_close:
        a.eyestatus.right_eye_status.normal_glass_eye_close,
      $eye_status_right_no_glass_eye_close:
        a.eyestatus.right_eye_status.no_glass_eye_close,
      $eye_status_right_no_glass_eye_open:
        a.eyestatus.right_eye_status.no_glass_eye_open,
      $eye_status_right_occlusion: a.eyestatus.right_eye_status.occlusion,
      $eye_status_right_dark_glasses: a.eyestatus.right_eye_status.dark_glasses,
      $eyegaze_right_position_x_coordinate:
        a.eyegaze.right_eye_gaze.position_x_coordinate,
      $eyegaze_right_position_y_coordinate:
        a.eyegaze.right_eye_gaze.position_y_coordinate,
      $eyegaze_right_vector_z: a.eyegaze.right_eye_gaze.vector_z_component,
      $eyegaze_right_vector_x: a.eyegaze.right_eye_gaze.vector_x_component,
      $eyegaze_right_vector_y: a.eyegaze.right_eye_gaze.vector_y_component,
      $eyegaze_left_position_x_coordinate:
        a.eyegaze.left_eye_gaze.position_x_coordinate,
      $eyegaze_left_position_y_coordinate:
        a.eyegaze.left_eye_gaze.position_y_coordinate,
      $eyegaze_left_vector_z: a.eyegaze.left_eye_gaze.vector_z_component,
      $eyegaze_left_vector_x: a.eyegaze.left_eye_gaze.vector_x_component,
      $eyegaze_left_vector_y: a.eyegaze.left_eye_gaze.vector_y_component,
      $facequality: a.facequality.value,
      $ethnicity: a.ethnicity.value,
      $top: face.face_rectangle.top,
      $left: face.face_rectangle.left,
      $width: face.face_rectangle.width,
      $height: face.face_rectangle.height
    };
    return obj;
  };

  createRowsFromFileInfo: (fileInfo: FileInfo) => Array<any> = (
    fileInfo: FileInfo
  ) => {
    const $hash = fileInfo.hash;
    const $version = this.config.deepLearningConfig.facePPDbVersion;
    const facePPResult =
      DeepLearningHelper.pullFacePPResult(fileInfo.hash) ||
      (fileInfo.facePP || {}).result;
    if (!facePPResult) {
      throw new Error("no face++ result");
    }
    const rows = [];
    facePPResult.faces.forEach((face: FacePPFace) => {
      rows.push(
        this.createValueMap(
          face,
          $version,
          $hash,
          facePPResult.image_id,
          facePPResult.face_num
        )
      );
    });
    return rows;
  };

  deleteByHash({ hash: $hash }: FileInfo): Promise<?FacePPRow> {
    return new Promise((resolve, reject) => {
      if (!$hash) {
        resolve();
        return;
      }
      const db = this.ss.spawn(this.ss.detectDbFilePath(TYPE_IMAGE));
      db.serialize(async () => {
        try {
          await this.prepareTable(db);
          if (!this.config.dryrun) {
            await DbHelper.beginSafe(db);
            db.run(
              `delete from ${this.config.deepLearningConfig.facePPDbTableName} where hash = $hash`,
              { $hash },
              err => {
                if (err) {
                  db.close();
                  reject(err);
                  return;
                }
                DbHelper.commitSafe(db, () => {
                  db.close();
                  resolve();
                });
              }
            );
          }
        } catch (e) {
          reject(e);
        }
      });
    });
  }

  queryByHash({ hash: $hash }: FileInfo): Promise<FacePPRow[]> {
    return new Promise((resolve, reject) => {
      if (!$hash) {
        resolve([]);
        return;
      }
      const db = this.spawnDb();
      db.serialize(async () => {
        await this.prepareTable(db);
        try {
          db.all(
            `select * from ${this.config.deepLearningConfig.facePPDbTableName} where hash = $hash`,
            { $hash },
            (err, rows: FacePPRow[]) => {
              db.close();
              if (!this.ss.handleEachError(db, err, reject)) {
                return;
              }
              resolve(rows);
            }
          );
        } catch (e) {
          reject(e);
        }
      });
    });
  }

  all: () => Promise<Array<FacePPRow>> = (): Promise<FacePPRow[]> =>
    new Promise((resolve, reject) => {
      const db = this.spawnDb();
      db.serialize(async () => {
        try {
          await this.prepareTable(db);
          db.all(
            `select * from ${this.config.deepLearningConfig.facePPDbTableName}`,
            {},
            (err, rows: FacePPRow[]) => {
              db.close();
              if (err) {
                reject(err);
                return;
              }
              resolve(rows);
            }
          );
        } catch (e) {
          reject(e);
        }
      });
    });

  spawnDb: () => Database<FacePPRow> = (): Database<FacePPRow> =>
    this.ss.spawn(this.ss.detectDbFilePath(TYPE_IMAGE));

  queryByValue(column: string, $value: number | string): Promise<FacePPRow[]> {
    return new Promise((resolve, reject) => {
      const db = this.spawnDb();
      const rows = [];
      db.serialize(async () => {
        try {
          await this.prepareTable(db);
          db.each(
            `select * from ${this.config.deepLearningConfig.facePPDbTableName} where ${column} = $value`,
            { $value },
            (err, row: FacePPRow) => {
              this.ss.handleEachError<FacePPRow>(db, err, reject, row, rows);
            },
            err => {
              db.close();
              if (err) {
                reject(err);
                return;
              }
              resolve(rows);
            }
          );
        } catch (e) {
          reject(e);
        }
      });
    });
  }

  static decodeCoordinate: (s: string) => FacePPCoordinate = (
    s: string
  ): FacePPCoordinate => {
    const [x, y] = s
      .split(FacePPDbService.SPLITTER_COORDINATE)
      .map(p => parseInt(p, 10));
    return { x, y };
  };

  static validateLandmark: (l?: FacePPLandmark) => void = (
    l?: FacePPLandmark
  ) => {
    if (l) {
      if (Object.keys(l).length === 83) {
        return;
      }
    }

    throw new Error("invalid face++ landmark");
  };

  static encodeLandmark: (l: FacePPLandmark) => string = (
    l: FacePPLandmark
  ) => {
    FacePPDbService.validateLandmark(l);
    const s = FacePPDbService.SPLITTER_COORDINATE;
    return [
      [l.contour_chin.x, l.contour_chin.y].join(s),
      [l.contour_left1.x, l.contour_left1.y].join(s),
      [l.contour_left2.x, l.contour_left2.y].join(s),
      [l.contour_left3.x, l.contour_left3.y].join(s),
      [l.contour_left4.x, l.contour_left4.y].join(s),
      [l.contour_left5.x, l.contour_left5.y].join(s),
      [l.contour_left6.x, l.contour_left6.y].join(s),
      [l.contour_left7.x, l.contour_left7.y].join(s),
      [l.contour_left8.x, l.contour_left8.y].join(s),
      [l.contour_left9.x, l.contour_left9.y].join(s),
      [l.contour_right1.x, l.contour_right1.y].join(s),
      [l.contour_right2.x, l.contour_right2.y].join(s),
      [l.contour_right3.x, l.contour_right3.y].join(s),
      [l.contour_right4.x, l.contour_right4.y].join(s),
      [l.contour_right5.x, l.contour_right5.y].join(s),
      [l.contour_right6.x, l.contour_right6.y].join(s),
      [l.contour_right7.x, l.contour_right7.y].join(s),
      [l.contour_right8.x, l.contour_right8.y].join(s),
      [l.contour_right9.x, l.contour_right9.y].join(s),
      [l.left_eye_bottom.x, l.left_eye_bottom.y].join(s),
      [l.left_eye_center.x, l.left_eye_center.y].join(s),
      [l.left_eye_left_corner.x, l.left_eye_left_corner.y].join(s),
      [l.left_eye_lower_left_quarter.x, l.left_eye_lower_left_quarter.y].join(
        s
      ),
      [l.left_eye_lower_right_quarter.x, l.left_eye_lower_right_quarter.y].join(
        s
      ),
      [l.left_eye_pupil.x, l.left_eye_pupil.y].join(s),
      [l.left_eye_right_corner.x, l.left_eye_right_corner.y].join(s),
      [l.left_eye_top.x, l.left_eye_top.y].join(s),
      [l.left_eye_upper_left_quarter.x, l.left_eye_upper_left_quarter.y].join(
        s
      ),
      [l.left_eye_upper_right_quarter.x, l.left_eye_upper_right_quarter.y].join(
        s
      ),
      [l.left_eyebrow_left_corner.x, l.left_eyebrow_left_corner.y].join(s),
      [
        l.left_eyebrow_lower_left_quarter.x,
        l.left_eyebrow_lower_left_quarter.y
      ].join(s),
      [l.left_eyebrow_lower_middle.x, l.left_eyebrow_lower_middle.y].join(s),
      [
        l.left_eyebrow_lower_right_quarter.x,
        l.left_eyebrow_lower_right_quarter.y
      ].join(s),
      [l.left_eyebrow_right_corner.x, l.left_eyebrow_right_corner.y].join(s),
      [
        l.left_eyebrow_upper_left_quarter.x,
        l.left_eyebrow_upper_left_quarter.y
      ].join(s),
      [l.left_eyebrow_upper_middle.x, l.left_eyebrow_upper_middle.y].join(s),
      [
        l.left_eyebrow_upper_right_quarter.x,
        l.left_eyebrow_upper_right_quarter.y
      ].join(s),
      [l.mouth_left_corner.x, l.mouth_left_corner.y].join(s),
      [l.mouth_lower_lip_bottom.x, l.mouth_lower_lip_bottom.y].join(s),
      [
        l.mouth_lower_lip_left_contour1.x,
        l.mouth_lower_lip_left_contour1.y
      ].join(s),
      [
        l.mouth_lower_lip_left_contour2.x,
        l.mouth_lower_lip_left_contour2.y
      ].join(s),
      [
        l.mouth_lower_lip_left_contour3.x,
        l.mouth_lower_lip_left_contour3.y
      ].join(s),
      [
        l.mouth_lower_lip_right_contour1.x,
        l.mouth_lower_lip_right_contour1.y
      ].join(s),
      [
        l.mouth_lower_lip_right_contour2.x,
        l.mouth_lower_lip_right_contour2.y
      ].join(s),
      [
        l.mouth_lower_lip_right_contour3.x,
        l.mouth_lower_lip_right_contour3.y
      ].join(s),
      [l.mouth_lower_lip_top.x, l.mouth_lower_lip_top.y].join(s),
      [l.mouth_right_corner.x, l.mouth_right_corner.y].join(s),
      [l.mouth_upper_lip_bottom.x, l.mouth_upper_lip_bottom.y].join(s),
      [
        l.mouth_upper_lip_left_contour1.x,
        l.mouth_upper_lip_left_contour1.y
      ].join(s),
      [
        l.mouth_upper_lip_left_contour2.x,
        l.mouth_upper_lip_left_contour2.y
      ].join(s),
      [
        l.mouth_upper_lip_left_contour3.x,
        l.mouth_upper_lip_left_contour3.y
      ].join(s),
      [
        l.mouth_upper_lip_right_contour1.x,
        l.mouth_upper_lip_right_contour1.y
      ].join(s),
      [
        l.mouth_upper_lip_right_contour2.x,
        l.mouth_upper_lip_right_contour2.y
      ].join(s),
      [
        l.mouth_upper_lip_right_contour3.x,
        l.mouth_upper_lip_right_contour3.y
      ].join(s),
      [l.mouth_upper_lip_top.x, l.mouth_upper_lip_top.y].join(s),
      [l.nose_contour_left1.x, l.nose_contour_left1.y].join(s),
      [l.nose_contour_left2.x, l.nose_contour_left2.y].join(s),
      [l.nose_contour_left3.x, l.nose_contour_left3.y].join(s),
      [l.nose_contour_lower_middle.x, l.nose_contour_lower_middle.y].join(s),
      [l.nose_contour_right1.x, l.nose_contour_right1.y].join(s),
      [l.nose_contour_right2.x, l.nose_contour_right2.y].join(s),
      [l.nose_contour_right3.x, l.nose_contour_right3.y].join(s),
      [l.nose_left.x, l.nose_left.y].join(s),
      [l.nose_right.x, l.nose_right.y].join(s),
      [l.nose_tip.x, l.nose_tip.y].join(s),
      [l.right_eye_bottom.x, l.right_eye_bottom.y].join(s),
      [l.right_eye_center.x, l.right_eye_center.y].join(s),
      [l.right_eye_left_corner.x, l.right_eye_left_corner.y].join(s),
      [l.right_eye_lower_left_quarter.x, l.right_eye_lower_left_quarter.y].join(
        s
      ),
      [
        l.right_eye_lower_right_quarter.x,
        l.right_eye_lower_right_quarter.y
      ].join(s),
      [l.right_eye_pupil.x, l.right_eye_pupil.y].join(s),
      [l.right_eye_right_corner.x, l.right_eye_right_corner.y].join(s),
      [l.right_eye_top.x, l.right_eye_top.y].join(s),
      [l.right_eye_upper_left_quarter.x, l.right_eye_upper_left_quarter.y].join(
        s
      ),
      [
        l.right_eye_upper_right_quarter.x,
        l.right_eye_upper_right_quarter.y
      ].join(s),
      [l.right_eyebrow_left_corner.x, l.right_eyebrow_left_corner.y].join(s),
      [
        l.right_eyebrow_lower_left_quarter.x,
        l.right_eyebrow_lower_left_quarter.y
      ].join(s),
      [l.right_eyebrow_lower_middle.x, l.right_eyebrow_lower_middle.y].join(s),
      [
        l.right_eyebrow_lower_right_quarter.x,
        l.right_eyebrow_lower_right_quarter.y
      ].join(s),
      [l.right_eyebrow_right_corner.x, l.right_eyebrow_right_corner.y].join(s),
      [
        l.right_eyebrow_upper_left_quarter.x,
        l.right_eyebrow_upper_left_quarter.y
      ].join(s),
      [l.right_eyebrow_upper_middle.x, l.right_eyebrow_upper_middle.y].join(s),
      [
        l.right_eyebrow_upper_right_quarter.x,
        l.right_eyebrow_upper_right_quarter.y
      ].join(s)
    ].join(FacePPDbService.SPLITTER_LANDMARK);
  };

  static SPLITTER_LANDMARK: string = ";";

  static SPLITTER_COORDINATE: string = ",";

  static decodeLandmark: (rawLandmark: string) => FacePPLandmark = (
    rawLandmark: string
  ): FacePPLandmark => {
    const l = rawLandmark.split(FacePPDbService.SPLITTER_LANDMARK);
    let p = 0;
    return {
      contour_chin: FacePPDbService.decodeCoordinate(l[p++]),
      contour_left1: FacePPDbService.decodeCoordinate(l[p++]),
      contour_left2: FacePPDbService.decodeCoordinate(l[p++]),
      contour_left3: FacePPDbService.decodeCoordinate(l[p++]),
      contour_left4: FacePPDbService.decodeCoordinate(l[p++]),
      contour_left5: FacePPDbService.decodeCoordinate(l[p++]),
      contour_left6: FacePPDbService.decodeCoordinate(l[p++]),
      contour_left7: FacePPDbService.decodeCoordinate(l[p++]),
      contour_left8: FacePPDbService.decodeCoordinate(l[p++]),
      contour_left9: FacePPDbService.decodeCoordinate(l[p++]),
      contour_right1: FacePPDbService.decodeCoordinate(l[p++]),
      contour_right2: FacePPDbService.decodeCoordinate(l[p++]),
      contour_right3: FacePPDbService.decodeCoordinate(l[p++]),
      contour_right4: FacePPDbService.decodeCoordinate(l[p++]),
      contour_right5: FacePPDbService.decodeCoordinate(l[p++]),
      contour_right6: FacePPDbService.decodeCoordinate(l[p++]),
      contour_right7: FacePPDbService.decodeCoordinate(l[p++]),
      contour_right8: FacePPDbService.decodeCoordinate(l[p++]),
      contour_right9: FacePPDbService.decodeCoordinate(l[p++]),
      left_eye_bottom: FacePPDbService.decodeCoordinate(l[p++]),
      left_eye_center: FacePPDbService.decodeCoordinate(l[p++]),
      left_eye_left_corner: FacePPDbService.decodeCoordinate(l[p++]),
      left_eye_lower_left_quarter: FacePPDbService.decodeCoordinate(l[p++]),
      left_eye_lower_right_quarter: FacePPDbService.decodeCoordinate(l[p++]),
      left_eye_pupil: FacePPDbService.decodeCoordinate(l[p++]),
      left_eye_right_corner: FacePPDbService.decodeCoordinate(l[p++]),
      left_eye_top: FacePPDbService.decodeCoordinate(l[p++]),
      left_eye_upper_left_quarter: FacePPDbService.decodeCoordinate(l[p++]),
      left_eye_upper_right_quarter: FacePPDbService.decodeCoordinate(l[p++]),
      left_eyebrow_left_corner: FacePPDbService.decodeCoordinate(l[p++]),
      left_eyebrow_lower_left_quarter: FacePPDbService.decodeCoordinate(l[p++]),
      left_eyebrow_lower_middle: FacePPDbService.decodeCoordinate(l[p++]),
      left_eyebrow_lower_right_quarter: FacePPDbService.decodeCoordinate(
        l[p++]
      ),
      left_eyebrow_right_corner: FacePPDbService.decodeCoordinate(l[p++]),
      left_eyebrow_upper_left_quarter: FacePPDbService.decodeCoordinate(l[p++]),
      left_eyebrow_upper_middle: FacePPDbService.decodeCoordinate(l[p++]),
      left_eyebrow_upper_right_quarter: FacePPDbService.decodeCoordinate(
        l[p++]
      ),
      mouth_left_corner: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_lower_lip_bottom: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_lower_lip_left_contour1: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_lower_lip_left_contour2: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_lower_lip_left_contour3: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_lower_lip_right_contour1: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_lower_lip_right_contour2: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_lower_lip_right_contour3: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_lower_lip_top: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_right_corner: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_upper_lip_bottom: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_upper_lip_left_contour1: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_upper_lip_left_contour2: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_upper_lip_left_contour3: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_upper_lip_right_contour1: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_upper_lip_right_contour2: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_upper_lip_right_contour3: FacePPDbService.decodeCoordinate(l[p++]),
      mouth_upper_lip_top: FacePPDbService.decodeCoordinate(l[p++]),
      nose_contour_left1: FacePPDbService.decodeCoordinate(l[p++]),
      nose_contour_left2: FacePPDbService.decodeCoordinate(l[p++]),
      nose_contour_left3: FacePPDbService.decodeCoordinate(l[p++]),
      nose_contour_lower_middle: FacePPDbService.decodeCoordinate(l[p++]),
      nose_contour_right1: FacePPDbService.decodeCoordinate(l[p++]),
      nose_contour_right2: FacePPDbService.decodeCoordinate(l[p++]),
      nose_contour_right3: FacePPDbService.decodeCoordinate(l[p++]),
      nose_left: FacePPDbService.decodeCoordinate(l[p++]),
      nose_right: FacePPDbService.decodeCoordinate(l[p++]),
      nose_tip: FacePPDbService.decodeCoordinate(l[p++]),
      right_eye_bottom: FacePPDbService.decodeCoordinate(l[p++]),
      right_eye_center: FacePPDbService.decodeCoordinate(l[p++]),
      right_eye_left_corner: FacePPDbService.decodeCoordinate(l[p++]),
      right_eye_lower_left_quarter: FacePPDbService.decodeCoordinate(l[p++]),
      right_eye_lower_right_quarter: FacePPDbService.decodeCoordinate(l[p++]),
      right_eye_pupil: FacePPDbService.decodeCoordinate(l[p++]),
      right_eye_right_corner: FacePPDbService.decodeCoordinate(l[p++]),
      right_eye_top: FacePPDbService.decodeCoordinate(l[p++]),
      right_eye_upper_left_quarter: FacePPDbService.decodeCoordinate(l[p++]),
      right_eye_upper_right_quarter: FacePPDbService.decodeCoordinate(l[p++]),
      right_eyebrow_left_corner: FacePPDbService.decodeCoordinate(l[p++]),
      right_eyebrow_lower_left_quarter: FacePPDbService.decodeCoordinate(
        l[p++]
      ),
      right_eyebrow_lower_middle: FacePPDbService.decodeCoordinate(l[p++]),
      right_eyebrow_lower_right_quarter: FacePPDbService.decodeCoordinate(
        l[p++]
      ),
      right_eyebrow_right_corner: FacePPDbService.decodeCoordinate(l[p++]),
      right_eyebrow_upper_left_quarter: FacePPDbService.decodeCoordinate(
        l[p++]
      ),
      right_eyebrow_upper_middle: FacePPDbService.decodeCoordinate(l[p++]),
      right_eyebrow_upper_right_quarter: FacePPDbService.decodeCoordinate(
        l[p++]
      )
    };
  };

  static rowToResult: (rows: Array<FacePPRow>) => ?FacePPResult = (
    rows: FacePPRow[]
  ): ?FacePPResult => {
    if (!rows.length) {
      return null;
    }
    return {
      time_used: 0,
      request_id: "",
      image_id: rows[0].image_id,
      face_num: rows[0].face_num,
      faces: rows.map(row => ({
        landmark: FacePPDbService.decodeLandmark(row.landmark),
        attributes: {
          emotion: {
            sadness: row.emotion_sadness,
            neutral: row.emotion_neutral,
            disgust: row.emotion_disgust,
            anger: row.emotion_anger,
            surprise: row.emotion_surprise,
            fear: row.emotion_fear,
            happiness: row.emotion_happiness
          },
          beauty: {
            female_score: row.beauty_female_score,
            male_score: row.beauty_male_score
          },
          gender: {
            value: row.gender
          },
          age: {
            value: row.age
          },
          mouthstatus: {
            close: row.mouth_close,
            surgical_mask_or_respirator: row.mouth_surgical_mask_or_respirator,
            open: row.mouth_open,
            other_occlusion: row.mouth_other_occlusion
          },
          glass: {
            value: row.glass
          },
          skinstatus: {
            dark_circle: row.skin_dark_circle,
            stain: row.skin_stain,
            acne: row.skin_acne,
            health: row.skin_health
          },
          headpose: {
            yaw_angle: row.headpose_yaw_angle,
            pitch_angle: row.headpose_pitch_angle,
            roll_angle: row.headpose_roll_angle
          },
          blur: {
            blurness: {
              threshold: 50,
              value: row.blurness
            },
            motionblur: {
              threshold: 50,
              value: row.motionblur
            },
            gaussianblur: {
              threshold: 50,
              value: row.gaussianblur
            }
          },
          smile: {
            threshold: 50,
            value: row.smile
          },
          eyestatus: {
            left_eye_status: {
              normal_glass_eye_open: row.eye_status_left_normal_glass_eye_open,
              no_glass_eye_close: row.eye_status_left_normal_glass_eye_close,
              occlusion: row.eye_status_left_occlusion,
              no_glass_eye_open: row.eye_status_left_no_glass_eye_open,
              normal_glass_eye_close:
                row.eye_status_left_normal_glass_eye_close,
              dark_glasses: row.eye_status_left_dark_glasses
            },
            right_eye_status: {
              normal_glass_eye_open: row.eye_status_right_normal_glass_eye_open,
              no_glass_eye_close: row.eye_status_right_normal_glass_eye_close,
              occlusion: row.eye_status_right_occlusion,
              no_glass_eye_open: row.eye_status_right_no_glass_eye_open,
              normal_glass_eye_close:
                row.eye_status_right_normal_glass_eye_close,
              dark_glasses: row.eye_status_right_dark_glasses
            }
          },
          facequality: {
            threshold: 70.1,
            value: row.facequality
          },
          ethnicity: {
            value: row.ethnicity
          },
          eyegaze: {
            right_eye_gaze: {
              position_x_coordinate: row.eyegaze_right_position_x_coordinate,
              vector_z_component: row.eyegaze_right_vector_z,
              vector_x_component: row.eyegaze_right_vector_x,
              vector_y_component: row.eyegaze_right_vector_y,
              position_y_coordinate: row.eyegaze_right_position_y_coordinate
            },
            left_eye_gaze: {
              position_x_coordinate: row.eyegaze_left_position_x_coordinate,
              vector_z_component: row.eyegaze_left_vector_z,
              vector_x_component: row.eyegaze_left_vector_x,
              vector_y_component: row.eyegaze_left_vector_y,
              position_y_coordinate: row.eyegaze_left_position_y_coordinate
            }
          }
        },
        face_rectangle: {
          width: row.width,
          top: row.top,
          left: row.left,
          height: row.height
        },
        face_token: row.face_token
      }))
    };
  };

  insert: (
    fileInfo: FileInfo,
    isReplace?: boolean,
    force?: boolean
  ) => Promise<void> = async (
    fileInfo: FileInfo,
    isReplace: boolean = true,
    force: boolean = false
  ) => {
    const isInsertNeedless = force
      ? false
      : await this.isInsertNeedless(fileInfo);
    if (isInsertNeedless) {
      return;
    }
    const rows = this.createRowsFromFileInfo(fileInfo);
    const db = this.spawnDb();
    // prevent sqlite conflict
    for (const row of rows) {
      // eslint-disable-next-line no-await-in-loop
      await new Promise((resolve, reject) => {
        db.serialize(async () => {
          try {
            await this.prepareTable(db);
            // this.log.info(`insert: row = ${JSON.stringify(row)}`);
            if (!this.config.dryrun) {
              const columns = [
                "hash",
                "image_id",
                "face_token",
                "face_num",
                "landmark",
                "version",
                "emotion_sadness",
                "emotion_neutral",
                "emotion_disgust",
                "emotion_anger",
                "emotion_surprise",
                "emotion_fear",
                "emotion_happiness",
                "beauty_female_score",
                "beauty_male_score",
                "gender",
                "age",
                "mouth_close",
                "mouth_surgical_mask_or_respirator",
                "mouth_open",
                "mouth_other_occlusion",
                "glass",
                "skin_dark_circle",
                "skin_stain",
                "skin_acne",
                "skin_health",
                "headpose_yaw_angle",
                "headpose_pitch_angle",
                "headpose_roll_angle",
                "gaussianblur",
                "motionblur",
                "blurness",
                "smile",
                "eye_status_left_normal_glass_eye_open",
                "eye_status_left_normal_glass_eye_close",
                "eye_status_left_no_glass_eye_close",
                "eye_status_left_no_glass_eye_open",
                "eye_status_left_occlusion",
                "eye_status_left_dark_glasses",
                "eye_status_right_normal_glass_eye_open",
                "eye_status_right_normal_glass_eye_close",
                "eye_status_right_no_glass_eye_close",
                "eye_status_right_no_glass_eye_open",
                "eye_status_right_occlusion",
                "eye_status_right_dark_glasses",
                "eyegaze_right_position_x_coordinate",
                "eyegaze_right_position_y_coordinate",
                "eyegaze_right_vector_z",
                "eyegaze_right_vector_x",
                "eyegaze_right_vector_y",
                "eyegaze_left_position_x_coordinate",
                "eyegaze_left_position_y_coordinate",
                "eyegaze_left_vector_z",
                "eyegaze_left_vector_x",
                "eyegaze_left_vector_y",
                "facequality",
                "ethnicity",
                "top",
                "left",
                "width",
                "height"
              ].join(",");
              const values = [
                "$hash",
                "$image_id",
                "$face_token",
                "$face_num",
                "$landmark",
                "$version",
                "$emotion_sadness",
                "$emotion_neutral",
                "$emotion_disgust",
                "$emotion_anger",
                "$emotion_surprise",
                "$emotion_fear",
                "$emotion_happiness",
                "$beauty_female_score",
                "$beauty_male_score",
                "$gender",
                "$age",
                "$mouth_close",
                "$mouth_surgical_mask_or_respirator",
                "$mouth_open",
                "$mouth_other_occlusion",
                "$glass",
                "$skin_dark_circle",
                "$skin_stain",
                "$skin_acne",
                "$skin_health",
                "$headpose_yaw_angle",
                "$headpose_pitch_angle",
                "$headpose_roll_angle",
                "$gaussianblur",
                "$motionblur",
                "$blurness",
                "$smile",
                "$eye_status_left_normal_glass_eye_open",
                "$eye_status_left_normal_glass_eye_close",
                "$eye_status_left_no_glass_eye_close",
                "$eye_status_left_no_glass_eye_open",
                "$eye_status_left_occlusion",
                "$eye_status_left_dark_glasses",
                "$eye_status_right_normal_glass_eye_open",
                "$eye_status_right_normal_glass_eye_close",
                "$eye_status_right_no_glass_eye_close",
                "$eye_status_right_no_glass_eye_open",
                "$eye_status_right_occlusion",
                "$eye_status_right_dark_glasses",
                "$eyegaze_right_position_x_coordinate",
                "$eyegaze_right_position_y_coordinate",
                "$eyegaze_right_vector_z",
                "$eyegaze_right_vector_x",
                "$eyegaze_right_vector_y",
                "$eyegaze_left_position_x_coordinate",
                "$eyegaze_left_position_y_coordinate",
                "$eyegaze_left_vector_z",
                "$eyegaze_left_vector_x",
                "$eyegaze_left_vector_y",
                "$facequality",
                "$ethnicity",
                "$top",
                "$left",
                "$width",
                "$height"
              ].join(",");

              const replaceStatement = isReplace ? " or replace" : "";
              await DbHelper.beginSafe(db);
              db.run(
                `insert${replaceStatement} into ${this.config.deepLearningConfig.facePPDbTableName} (${columns}) values (${values})`,
                row,
                err => {
                  if (err) {
                    db.close();
                    reject(err);
                    return;
                  }
                  DbHelper.commitSafe(db, () => {
                    db.close();
                    resolve();
                  });
                }
              );
            } else {
              resolve();
            }
          } catch (e) {
            reject(e);
          }
        });
      });
    }
  };
}