Reformatted to match gallery core standards, removed a temp file
This commit is contained in:
parent
00cc49d7fe
commit
ae78daac3c
|
@ -1,22 +1,37 @@
|
||||||
<?php defined("SYSPATH") or die("No direct script access.");
|
<?php defined("SYSPATH") or die("No direct script access.");
|
||||||
|
/**
|
||||||
class picasa_faces_event_Core
|
* Gallery - a web based photo album viewer and editor
|
||||||
{
|
* Copyright (C) 2000-2010 Bharat Mediratta
|
||||||
static function module_change($changes)
|
*
|
||||||
{
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
class picasa_faces_event_Core {
|
||||||
|
static function module_change($changes) {
|
||||||
// See if the Photo Annotation module is installed,
|
// See if the Photo Annotation module is installed,
|
||||||
// tell the user to install it if it isn't.
|
// tell the user to install it if it isn't.
|
||||||
if (!module::is_active("photoannotation") || in_array("photoannotation", $changes->deactivate))
|
if (!module::is_active("photoannotation") || in_array("photoannotation", $changes->deactivate)) {
|
||||||
{
|
|
||||||
site_status::warning(
|
site_status::warning(
|
||||||
t("The Picasa Faces module requires the Photo Annotation module. " .
|
t("The Picasa Faces module requires the Photo Annotation module. " .
|
||||||
"<a href=\"%url\">Activate the Photo Annotation module now</a>",
|
"<a href=\"%url\">Activate the Photo Annotation module now</a>",
|
||||||
array("url" => url::site("admin/modules"))),
|
array("url" => url::site("admin/modules"))),
|
||||||
"picasa_faces_needs_photoannotation");
|
"picasa_faces_needs_photoannotation");
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
site_status::clear("picasa_faces_needs_photoannotation");
|
site_status::clear("picasa_faces_needs_photoannotation");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
|
@ -1,9 +1,24 @@
|
||||||
<?php defined("SYSPATH") or die("No direct script access.");
|
<?php defined("SYSPATH") or die("No direct script access.");
|
||||||
|
/**
|
||||||
class picasa_faces_installer
|
* Gallery - a web based photo album viewer and editor
|
||||||
{
|
* Copyright (C) 2000-2010 Bharat Mediratta
|
||||||
static function install()
|
*
|
||||||
{
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
class picasa_faces_installer {
|
||||||
|
static function install() {
|
||||||
// Create a table to store face mappings in.
|
// Create a table to store face mappings in.
|
||||||
$db = Database::instance();
|
$db = Database::instance();
|
||||||
|
|
||||||
|
@ -22,10 +37,8 @@ class picasa_faces_installer
|
||||||
module::set_version("picasa_faces", 2);
|
module::set_version("picasa_faces", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function upgrade($version)
|
static function upgrade($version) {
|
||||||
{
|
if ($version == 1) {
|
||||||
if ($version == 1)
|
|
||||||
{
|
|
||||||
Database::instance()->query(
|
Database::instance()->query(
|
||||||
"ALTER TABLE `picasa_faces` ADD `user_id` int(9) NOT NULL"
|
"ALTER TABLE `picasa_faces` ADD `user_id` int(9) NOT NULL"
|
||||||
);
|
);
|
||||||
|
@ -34,17 +47,17 @@ class picasa_faces_installer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static function deactivate()
|
static function deactivate() {
|
||||||
{
|
|
||||||
// Clear the require photo annototaion message when picasa faces is deactivated.
|
// Clear the require photo annototaion message when picasa faces is deactivated.
|
||||||
site_status::clear("picasa_faces_needs_photoannotation");
|
site_status::clear("picasa_faces_needs_photoannotation");
|
||||||
}
|
}
|
||||||
|
|
||||||
static function uninstall()
|
static function uninstall() {
|
||||||
{
|
|
||||||
// Delete the face mapping table before uninstalling.
|
// Delete the face mapping table before uninstalling.
|
||||||
$db = Database::instance();
|
$db = Database::instance();
|
||||||
$db->query("DROP TABLE IF EXISTS {picasa_faces};");
|
$db->query("DROP TABLE IF EXISTS {picasa_faces};");
|
||||||
module::delete("picasa_faces");
|
module::delete("picasa_faces");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
|
@ -1,9 +1,24 @@
|
||||||
<?php defined("SYSPATH") or die("No direct script access.");
|
<?php defined("SYSPATH") or die("No direct script access.");
|
||||||
|
/**
|
||||||
class picasa_faces_task_Core
|
* Gallery - a web based photo album viewer and editor
|
||||||
{
|
* Copyright (C) 2000-2010 Bharat Mediratta
|
||||||
static function available_tasks()
|
*
|
||||||
{
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
class picasa_faces_task_Core {
|
||||||
|
static function available_tasks() {
|
||||||
return array(Task_Definition::factory()
|
return array(Task_Definition::factory()
|
||||||
->callback("picasa_faces_task::import_faces")
|
->callback("picasa_faces_task::import_faces")
|
||||||
->name(t("Import faces from Picasa"))
|
->name(t("Import faces from Picasa"))
|
||||||
|
@ -11,10 +26,8 @@ class picasa_faces_task_Core
|
||||||
->severity(log::SUCCESS));
|
->severity(log::SUCCESS));
|
||||||
}
|
}
|
||||||
|
|
||||||
static function import_faces($task)
|
static function import_faces($task) {
|
||||||
{
|
if (!module::is_active("photoannotation")) {
|
||||||
if (!module::is_active("photoannotation"))
|
|
||||||
{
|
|
||||||
$task->done = true;
|
$task->done = true;
|
||||||
$task->status = t("Photo Annotation module is inactive, no faces will be imported");
|
$task->status = t("Photo Annotation module is inactive, no faces will be imported");
|
||||||
return;
|
return;
|
||||||
|
@ -25,8 +38,7 @@ class picasa_faces_task_Core
|
||||||
// Figure out the total number of albums in the database.
|
// Figure out the total number of albums in the database.
|
||||||
// If this is the first run, also set last_id and completed to 0.
|
// If this is the first run, also set last_id and completed to 0.
|
||||||
$total = $task->get("total");
|
$total = $task->get("total");
|
||||||
if (empty($total))
|
if (empty($total)) {
|
||||||
{
|
|
||||||
$task->set("total", $total = count(ORM::factory("item")->where("type", "=", "album")->find_all()));
|
$task->set("total", $total = count(ORM::factory("item")->where("type", "=", "album")->find_all()));
|
||||||
$task->set("last_id", 0);
|
$task->set("last_id", 0);
|
||||||
$task->set("completed", 0);
|
$task->set("completed", 0);
|
||||||
|
@ -41,8 +53,7 @@ class picasa_faces_task_Core
|
||||||
// Try to find a contacts.xml file, and parse out the contents if it exists
|
// Try to find a contacts.xml file, and parse out the contents if it exists
|
||||||
$contacts = null;
|
$contacts = null;
|
||||||
$contactsXML = VARPATH . "albums/contacts.xml";
|
$contactsXML = VARPATH . "albums/contacts.xml";
|
||||||
if (file_exists($contactsXML))
|
if (file_exists($contactsXML)) {
|
||||||
{
|
|
||||||
$xml = simplexml_load_file($contactsXML);
|
$xml = simplexml_load_file($contactsXML);
|
||||||
$contacts = $xml->contact;
|
$contacts = $xml->contact;
|
||||||
}
|
}
|
||||||
|
@ -52,38 +63,30 @@ class picasa_faces_task_Core
|
||||||
foreach (ORM::factory("item")
|
foreach (ORM::factory("item")
|
||||||
->where("id", ">", $last_id)
|
->where("id", ">", $last_id)
|
||||||
->where("type", "=", "album")
|
->where("type", "=", "album")
|
||||||
->find_all(100) as $albumItem)
|
->find_all(100) as $albumItem) {
|
||||||
{
|
|
||||||
$picasaFile = $albumItem->file_path()."/.picasa.ini";
|
$picasaFile = $albumItem->file_path()."/.picasa.ini";
|
||||||
if (file_exists($picasaFile))
|
if (file_exists($picasaFile)) {
|
||||||
{
|
|
||||||
// Parse the .picasa.ini file and extract any faces
|
// Parse the .picasa.ini file and extract any faces
|
||||||
$photosWithFaces = self::parsePicasaIni($picasaFile);
|
$photosWithFaces = self::parsePicasaIni($picasaFile);
|
||||||
|
|
||||||
// Build a mapping from photo filenames in this album to the items
|
// Build a mapping from photo filenames in this album to the items
|
||||||
$photos = array();
|
$photos = array();
|
||||||
foreach ($albumItem->children() as $child)
|
foreach ($albumItem->children() as $child) {
|
||||||
{
|
if ($child->is_photo()) {
|
||||||
if ($child->is_photo())
|
|
||||||
{
|
|
||||||
$photos[$child->name] = $child;
|
$photos[$child->name] = $child;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate through all the photos with faces in them
|
// Iterate through all the photos with faces in them
|
||||||
foreach ($photosWithFaces as $photoName => $faces)
|
foreach ($photosWithFaces as $photoName => $faces) {
|
||||||
{
|
|
||||||
// Find the item for this photo
|
// Find the item for this photo
|
||||||
$photoItem = $photos[$photoName];
|
$photoItem = $photos[$photoName];
|
||||||
if ($photoItem)
|
if ($photoItem) {
|
||||||
{
|
foreach ($faces as $faceId => $faceCoords) {
|
||||||
foreach ($faces as $faceId => $faceCoords)
|
|
||||||
{
|
|
||||||
$faceMapping = ORM::factory("picasa_face")->where("face_id", "=", $faceId)->find();
|
$faceMapping = ORM::factory("picasa_face")->where("face_id", "=", $faceId)->find();
|
||||||
|
|
||||||
// If we don't already have a mapping for this face, create one
|
// If we don't already have a mapping for this face, create one
|
||||||
if (!$faceMapping->loaded())
|
if (!$faceMapping->loaded()) {
|
||||||
{
|
|
||||||
$newTagId = self::getFaceMapping($faceId, $contacts);
|
$newTagId = self::getFaceMapping($faceId, $contacts);
|
||||||
|
|
||||||
// Save the mapping from Picasa face id to tag id, so
|
// Save the mapping from Picasa face id to tag id, so
|
||||||
|
@ -94,15 +97,13 @@ class picasa_faces_task_Core
|
||||||
$faceMapping->save();
|
$faceMapping->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($faceMapping->user_id == 0)
|
if ($faceMapping->user_id == 0) {
|
||||||
{
|
|
||||||
$numTagsOnPhoto = ORM::factory("items_face")
|
$numTagsOnPhoto = ORM::factory("items_face")
|
||||||
->where("tag_id", "=", $faceMapping->tag_id)
|
->where("tag_id", "=", $faceMapping->tag_id)
|
||||||
->where("item_id", "=", $photoItem->id)
|
->where("item_id", "=", $photoItem->id)
|
||||||
->count_all();
|
->count_all();
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
$numTagsOnPhoto = ORM::factory("items_user")
|
$numTagsOnPhoto = ORM::factory("items_user")
|
||||||
->where("user_id", "=", $faceMapping->user_id)
|
->where("user_id", "=", $faceMapping->user_id)
|
||||||
->where("item_id", "=", $photoItem->id)
|
->where("item_id", "=", $photoItem->id)
|
||||||
|
@ -111,13 +112,11 @@ class picasa_faces_task_Core
|
||||||
|
|
||||||
// If this face tag isn't already on this photo, add it (we
|
// If this face tag isn't already on this photo, add it (we
|
||||||
// assume a face should only ever appear once per photo)
|
// assume a face should only ever appear once per photo)
|
||||||
if ($numTagsOnPhoto == 0)
|
if ($numTagsOnPhoto == 0) {
|
||||||
{
|
|
||||||
self::addNewFace($faceMapping, $faceCoords, $photoItem);
|
self::addNewFace($faceMapping, $faceCoords, $photoItem);
|
||||||
$new_faces++;
|
$new_faces++;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
$old_faces++;
|
$old_faces++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,8 +127,7 @@ class picasa_faces_task_Core
|
||||||
$last_id = $albumItem->id;
|
$last_id = $albumItem->id;
|
||||||
$completed++;
|
$completed++;
|
||||||
|
|
||||||
if ($completed == $total || microtime(true) - $start > 1.5)
|
if ($completed == $total || microtime(true) - $start > 1.5) {
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,14 +137,12 @@ class picasa_faces_task_Core
|
||||||
$task->set("new_faces", $new_faces);
|
$task->set("new_faces", $new_faces);
|
||||||
$task->set("old_faces", $old_faces);
|
$task->set("old_faces", $old_faces);
|
||||||
|
|
||||||
if ($total == $completed)
|
if ($total == $completed) {
|
||||||
{
|
|
||||||
$task->done = true;
|
$task->done = true;
|
||||||
$task->state = "success";
|
$task->state = "success";
|
||||||
$task->percent_complete = 100;
|
$task->percent_complete = 100;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
$task->percent_complete = round(100 * $completed / $total);
|
$task->percent_complete = round(100 * $completed / $total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,23 +150,18 @@ class picasa_faces_task_Core
|
||||||
array("completed" => $completed, "total" => $total, "new_faces" => $new_faces, "old_faces" => $old_faces));
|
array("completed" => $completed, "total" => $total, "new_faces" => $new_faces, "old_faces" => $old_faces));
|
||||||
}
|
}
|
||||||
|
|
||||||
static function getFaceMapping($faceId, $contacts)
|
static function getFaceMapping($faceId, $contacts) {
|
||||||
{
|
|
||||||
$personTag = null;
|
$personTag = null;
|
||||||
|
|
||||||
// If we have a contacts.xml file, try to find the face id there
|
// If we have a contacts.xml file, try to find the face id there
|
||||||
if ($contacts != null)
|
if ($contacts != null) {
|
||||||
{
|
foreach ($contacts as $contact) {
|
||||||
foreach ($contacts as $contact)
|
if ($contact['id'] == $faceId) {
|
||||||
{
|
|
||||||
if ($contact['id'] == $faceId)
|
|
||||||
{
|
|
||||||
// We found this face id in the contacts.xml. See if a tag already exists.
|
// We found this face id in the contacts.xml. See if a tag already exists.
|
||||||
$personTag = ORM::factory("tag")->where("name", "=", $contact['name'])->find();
|
$personTag = ORM::factory("tag")->where("name", "=", $contact['name'])->find();
|
||||||
|
|
||||||
// If the tag doesn't exist already, add it
|
// If the tag doesn't exist already, add it
|
||||||
if (!$personTag->loaded())
|
if (!$personTag->loaded()) {
|
||||||
{
|
|
||||||
$personTag->name = $contact['name'];
|
$personTag->name = $contact['name'];
|
||||||
$personTag->save();
|
$personTag->save();
|
||||||
}
|
}
|
||||||
|
@ -182,13 +173,11 @@ class picasa_faces_task_Core
|
||||||
|
|
||||||
// We either didn't find the face in contacts.xml, or we don't have contacts.xml.
|
// We either didn't find the face in contacts.xml, or we don't have contacts.xml.
|
||||||
// Add the face using a generic name.
|
// Add the face using a generic name.
|
||||||
if ($personTag == null)
|
if ($personTag == null) {
|
||||||
{
|
|
||||||
// Find an unused "Picasa x" tag
|
// Find an unused "Picasa x" tag
|
||||||
$personID = 0;
|
$personID = 0;
|
||||||
$personName = "";
|
$personName = "";
|
||||||
do
|
do {
|
||||||
{
|
|
||||||
$personID++;
|
$personID++;
|
||||||
$personName = "Picasa ".$personID;
|
$personName = "Picasa ".$personID;
|
||||||
$personTag = ORM::factory("tag")->where("name", "=", $personName)->find();
|
$personTag = ORM::factory("tag")->where("name", "=", $personName)->find();
|
||||||
|
@ -202,8 +191,7 @@ class picasa_faces_task_Core
|
||||||
return $personTag->id;
|
return $personTag->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function addNewFace($faceMapping, $faceCoords, $photoItem)
|
static function addNewFace($faceMapping, $faceCoords, $photoItem) {
|
||||||
{
|
|
||||||
// Calculate the face coordinates. Picasa stores them as 0-65535, and we remap
|
// Calculate the face coordinates. Picasa stores them as 0-65535, and we remap
|
||||||
// that to the resize dimensions.
|
// that to the resize dimensions.
|
||||||
$left = (int)(($photoItem->resize_width * $faceCoords["left"]) / 65535);
|
$left = (int)(($photoItem->resize_width * $faceCoords["left"]) / 65535);
|
||||||
|
@ -211,8 +199,7 @@ class picasa_faces_task_Core
|
||||||
$right = (int)(($photoItem->resize_width * $faceCoords["right"]) / 65535);
|
$right = (int)(($photoItem->resize_width * $faceCoords["right"]) / 65535);
|
||||||
$bottom = (int)(($photoItem->resize_height * $faceCoords["bottom"]) / 65535);
|
$bottom = (int)(($photoItem->resize_height * $faceCoords["bottom"]) / 65535);
|
||||||
|
|
||||||
if ($faceMapping->user_id == 0)
|
if ($faceMapping->user_id == 0) {
|
||||||
{
|
|
||||||
// Add the photo to this tag
|
// Add the photo to this tag
|
||||||
$tag = ORM::factory("tag")->where("id", "=", $faceMapping->tag_id)->find();
|
$tag = ORM::factory("tag")->where("id", "=", $faceMapping->tag_id)->find();
|
||||||
$tag->add($photoItem);
|
$tag->add($photoItem);
|
||||||
|
@ -230,8 +217,7 @@ class picasa_faces_task_Core
|
||||||
$newFace->description = "";
|
$newFace->description = "";
|
||||||
$newFace->save();
|
$newFace->save();
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
// Add the face
|
// Add the face
|
||||||
$newFace = ORM::factory("items_user");
|
$newFace = ORM::factory("items_user");
|
||||||
$newFace->user_id = $faceMapping->user_id;
|
$newFace->user_id = $faceMapping->user_id;
|
||||||
|
@ -245,8 +231,7 @@ class picasa_faces_task_Core
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static function parsePicasaIni($filePath)
|
static function parsePicasaIni($filePath) {
|
||||||
{
|
|
||||||
// It would be nice to use parse_ini_file here, but the parens used with the rect64 values break it
|
// It would be nice to use parse_ini_file here, but the parens used with the rect64 values break it
|
||||||
$ini_lines = file($filePath);
|
$ini_lines = file($filePath);
|
||||||
|
|
||||||
|
@ -254,30 +239,25 @@ class picasa_faces_task_Core
|
||||||
|
|
||||||
$photosWithFaces = array();
|
$photosWithFaces = array();
|
||||||
|
|
||||||
foreach ($ini_lines as $ini_line)
|
foreach ($ini_lines as $ini_line) {
|
||||||
{
|
|
||||||
// Trim off any whitespace at the ends
|
// Trim off any whitespace at the ends
|
||||||
$ini_line = trim($ini_line);
|
$ini_line = trim($ini_line);
|
||||||
|
|
||||||
if ($ini_line[0] == '[')
|
if ($ini_line[0] == '[') {
|
||||||
{
|
|
||||||
// If this line starts with [ it's a filename, strip off the brackets
|
// If this line starts with [ it's a filename, strip off the brackets
|
||||||
$curFilename = substr($ini_line, 1, -1);
|
$curFilename = substr($ini_line, 1, -1);
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
// If this isn't a filename, it must be data for a file, get the key/value pair
|
// If this isn't a filename, it must be data for a file, get the key/value pair
|
||||||
$photoData = explode("=", $ini_line);
|
$photoData = explode("=", $ini_line);
|
||||||
|
|
||||||
if ($photoData[0] == "faces")
|
if ($photoData[0] == "faces") {
|
||||||
{
|
|
||||||
// If it's face data, break it up by face
|
// If it's face data, break it up by face
|
||||||
$faces = explode(";", $photoData[1]);
|
$faces = explode(";", $photoData[1]);
|
||||||
|
|
||||||
$photoFaces = array();
|
$photoFaces = array();
|
||||||
|
|
||||||
foreach ($faces as $face)
|
foreach ($faces as $face) {
|
||||||
{
|
|
||||||
// Split a face into the rectangle and face id
|
// Split a face into the rectangle and face id
|
||||||
$splitface = explode(",", $face);
|
$splitface = explode(",", $face);
|
||||||
|
|
||||||
|
|
|
@ -1,313 +0,0 @@
|
||||||
<?php defined("SYSPATH") or die("No direct script access.");
|
|
||||||
|
|
||||||
class picasa_faces_task_Core
|
|
||||||
{
|
|
||||||
static function available_tasks()
|
|
||||||
{
|
|
||||||
return array(Task_Definition::factory()
|
|
||||||
->callback("picasa_faces_task::import_faces")
|
|
||||||
->name(t("Import faces from Picasa"))
|
|
||||||
->description(t("Scan all albums for Picasa face data and add any faces that don't already exist"))
|
|
||||||
->severity(log::SUCCESS));
|
|
||||||
}
|
|
||||||
|
|
||||||
static function import_faces($task)
|
|
||||||
{
|
|
||||||
if (!module::is_active("photoannotation"))
|
|
||||||
{
|
|
||||||
$task->done = true;
|
|
||||||
$task->status = t("Photo Annotation module is inactive, no faces will be imported");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$start = microtime(true);
|
|
||||||
|
|
||||||
// Figure out the total number of albums in the database.
|
|
||||||
// If this is the first run, also set last_id and completed to 0.
|
|
||||||
$total = $task->get("total");
|
|
||||||
if (empty($total))
|
|
||||||
{
|
|
||||||
$task->set("total", $total = count(ORM::factory("item")->where("type", "=", "album")->find_all()));
|
|
||||||
$task->set("last_id", 0);
|
|
||||||
$task->set("completed", 0);
|
|
||||||
$task->set("new_faces", 0);
|
|
||||||
$task->set("old_faces", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
$last_id = $task->get("last_id");
|
|
||||||
$completed = $task->get("completed");
|
|
||||||
$new_faces = $task->get("new_faces");
|
|
||||||
$old_faces = $task->get("old_faces");
|
|
||||||
|
|
||||||
// Try to find a contacts.xml file, and parse out the contents if it exists
|
|
||||||
$contacts = null;
|
|
||||||
$contactsXML = VARPATH . "albums/contacts.xml";
|
|
||||||
if (file_exists($contactsXML))
|
|
||||||
{
|
|
||||||
$xml = simplexml_load_file($contactsXML);
|
|
||||||
$contacts = $xml->contact;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check each album in the database to see if it has a .picasa.ini file on disk,
|
|
||||||
// and extract any faces if it does.
|
|
||||||
foreach (ORM::factory("item")
|
|
||||||
->where("id", ">", $last_id)
|
|
||||||
->where("type", "=", "album")
|
|
||||||
->find_all(100) as $albumItem)
|
|
||||||
{
|
|
||||||
$picasaFile = $albumItem->file_path()."/.picasa.ini";
|
|
||||||
if (file_exists($picasaFile))
|
|
||||||
{
|
|
||||||
// Parse the .picasa.ini file and extract any faces
|
|
||||||
$photosWithFaces = self::parsePicasaIni($picasaFile);
|
|
||||||
|
|
||||||
// Build a mapping from photo filenames in this album to the items
|
|
||||||
$photos = array();
|
|
||||||
foreach ($albumItem->children() as $child)
|
|
||||||
{
|
|
||||||
if ($child->is_photo())
|
|
||||||
{
|
|
||||||
$photos[$child->name] = $child;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through all the photos with faces in them
|
|
||||||
foreach ($photosWithFaces as $photoName => $faces)
|
|
||||||
{
|
|
||||||
// Find the item for this photo
|
|
||||||
$photoItem = $photos[$photoName];
|
|
||||||
if ($photoItem)
|
|
||||||
{
|
|
||||||
foreach ($faces as $faceId => $faceCoords)
|
|
||||||
{
|
|
||||||
$faceMapping = ORM::factory("picasa_face")->where("face_id", "=", $faceId)->find();
|
|
||||||
|
|
||||||
// If we don't already have a mapping for this face, create one
|
|
||||||
if (!$faceMapping->loaded())
|
|
||||||
{
|
|
||||||
$newTagId = self::getFaceMapping($faceId, $contacts);
|
|
||||||
|
|
||||||
// Save the mapping from Picasa face id to tag id, so
|
|
||||||
// faces will be grouped properly
|
|
||||||
$faceMapping->face_id = $faceId;
|
|
||||||
$faceMapping->tag_id = $newTagId;
|
|
||||||
$faceMapping->user_id = 0;
|
|
||||||
$faceMapping->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($faceMapping->user_id == 0)
|
|
||||||
{
|
|
||||||
$numTagsOnPhoto = ORM::factory("items_face")
|
|
||||||
->where("tag_id", "=", $faceMapping->tag_id)
|
|
||||||
->where("item_id", "=", $photoItem->id)
|
|
||||||
->count_all();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$numTagsOnPhoto = ORM::factory("items_user")
|
|
||||||
->where("user_id", "=", $faceMapping->user_id)
|
|
||||||
->where("item_id", "=", $photoItem->id)
|
|
||||||
->count_all();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this face tag isn't already on this photo, add it (we
|
|
||||||
// assume a face should only ever appear once per photo)
|
|
||||||
if ($numTagsOnPhoto == 0)
|
|
||||||
{
|
|
||||||
self::addNewFace($faceMapping, $faceCoords, $photoItem);
|
|
||||||
$new_faces++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$old_faces++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$last_id = $albumItem->id;
|
|
||||||
$completed++;
|
|
||||||
|
|
||||||
if ($completed == $total || microtime(true) - $start > 1.5)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$task->set("completed", $completed);
|
|
||||||
$task->set("last_id", $last_id);
|
|
||||||
$task->set("new_faces", $new_faces);
|
|
||||||
$task->set("old_faces", $old_faces);
|
|
||||||
|
|
||||||
if ($total == $completed)
|
|
||||||
{
|
|
||||||
$task->done = true;
|
|
||||||
$task->state = "success";
|
|
||||||
$task->percent_complete = 100;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$task->percent_complete = round(100 * $completed / $total);
|
|
||||||
}
|
|
||||||
|
|
||||||
$task->status = t("%completed / %total albums scanned, %new_faces faces added, %old_faces faces unchanged",
|
|
||||||
array("completed" => $completed, "total" => $total, "new_faces" => $new_faces, "old_faces" => $old_faces));
|
|
||||||
}
|
|
||||||
|
|
||||||
static function getFaceMapping($faceId, $contacts)
|
|
||||||
{
|
|
||||||
$personTag = null;
|
|
||||||
|
|
||||||
// If we have a contacts.xml file, try to find the face id there
|
|
||||||
if ($contacts != null)
|
|
||||||
{
|
|
||||||
foreach ($contacts as $contact)
|
|
||||||
{
|
|
||||||
if ($contact['id'] == $faceId)
|
|
||||||
{
|
|
||||||
// We found this face id in the contacts.xml. See if a tag already exists.
|
|
||||||
$personTag = ORM::factory("tag")->where("name", "=", $contact['name'])->find();
|
|
||||||
|
|
||||||
// If the tag doesn't exist already, add it
|
|
||||||
if (!$personTag->loaded())
|
|
||||||
{
|
|
||||||
$personTag->name = $contact['name'];
|
|
||||||
$personTag->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We either didn't find the face in contacts.xml, or we don't have contacts.xml.
|
|
||||||
// Add the face using a generic name.
|
|
||||||
if ($personTag == null)
|
|
||||||
{
|
|
||||||
// Find an unused "Picasa x" tag
|
|
||||||
$personID = 0;
|
|
||||||
$personName = "";
|
|
||||||
do
|
|
||||||
{
|
|
||||||
$personID++;
|
|
||||||
$personName = "Picasa ".$personID;
|
|
||||||
$personTag = ORM::factory("tag")->where("name", "=", $personName)->find();
|
|
||||||
} while ($personTag->loaded());
|
|
||||||
|
|
||||||
// We found an open name, save it so we can get the id
|
|
||||||
$personTag->name = $personName;
|
|
||||||
$personTag->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $personTag->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static function addNewFace($faceMapping, $faceCoords, $photoItem)
|
|
||||||
{
|
|
||||||
// Calculate the face coordinates. Picasa stores them as 0-65535, and we remap
|
|
||||||
// that to the resize dimensions.
|
|
||||||
$left = (int)(($photoItem->resize_width * $faceCoords["left"]) / 65535);
|
|
||||||
$top = (int)(($photoItem->resize_height * $faceCoords["top"]) / 65535);
|
|
||||||
$right = (int)(($photoItem->resize_width * $faceCoords["right"]) / 65535);
|
|
||||||
$bottom = (int)(($photoItem->resize_height * $faceCoords["bottom"]) / 65535);
|
|
||||||
|
|
||||||
if ($faceMapping->user_id == 0)
|
|
||||||
{
|
|
||||||
// Add the photo to this tag
|
|
||||||
$tag = ORM::factory("tag")->where("id", "=", $faceMapping->tag_id)->find();
|
|
||||||
$tag->add($photoItem);
|
|
||||||
$tag->count++;
|
|
||||||
$tag->save();
|
|
||||||
|
|
||||||
// Add the face
|
|
||||||
$newFace = ORM::factory("items_face");
|
|
||||||
$newFace->tag_id = $faceMapping->tag_id;
|
|
||||||
$newFace->item_id = $photoItem->id;
|
|
||||||
$newFace->x1 = $left;
|
|
||||||
$newFace->y1 = $top;
|
|
||||||
$newFace->x2 = $right;
|
|
||||||
$newFace->y2 = $bottom;
|
|
||||||
$newFace->description = "";
|
|
||||||
$newFace->save();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Add the face
|
|
||||||
$newFace = ORM::factory("items_user");
|
|
||||||
$newFace->user_id = $faceMapping->user_id;
|
|
||||||
$newFace->item_id = $photoItem->id;
|
|
||||||
$newFace->x1 = $left;
|
|
||||||
$newFace->y1 = $top;
|
|
||||||
$newFace->x2 = $right;
|
|
||||||
$newFace->y2 = $bottom;
|
|
||||||
$newFace->description = "";
|
|
||||||
$newFace->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static function parsePicasaIni($filePath)
|
|
||||||
{
|
|
||||||
// It would be nice to use parse_ini_file here, but the parens used with the rect64 values break it
|
|
||||||
$ini_lines = file($filePath);
|
|
||||||
|
|
||||||
$curFilename = "";
|
|
||||||
|
|
||||||
$photosWithFaces = array();
|
|
||||||
|
|
||||||
foreach ($ini_lines as $ini_line)
|
|
||||||
{
|
|
||||||
// Trim off any whitespace at the ends
|
|
||||||
$ini_line = trim($ini_line);
|
|
||||||
|
|
||||||
if ($ini_line[0] == '[')
|
|
||||||
{
|
|
||||||
// If this line starts with [ it's a filename, strip off the brackets
|
|
||||||
$curFilename = substr($ini_line, 1, -1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If this isn't a filename, it must be data for a file, get the key/value pair
|
|
||||||
$photoData = explode("=", $ini_line);
|
|
||||||
|
|
||||||
if ($photoData[0] == "faces")
|
|
||||||
{
|
|
||||||
// If it's face data, break it up by face
|
|
||||||
$faces = explode(";", $photoData[1]);
|
|
||||||
|
|
||||||
$photoFaces = array();
|
|
||||||
|
|
||||||
foreach ($faces as $face)
|
|
||||||
{
|
|
||||||
// Split a face into the rectangle and face id
|
|
||||||
$splitface = explode(",", $face);
|
|
||||||
|
|
||||||
$hexrect = substr($splitface[0], 7, -1);
|
|
||||||
// We need a string with 16 chars. Fill up with zeros from left.
|
|
||||||
$hexrect = str_pad($hexrect, 16, "0", STR_PAD_LEFT);
|
|
||||||
$person = $splitface[1];
|
|
||||||
|
|
||||||
// The rectangle is 4 4-character hex values
|
|
||||||
$left = hexdec(substr($hexrect,0,4));
|
|
||||||
$top = hexdec(substr($hexrect,4,4));
|
|
||||||
$right = hexdec(substr($hexrect,8,4));
|
|
||||||
$bottom = hexdec(substr($hexrect,12,4));
|
|
||||||
|
|
||||||
$facePos = array("left" => $left,
|
|
||||||
"top" => $top,
|
|
||||||
"right" => $right,
|
|
||||||
"bottom" => $bottom);
|
|
||||||
|
|
||||||
$photoFaces[$person] = $facePos;
|
|
||||||
}
|
|
||||||
|
|
||||||
$photosWithFaces[$curFilename] = $photoFaces;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $photosWithFaces;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,5 +1,22 @@
|
||||||
<?php defined("SYSPATH") or die("No direct script access.");
|
<?php defined("SYSPATH") or die("No direct script access.");
|
||||||
class Picasa_Face_Model extends ORM
|
/**
|
||||||
{
|
* Gallery - a web based photo album viewer and editor
|
||||||
|
* Copyright (C) 2000-2010 Bharat Mediratta
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
class Picasa_Face_Model extends ORM {
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
Reference in New Issue
Block a user