<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ChRainG</title>
    <description>The latest articles on DEV Community by ChRainG (@chraing).</description>
    <link>https://dev.to/chraing</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4142753%2Fe121aaa4-7d10-4a79-893c-7aadcd09e5e6.png</url>
      <title>DEV Community: ChRainG</title>
      <link>https://dev.to/chraing</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chraing"/>
    <language>en</language>
    <item>
      <title>I Built a Photo Management SaaS with FastAPI, Qdrant, CLIP and Automatic Duplicate Detection</title>
      <dc:creator>ChRainG</dc:creator>
      <pubDate>Fri, 25 Sep 2026 11:03:50 +0000</pubDate>
      <link>https://dev.to/chraing/i-built-a-photo-management-saas-with-fastapi-qdrant-clip-and-automatic-duplicate-detection-3ggm</link>
      <guid>https://dev.to/chraing/i-built-a-photo-management-saas-with-fastapi-qdrant-clip-and-automatic-duplicate-detection-3ggm</guid>
      <description>&lt;p&gt;Managing a few hundred photos is easy.&lt;/p&gt;

&lt;p&gt;Managing 20,000, 50,000 or 100,000 photos is a different problem.&lt;/p&gt;

&lt;p&gt;At some point you stop remembering where a photo is stored, whether you already have another copy of it, or why there are 15 almost identical images from the same moment.&lt;/p&gt;

&lt;p&gt;That problem is what led me to build &lt;strong&gt;Renvumi&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Renvumi is a web service for analyzing and organizing large photo libraries.&lt;/p&gt;

&lt;p&gt;It is available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://renvumi.ru/" rel="noopener noreferrer"&gt;https://renvumi.ru/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The interface is now available in both English and Russian.&lt;/p&gt;

&lt;h2&gt;
  
  
  The original problem
&lt;/h2&gt;

&lt;p&gt;The first version was supposed to do one thing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;find duplicate photos&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Exact duplicates are relatively easy.&lt;/p&gt;

&lt;p&gt;If two files have the same content, a cryptographic hash can identify them.&lt;/p&gt;

&lt;p&gt;But real photo libraries are much messier.&lt;/p&gt;

&lt;p&gt;You often have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;resized copies&lt;/li&gt;
&lt;li&gt;recompressed JPEGs&lt;/li&gt;
&lt;li&gt;edited versions&lt;/li&gt;
&lt;li&gt;burst shots&lt;/li&gt;
&lt;li&gt;nearly identical frames&lt;/li&gt;
&lt;li&gt;the same photo stored in several albums&lt;/li&gt;
&lt;li&gt;files with identical names but completely different contents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So exact hashing solved only a small part of the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exact duplicates
&lt;/h2&gt;

&lt;p&gt;For exact duplicates, Renvumi uses file hashes.&lt;/p&gt;

&lt;p&gt;This is useful when several backup drives or old folders have been merged together.&lt;/p&gt;

&lt;p&gt;If two files have identical content, they can be grouped immediately without expensive AI processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Near duplicates
&lt;/h2&gt;

&lt;p&gt;Near duplicates are more interesting.&lt;/p&gt;

&lt;p&gt;Two files can look almost identical but have different binary contents.&lt;/p&gt;

&lt;p&gt;For this case I use perceptual hashing.&lt;/p&gt;

&lt;p&gt;This works well for things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;resized images&lt;/li&gt;
&lt;li&gt;recompressed images&lt;/li&gt;
&lt;li&gt;small edits&lt;/li&gt;
&lt;li&gt;slightly modified copies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But perceptual hashes are not enough for broader semantic similarity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Similar image search with CLIP
&lt;/h2&gt;

&lt;p&gt;For more flexible similarity search, Renvumi uses CLIP embeddings.&lt;/p&gt;

&lt;p&gt;Each analyzed image gets a vector representation.&lt;/p&gt;

&lt;p&gt;Those vectors are stored in &lt;strong&gt;Qdrant&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This makes it possible to search for visually related photos even when they are not near duplicates.&lt;/p&gt;

&lt;p&gt;For example, you can select one photo and search for similar photos across the entire library.&lt;/p&gt;

&lt;p&gt;That can work for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the same car photographed at different times&lt;/li&gt;
&lt;li&gt;the same building&lt;/li&gt;
&lt;li&gt;similar landscapes&lt;/li&gt;
&lt;li&gt;related scenes&lt;/li&gt;
&lt;li&gt;different photos from the same event&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Renvumi uses absolute CLIP cosine similarity for this type of search.&lt;/p&gt;

&lt;p&gt;Perceptual hashes are kept for near-duplicate detection.&lt;/p&gt;

&lt;p&gt;I intentionally separated these two concepts because they solve different problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Text search
&lt;/h2&gt;

&lt;p&gt;CLIP also makes text-to-image search possible.&lt;/p&gt;

&lt;p&gt;Instead of remembering filenames, you can search for something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dog on the beach
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;red car near a house
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system converts the query into the same embedding space and searches the existing image vectors.&lt;/p&gt;

&lt;p&gt;This is one of the features that makes a large photo archive feel less like a filesystem and more like a searchable database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Global search across albums
&lt;/h2&gt;

&lt;p&gt;Originally, every operation was scoped to one album.&lt;/p&gt;

&lt;p&gt;That quickly became a limitation.&lt;/p&gt;

&lt;p&gt;Imagine this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Phone Backup/
    IMG_1001.JPG

Old HDD/
    IMG_1001.JPG

Vacation/
    copy_IMG_1001.JPG
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same image may exist in several places.&lt;/p&gt;

&lt;p&gt;So Renvumi now has a &lt;strong&gt;Global&lt;/strong&gt; mode.&lt;/p&gt;

&lt;p&gt;It aggregates results from all analyzed albums.&lt;/p&gt;

&lt;p&gt;From there users can work with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;exact duplicates&lt;/li&gt;
&lt;li&gt;near duplicates&lt;/li&gt;
&lt;li&gt;similar photos&lt;/li&gt;
&lt;li&gt;unique photos&lt;/li&gt;
&lt;li&gt;people&lt;/li&gt;
&lt;li&gt;best shots&lt;/li&gt;
&lt;li&gt;cleanup candidates&lt;/li&gt;
&lt;li&gt;filename conflicts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The original album name is still shown for every result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Same filename, different file
&lt;/h2&gt;

&lt;p&gt;This turned out to be a surprisingly useful feature.&lt;/p&gt;

&lt;p&gt;Cameras and phones reuse filenames over time.&lt;/p&gt;

&lt;p&gt;After several years, a library may contain many files called:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IMG_0001.JPG
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But they may all be different photos.&lt;/p&gt;

&lt;p&gt;Renvumi detects this separately.&lt;/p&gt;

&lt;p&gt;The rule is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;same filename + different SHA-256 = filename conflict
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If both filename and content are identical, it is treated as an ordinary exact duplicate instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the best shots in a series
&lt;/h2&gt;

&lt;p&gt;Another common problem is burst photography.&lt;/p&gt;

&lt;p&gt;You may have 10 or 20 images taken within a few seconds.&lt;/p&gt;

&lt;p&gt;They are all different files, but from a user's perspective they form one series.&lt;/p&gt;

&lt;p&gt;Renvumi groups related shots and helps identify stronger candidates.&lt;/p&gt;

&lt;p&gt;It can use signals such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sharpness&lt;/li&gt;
&lt;li&gt;face detection&lt;/li&gt;
&lt;li&gt;closed-eye detection&lt;/li&gt;
&lt;li&gt;visual similarity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But I deliberately do not let the software automatically decide what the user must delete.&lt;/p&gt;

&lt;p&gt;A technically imperfect photo can still be the best photo emotionally.&lt;/p&gt;

&lt;p&gt;So the system narrows the set.&lt;/p&gt;

&lt;p&gt;The user makes the final decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Face detection and people grouping
&lt;/h2&gt;

&lt;p&gt;For people detection I use OpenCV components including YuNet and face recognition logic.&lt;/p&gt;

&lt;p&gt;The pipeline also contains additional checks to reduce obvious false positives.&lt;/p&gt;

&lt;p&gt;The goal is to group images of the same person across an album and then aggregate those results globally.&lt;/p&gt;

&lt;p&gt;This part turned out to require much more tuning than I initially expected.&lt;/p&gt;

&lt;p&gt;Face detection itself is easy.&lt;/p&gt;

&lt;p&gt;Reliable grouping across thousands of real photos is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatic analysis of new uploads
&lt;/h2&gt;

&lt;p&gt;One workflow decision changed recently.&lt;/p&gt;

&lt;p&gt;Previously the process was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;upload photos
click Analyze
wait
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That felt unnecessary.&lt;/p&gt;

&lt;p&gt;A photo analysis service without analysis is not very useful.&lt;/p&gt;

&lt;p&gt;Now the workflow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;upload photos
analysis starts automatically
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there is an important optimization.&lt;/p&gt;

&lt;p&gt;Only &lt;strong&gt;new, not-yet-analyzed photos&lt;/strong&gt; are processed.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Day 1:
Upload 5,000 photos
Analyze 5,000 photos

Day 7:
Upload 100 photos
Analyze only those 100 photos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The existing 5,000 images are not analyzed again.&lt;/p&gt;

&lt;p&gt;This saves GPU time and user quota.&lt;/p&gt;

&lt;p&gt;If another upload happens while analysis is already running, the new photos remain pending and are picked up by the next analysis job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background processing
&lt;/h2&gt;

&lt;p&gt;Image analysis is expensive enough that I did not want HTTP requests doing the heavy work.&lt;/p&gt;

&lt;p&gt;Renvumi uses &lt;strong&gt;Celery&lt;/strong&gt; with Redis for background jobs.&lt;/p&gt;

&lt;p&gt;Different types of work are separated.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPU analysis&lt;/li&gt;
&lt;li&gt;import operations&lt;/li&gt;
&lt;li&gt;maintenance operations&lt;/li&gt;
&lt;li&gt;scheduled jobs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The GPU worker currently runs with concurrency set to one.&lt;/p&gt;

&lt;p&gt;That prevents multiple large model workloads from fighting for GPU memory.&lt;/p&gt;

&lt;p&gt;Interactive work also gets higher queue priority than large background analysis jobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoiding O(n²) comparisons
&lt;/h2&gt;

&lt;p&gt;One of the less glamorous problems appears when a photo library becomes large.&lt;/p&gt;

&lt;p&gt;A naive pairwise comparison of all images grows very quickly.&lt;/p&gt;

&lt;p&gt;For 10,000 photos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10,000 × 9,999 / 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is almost 50 million pairs.&lt;/p&gt;

&lt;p&gt;That is not something I wanted to do every time a user opened a result page.&lt;/p&gt;

&lt;p&gt;So candidate generation happens first.&lt;/p&gt;

&lt;p&gt;For near-duplicate processing I use perceptual-hash-based indexing to narrow the candidate set before applying more expensive comparisons.&lt;/p&gt;

&lt;p&gt;This was one of the biggest performance improvements for the global archive mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  The backend stack
&lt;/h2&gt;

&lt;p&gt;The current backend includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FastAPI
PostgreSQL 16
Redis
Celery
Qdrant
OpenCV
CLIP
LibreTranslate
Docker Compose
Caddy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The entire service runs in Docker.&lt;/p&gt;

&lt;p&gt;Caddy handles HTTPS.&lt;/p&gt;

&lt;p&gt;PostgreSQL stores application data.&lt;/p&gt;

&lt;p&gt;Qdrant stores image embeddings.&lt;/p&gt;

&lt;p&gt;Redis handles Celery, caching and temporary runtime state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why LibreTranslate?
&lt;/h2&gt;

&lt;p&gt;Renvumi supports text-to-image search.&lt;/p&gt;

&lt;p&gt;But CLIP works more predictably for my use case when queries are normalized into English.&lt;/p&gt;

&lt;p&gt;So I run a private LibreTranslate container inside the Docker network.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;жираф
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can be translated internally to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;giraffe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;before going to the search pipeline.&lt;/p&gt;

&lt;p&gt;No external translation API is required.&lt;/p&gt;

&lt;p&gt;The same internal translation system is also now used as part of the English localization workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  English and Russian interface
&lt;/h2&gt;

&lt;p&gt;Renvumi originally started as a Russian-language product.&lt;/p&gt;

&lt;p&gt;I recently added full language switching:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🇷🇺 Russian
🇬🇧 English
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The selected language applies to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;main UI&lt;/li&gt;
&lt;li&gt;account pages&lt;/li&gt;
&lt;li&gt;Help&lt;/li&gt;
&lt;li&gt;API documentation&lt;/li&gt;
&lt;li&gt;examples&lt;/li&gt;
&lt;li&gt;UI messages&lt;/li&gt;
&lt;li&gt;PWA metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The language is remembered in a cookie.&lt;/p&gt;

&lt;p&gt;On the first visit, the service can use the browser language as the default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storage and safety
&lt;/h2&gt;

&lt;p&gt;Photo management software has one dangerous feature:&lt;/p&gt;

&lt;p&gt;it can delete photos.&lt;/p&gt;

&lt;p&gt;That means recovery matters.&lt;/p&gt;

&lt;p&gt;Renvumi uses a trash workflow rather than immediately destroying files.&lt;/p&gt;

&lt;p&gt;There are also backup and restore mechanisms.&lt;/p&gt;

&lt;p&gt;I have spent more time than expected on things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;interrupted jobs&lt;/li&gt;
&lt;li&gt;database pool exhaustion&lt;/li&gt;
&lt;li&gt;backup consistency&lt;/li&gt;
&lt;li&gt;recovery&lt;/li&gt;
&lt;li&gt;storage encryption&lt;/li&gt;
&lt;li&gt;failed analysis&lt;/li&gt;
&lt;li&gt;user quotas&lt;/li&gt;
&lt;li&gt;long-running imports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These parts are much less exciting than AI models, but they are what make the service usable in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  API
&lt;/h2&gt;

&lt;p&gt;Renvumi also has an external API for paid plans.&lt;/p&gt;

&lt;p&gt;The API documentation includes examples for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cURL
Python
PowerShell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wanted the API docs to describe not just the endpoint name, but also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what the function does&lt;/li&gt;
&lt;li&gt;parameters&lt;/li&gt;
&lt;li&gt;returned data&lt;/li&gt;
&lt;li&gt;request example&lt;/li&gt;
&lt;li&gt;response example&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The documentation is available in both English and Russian.&lt;/p&gt;

&lt;h2&gt;
  
  
  The infrastructure is currently self-hosted
&lt;/h2&gt;

&lt;p&gt;One unusual part of the project is that Renvumi currently runs on my own server.&lt;/p&gt;

&lt;p&gt;The machine has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;72 CPU threads&lt;/li&gt;
&lt;li&gt;256 GB RAM&lt;/li&gt;
&lt;li&gt;NVIDIA GPU&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives me much more control over GPU workloads than using an expensive managed inference API for every image.&lt;/p&gt;

&lt;p&gt;It also makes performance tuning interesting because I can see exactly where the bottlenecks are.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned building it
&lt;/h2&gt;

&lt;p&gt;One thing became clear fairly quickly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI was not the hardest part.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Getting an embedding from CLIP is easy.&lt;/p&gt;

&lt;p&gt;Building a real service around it is much harder.&lt;/p&gt;

&lt;p&gt;The difficult parts were things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;making large jobs resumable&lt;/li&gt;
&lt;li&gt;not analyzing the same photo twice&lt;/li&gt;
&lt;li&gt;keeping interactive searches responsive&lt;/li&gt;
&lt;li&gt;limiting database connections&lt;/li&gt;
&lt;li&gt;cleaning up thousands of images safely&lt;/li&gt;
&lt;li&gt;keeping backup operations isolated&lt;/li&gt;
&lt;li&gt;handling users with large libraries&lt;/li&gt;
&lt;li&gt;making analysis results understandable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model is only one component.&lt;/p&gt;

&lt;p&gt;The product is everything around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current status
&lt;/h2&gt;

&lt;p&gt;Renvumi is live:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://renvumi.ru/" rel="noopener noreferrer"&gt;https://renvumi.ru/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The free tier currently includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1,000 analyses
1 GB storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My main goal now is not to keep adding features.&lt;/p&gt;

&lt;p&gt;I want to see how the system behaves on real photo libraries.&lt;/p&gt;

&lt;p&gt;Especially:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10,000 photos
50,000 photos
100,000+ photos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test datasets are useful, but real archives always contain weird cases you did not expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  I would like feedback
&lt;/h2&gt;

&lt;p&gt;If you have a large photo library, I would be interested in hearing how you manage it today.&lt;/p&gt;

&lt;p&gt;Do you use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lightroom?&lt;/li&gt;
&lt;li&gt;Google Photos?&lt;/li&gt;
&lt;li&gt;Apple Photos?&lt;/li&gt;
&lt;li&gt;local folders?&lt;/li&gt;
&lt;li&gt;a NAS?&lt;/li&gt;
&lt;li&gt;custom scripts?&lt;/li&gt;
&lt;li&gt;something else?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And what would stop you from using a service like Renvumi?&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;privacy&lt;/li&gt;
&lt;li&gt;upload speed&lt;/li&gt;
&lt;li&gt;cloud storage&lt;/li&gt;
&lt;li&gt;price&lt;/li&gt;
&lt;li&gt;wanting a desktop version&lt;/li&gt;
&lt;li&gt;not trusting automatic similarity detection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those answers are probably more valuable to me right now than another feature request.&lt;/p&gt;

&lt;p&gt;Renvumi:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://renvumi.ru/" rel="noopener noreferrer"&gt;https://renvumi.ru/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>saas</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
