Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
use OCP\Share\Events\ShareCreatedEvent;
use OCP\Share\Events\ShareDeletedEvent;

class Application extends App implements IBootstrap {
final class Application extends App implements IBootstrap {
public const APP_ID = 'recognize';

public function __construct() {
Expand Down
2 changes: 1 addition & 1 deletion lib/BackgroundJobs/ClassifierJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ protected function runClassifier(string $model, array $argument): void {
abstract protected function getBatchSize(): int;

/**
* @param array $files
* @param list<OCA\Recognize\Db\QueueFile> $files
* @return void
* @throws \RuntimeException|\ErrorException
*/
Expand Down
2 changes: 1 addition & 1 deletion lib/BackgroundJobs/ClassifyFacesJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use OCP\BackgroundJob\IJobList;
use OCP\Files\Config\IUserMountCache;

class ClassifyFacesJob extends ClassifierJob {
final class ClassifyFacesJob extends ClassifierJob {
public const MODEL_NAME = 'faces';

private SettingsService $settingsService;
Expand Down
2 changes: 1 addition & 1 deletion lib/BackgroundJobs/ClassifyImagenetJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use OCP\BackgroundJob\IJobList;
use OCP\Files\Config\IUserMountCache;

class ClassifyImagenetJob extends ClassifierJob {
final class ClassifyImagenetJob extends ClassifierJob {
public const MODEL_NAME = 'imagenet';

private SettingsService $settingsService;
Expand Down
2 changes: 1 addition & 1 deletion lib/BackgroundJobs/ClassifyLandmarksJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use OCP\BackgroundJob\IJobList;
use OCP\Files\Config\IUserMountCache;

class ClassifyLandmarksJob extends ClassifierJob {
final class ClassifyLandmarksJob extends ClassifierJob {
public const MODEL_NAME = 'landmarks';

private SettingsService $settingsService;
Expand Down
2 changes: 1 addition & 1 deletion lib/BackgroundJobs/ClassifyMovinetJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use OCP\BackgroundJob\IJobList;
use OCP\Files\Config\IUserMountCache;

class ClassifyMovinetJob extends ClassifierJob {
final class ClassifyMovinetJob extends ClassifierJob {
public const MODEL_NAME = 'movinet';

private SettingsService $settingsService;
Expand Down
2 changes: 1 addition & 1 deletion lib/BackgroundJobs/ClassifyMusicnnJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use OCP\BackgroundJob\IJobList;
use OCP\Files\Config\IUserMountCache;

class ClassifyMusicnnJob extends ClassifierJob {
final class ClassifyMusicnnJob extends ClassifierJob {
public const MODEL_NAME = 'musicnn';

private SettingsService $settingsService;
Expand Down
2 changes: 1 addition & 1 deletion lib/BackgroundJobs/ClusterFacesJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
use OCP\DB\Exception;
use Psr\Log\LoggerInterface;

class ClusterFacesJob extends QueuedJob {
final class ClusterFacesJob extends QueuedJob {
private FaceClusterAnalyzer $clusterAnalyzer;
private IJobList $jobList;
private LoggerInterface $logger;
Expand Down
2 changes: 1 addition & 1 deletion lib/BackgroundJobs/MaintenanceJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use OCP\DB\Exception;
use Psr\Log\LoggerInterface;

class MaintenanceJob extends TimedJob {
final class MaintenanceJob extends TimedJob {

public function __construct(
ITimeFactory $time,
Expand Down
2 changes: 1 addition & 1 deletion lib/BackgroundJobs/SchedulerJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
use OCP\BackgroundJob\QueuedJob;
use Psr\Log\LoggerInterface;

class SchedulerJob extends QueuedJob {
final class SchedulerJob extends QueuedJob {
public const INTERVAL = 30 * 60; // 30 minutes
public const ALLOWED_MOUNT_TYPES = [
'OC\Files\Mount\LocalHomeMountProvider',
Expand Down
2 changes: 1 addition & 1 deletion lib/BackgroundJobs/StorageCrawlJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
use OCP\DB\Exception;
use Psr\Log\LoggerInterface;

class StorageCrawlJob extends QueuedJob {
final class StorageCrawlJob extends QueuedJob {
public const BATCH_SIZE = 2000;
private LoggerInterface $logger;
private QueueService $queue;
Expand Down
2 changes: 1 addition & 1 deletion lib/Classifiers/Audio/MusicnnClassifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
use OCP\IPreview;
use OCP\ITempManager;

class MusicnnClassifier extends Classifier {
final class MusicnnClassifier extends Classifier {
public const AUDIO_TIMEOUT = 40; // seconds
public const AUDIO_PUREJS_TIMEOUT = 420; // seconds
public const MODEL_NAME = 'musicnn';
Expand Down
48 changes: 33 additions & 15 deletions lib/Classifiers/Classifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,19 @@ public function classifyFiles(string $model, array $queueFiles, int $timeout): \
$path = $this->getConvertedFilePath($files[0]);
if (in_array($model, [ImagenetClassifier::MODEL_NAME, LandmarksClassifier::MODEL_NAME, ClusteringFaceClassifier::MODEL_NAME], true)) {
// Check file data size
$filesizeMb = filesize($path) / (1024 * 1024);
if ($filesizeMb > 8) {
$this->logger->debug('File is too large for classifier: ' . $files[0]->getPath());
try {
$this->logger->debug('removing ' . $queueFile->getFileId() . ' from ' . $model . ' queue');
$this->queue->removeFromQueue($model, $queueFile);
} catch (Exception $e) {
$this->logger->warning($e->getMessage(), ['exception' => $e]);
$filesize = filesize($path);
if ($filesize !== false) {
$filesizeMb = $filesize / (1024 * 1024);
if ($filesizeMb > 8) {
$this->logger->debug('File is too large for classifier: ' . $files[0]->getPath());
try {
$this->logger->debug('removing ' . $queueFile->getFileId() . ' from ' . $model . ' queue');
$this->queue->removeFromQueue($model, $queueFile);
} catch (Exception $e) {
$this->logger->warning($e->getMessage(), ['exception' => $e]);
}
continue;
}
continue;
}
// Check file dimensions
$dimensions = @getimagesize($path);
Expand Down Expand Up @@ -183,7 +186,7 @@ public function classifyFiles(string $model, array $queueFiles, int $timeout): \
$proc->start();

if ($cores !== '0') {
@exec('taskset -cp ' . implode(',', range(0, (int)$cores, 1)) . ' ' . $proc->getPid());
@exec('taskset -cp ' . implode(',', range(0, (int)$cores, 1)) . ' ' . ((string)$proc->getPid()));
}

$i = 0;
Expand Down Expand Up @@ -340,7 +343,14 @@ public function generatePreviewWithProvider(File $file): string {
$imagetype = exif_imagetype($tmpname);

if (in_array($imagetype, [IMAGETYPE_WEBP, IMAGETYPE_AVIF, false])) { // To troubleshoot if it is a webp or avif.
$previewImage = imagecreatefromstring(file_get_contents($tmpname));
$imageString = file_get_contents($tmpname);
if ($imageString === false) {
throw new \OCA\Recognize\Exception\Exception('Could not load preview file from temp folder');
}
$previewImage = imagecreatefromstring($imageString);
if ($previewImage === false) {
throw new \OCA\Recognize\Exception\Exception('Could not load preview file from temp folder');
}
$use_gd_quality = (int)\OCP\Server::get(IConfig::class)->getSystemValue('recognize.preview.quality', '100');
if (imagejpeg($previewImage, $tmpname, $use_gd_quality) === false) {
imagedestroy($previewImage);
Expand All @@ -358,18 +368,26 @@ public function generatePreviewWithProvider(File $file): string {
* @throws \OCA\Recognize\Exception\Exception
*/
public function generatePreviewWithGD(string $path): string {
$image = imagecreatefromstring(file_get_contents($path));
$imageContents = file_get_contents($path);
if (!$imageContents) {
throw new \OCA\Recognize\Exception\Exception('Could not load image for preview with gdlib');
}
$image = imagecreatefromstring($imageContents);
if (!$image) {
throw new \OCA\Recognize\Exception\Exception('Could not load image for preview with gdlib');
}
$width = imagesx($image);
$height = imagesy($image);

$maxWidth = self::TEMP_FILE_DIMENSION;
$maxHeight = self::TEMP_FILE_DIMENSION;
if ($width === false || $height === false) {
throw new \OCA\Recognize\Exception\Exception('Could not get image dimensions for preview with gdlib');
}

$maxWidth = (float) self::TEMP_FILE_DIMENSION;
$maxHeight = (float) self::TEMP_FILE_DIMENSION;

if ($width > $maxWidth || $height > $maxHeight) {
$aspectRatio = $width / $height;
$aspectRatio = (float) ($width / $height);
if ($width > $height) {
$newWidth = $maxWidth;
$newHeight = $maxWidth / $aspectRatio;
Expand Down
2 changes: 1 addition & 1 deletion lib/Classifiers/Images/ClusteringFaceClassifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
use OCP\ITempManager;
use OCP\Share\IManager;

class ClusteringFaceClassifier extends Classifier {
final class ClusteringFaceClassifier extends Classifier {
public const IMAGE_TIMEOUT = 120; // seconds
public const IMAGE_PUREJS_TIMEOUT = 360; // seconds
public const MIN_FACE_RECOGNITION_SCORE = 0.9;
Expand Down
2 changes: 1 addition & 1 deletion lib/Classifiers/Images/ImagenetClassifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
use OCP\IPreview;
use OCP\ITempManager;

class ImagenetClassifier extends Classifier {
final class ImagenetClassifier extends Classifier {
public const IMAGE_TIMEOUT = 480; // seconds
public const IMAGE_PUREJS_TIMEOUT = 600; // seconds
public const MODEL_NAME = 'imagenet';
Expand Down
2 changes: 1 addition & 1 deletion lib/Classifiers/Images/LandmarksClassifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
use OCP\IPreview;
use OCP\ITempManager;

class LandmarksClassifier extends Classifier {
final class LandmarksClassifier extends Classifier {
public const IMAGE_TIMEOUT = 480; // seconds
public const IMAGE_PUREJS_TIMEOUT = 600; // seconds
public const MODEL_NAME = 'landmarks';
Expand Down
2 changes: 1 addition & 1 deletion lib/Classifiers/Video/MovinetClassifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
use OCP\IPreview;
use OCP\ITempManager;

class MovinetClassifier extends Classifier {
final class MovinetClassifier extends Classifier {
public const VIDEO_TIMEOUT = 480; // seconds
public const MODEL_NAME = 'movinet';

Expand Down
2 changes: 1 addition & 1 deletion lib/Clustering/DualTreeBall.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
use \OCA\Recognize\Vendor\Rubix\ML\Kernels\Distance\Distance;
use function \OCA\Recognize\Vendor\Rubix\ML\argmax;

class DualTreeBall extends Ball {
final class DualTreeBall extends Ball {
protected float $longestDistanceInNode = INF;
protected bool $fullyConnected = false;
protected $setId;
Expand Down
9 changes: 6 additions & 3 deletions lib/Clustering/DualTreeClique.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
use \OCA\Recognize\Vendor\Rubix\ML\Kernels\Distance\Distance;
use function \OCA\Recognize\Vendor\Rubix\ML\argmax;

class DualTreeClique extends Clique {
final class DualTreeClique extends Clique {
protected float $longestDistanceInNode = INF;
protected bool $fullyConnected = false;
/**
Expand Down Expand Up @@ -63,8 +63,11 @@ public function propagateSetChanges(array &$labelToSetId) {
}

$labels = $this->dataset->labels();

$setId = $labelToSetId[array_pop($labels)];
$lastLabel = array_pop($labels);
if (!isset($labelToSetId[$lastLabel])) {
return null;
}
$setId = $labelToSetId[$lastLabel];

foreach ($labels as $label) {
if ($setId !== $labelToSetId[$label]) {
Expand Down
2 changes: 1 addition & 1 deletion lib/Clustering/HDBSCAN.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
*
* @author Sami Finnilä
*/
class HDBSCAN {
final class HDBSCAN {
/**
* The minimum number of samples that can be considered to form a cluster.
* Larger values will generate more stable clusters.
Expand Down
26 changes: 16 additions & 10 deletions lib/Clustering/MrdBallTree.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@
use \OCA\Recognize\Vendor\Rubix\ML\Graph\Trees\BallTree;
use \OCA\Recognize\Vendor\Rubix\ML\Kernels\Distance\Distance;

class MrdBallTree extends BallTree {
final class MrdBallTree extends BallTree {
private ?Labeled $dataset = null;
private array $nativeInterpointCache = [];
/**
* @var array<array-key,list<float>>
*/
private array $coreDistances = [];
/**
* @var array<array-key,list<float>>
*/
private array $coreNeighborDistances = [];
private int $sampleSize;
private array $nodeDistances;
Expand Down Expand Up @@ -122,7 +128,7 @@ public function getDataset(): Labeled {
* @param \OCA\Recognize\Clustering\DualTreeBall|\OCA\Recognize\Clustering\DualTreeClique $referenceNode
* @param int $k
* @param float $maxRange
* @param array $bestDistances
* @param array<array-key, float> $bestDistances
* @return void
*/
private function updateNearestNeighbors($queryNode, $referenceNode, $k, $maxRange, &$bestDistances): void {
Expand Down Expand Up @@ -157,7 +163,7 @@ private function updateNearestNeighbors($queryNode, $referenceNode, $k, $maxRang
if (count($coreNeighborDistances) >= $k) {
asort($coreNeighborDistances);
$coreNeighborDistances = array_slice($coreNeighborDistances, 0, $k, true);
$bestDistance = min(end($coreNeighborDistances), $maxRange);
$bestDistance = (float) min(end($coreNeighborDistances), $maxRange);
}
}
}
Expand All @@ -173,9 +179,9 @@ private function updateNearestNeighbors($queryNode, $referenceNode, $k, $maxRang
}

if ($this->kernel instanceof SquaredDistance) {
$longestDistance = min($longestDistance, (2 * sqrt($queryNode->radius()) + sqrt($shortestDistance)) ** 2);
$longestDistance = min($longestDistance, (2.0 * sqrt($queryNode->radius()) + sqrt($shortestDistance)) ** 2.0);
} else {
$longestDistance = min($longestDistance, 2 * $queryNode->radius() + $shortestDistance);
$longestDistance = (float) min($longestDistance, 2.0 * $queryNode->radius() + $shortestDistance);
}
$queryNode->setLongestDistance($longestDistance);
}
Expand Down Expand Up @@ -234,14 +240,14 @@ private function findNearestNeighbors($queryNode, $referenceNode, $k, $maxRange,
// TODO: min($longestLeft, $longestRight) + 2 * ($queryNode->radius()) <--- Can be made tighter by using the shortest distance from child.
if ($this->kernel instanceof SquaredDistance) {
$longestDistance = max($longestLeft, $longestRight);
$longestLeft = (sqrt($longestLeft) + 2 * (sqrt($queryNode->radius()) - sqrt($queryLeft->radius()))) ** 2;
$longestRight = (sqrt($longestRight) + 2 * (sqrt($queryNode->radius()) - sqrt($queryRight->radius()))) ** 2;
$longestDistance = min($longestDistance, min($longestLeft, $longestRight), (sqrt(min($longestLeft, $longestRight)) + 2 * (sqrt($queryNode->radius()))) ** 2);
$longestLeft = (sqrt($longestLeft) + 2.0 * (sqrt($queryNode->radius()) - sqrt($queryLeft->radius()))) ** 2.0;
$longestRight = (sqrt($longestRight) + 2.0 * (sqrt($queryNode->radius()) - sqrt($queryRight->radius()))) ** 2.0;
$longestDistance = (float) min($longestDistance, min($longestLeft, $longestRight), (sqrt(min($longestLeft, $longestRight)) + 2.0 * (sqrt($queryNode->radius()))) ** 2.0);
} else {
$longestDistance = max($longestLeft, $longestRight);
$longestLeft = $longestLeft + 2 * ($queryNode->radius() - $queryLeft->radius());
$longestRight = $longestRight + 2 * ($queryNode->radius() - $queryRight->radius());
$longestDistance = min($longestDistance, min($longestLeft, $longestRight), min($longestLeft, $longestRight) + 2 * ($queryNode->radius()));
$longestDistance = (float) min($longestDistance, min($longestLeft, $longestRight), min($longestLeft, $longestRight) + 2.0 * ($queryNode->radius()));
}

$queryNode->setLongestDistance($longestDistance);
Expand Down Expand Up @@ -573,7 +579,7 @@ public function cachedRange($sampleLabel, float $radius): array {
$childRadius = sqrt($child->radius());
$minDistance = $distance - $childRadius;
$minDistance = abs($minDistance) * $minDistance;
$maxDistance = ($distance + $childRadius) ** 2;
$maxDistance = ($distance + $childRadius) ** 2.0;
} else {
$childRadius = $child->radius();
$minDistance = $distance - $childRadius;
Expand Down
9 changes: 6 additions & 3 deletions lib/Clustering/MstClusterer.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
namespace OCA\Recognize\Clustering;

// TODO: store vertex lambda length (relative to cluster lambda length) for all vertices for improved soft clustering (see https://hdbscan.readthedocs.io/en/latest/soft_clustering.html)
class MstClusterer {
final class MstClusterer {
/**
* @var array<int, array{vertexFrom: int, vertexTo:int, distance:float, finalLambda?: float}>
*/
Expand Down Expand Up @@ -115,6 +115,9 @@ public function processCluster(): array {
}

$currentLongestEdgeKey = array_key_last($this->remainingEdges);
/**
* @var $currentLongestEdge array{vertexFrom: int, vertexTo:int, distance:float, finalLambda?: float}
*/
$currentLongestEdge = array_pop($this->remainingEdges);

$vertexConnectedFrom = $currentLongestEdge["vertexFrom"];
Expand All @@ -127,9 +130,9 @@ public function processCluster(): array {
if ($edgeLength > $this->maxEdgeLength) {
// Prevent formation of clusters with edges longer than the maximum edge length
// This is done by forcing the weight of the current cluster to zero
$lastLambda = $currentLambda = 1 / $edgeLength;
$lastLambda = $currentLambda = 1.0 / $edgeLength;
} elseif ($edgeLength > 0.0) {
$currentLambda = 1 / $edgeLength;
$currentLambda = 1.0 / $edgeLength;
}

$this->clusterWeight += ($currentLambda - $lastLambda) * $edgeCount;
Expand Down
Loading