<?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: Aryamaan jain</title>
    <description>The latest articles on DEV Community by Aryamaan jain (@strikeraryu).</description>
    <link>https://dev.to/strikeraryu</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F520116%2Fdbae1273-1c0f-47c2-ac9e-24a661b5fb0f.jpeg</url>
      <title>DEV Community: Aryamaan jain</title>
      <link>https://dev.to/strikeraryu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/strikeraryu"/>
    <language>en</language>
    <item>
      <title>Building My Home Server from an Old Laptop</title>
      <dc:creator>Aryamaan jain</dc:creator>
      <pubDate>Sat, 11 Oct 2025 11:22:53 +0000</pubDate>
      <link>https://dev.to/strikeraryu/building-my-home-server-from-an-old-laptop-4b39</link>
      <guid>https://dev.to/strikeraryu/building-my-home-server-from-an-old-laptop-4b39</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2A8LR2iawmi_RLRyy2" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2A8LR2iawmi_RLRyy2" alt="Building Home Server" width="760" height="506"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Building Home Server&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A few months ago, I noticed my old laptop lying in the corner, untouched and gathering dust. It wasn’t powerful enough for my daily work anymore, but I didn’t feel like throwing it away either. That’s when the idea struck me: why not turn it into a home server?&lt;/p&gt;

&lt;p&gt;I wiped everything off, installed a fresh copy of Ubuntu Server, and slowly started experimenting. At first, it was just about getting the basics running, but one thing led to another. Soon, I had a NAS for file storage, AdGuard to block ads across my network, a media server for streaming, a VPN for secure browsing, and even a place to run some of my own apps.&lt;/p&gt;

&lt;p&gt;This blog covers how I set things up, the tools I used, and some lessons I picked up along the way. If you’ve got an old machine lying around, this might give you a reason to bring it back to life.&lt;/p&gt;
&lt;h3&gt;
  
  
  Base Ubuntu Server Setup
&lt;/h3&gt;

&lt;p&gt;I won’t go too deep into the installation here (that probably deserves its own blog — let me know if you’d like one). If you’re new, you can follow this reference &lt;a href="https://www.youtube.com/watch?v=2Btkx9toufg" rel="noopener noreferrer"&gt;video&lt;/a&gt; — it’s a solid guide for installing Ubuntu Server. Just watch until the installation part if that’s all you need, or continue further if you want a deeper dive. Here’s a quick outline of the steps you’ll likely follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Created a flash drive with the Ubuntu Server image.&lt;/li&gt;
&lt;li&gt;Logged into BIOS and changed boot order.&lt;/li&gt;
&lt;li&gt;Booted from USB and installed Ubuntu with mostly default values.&lt;/li&gt;
&lt;li&gt;Chose DHCP for network initially (later switched to static IP).&lt;/li&gt;
&lt;li&gt;Selected the laptop’s drive for installation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  SSH Setup
&lt;/h3&gt;

&lt;p&gt;First things first — let’s make this laptop feel like a real server by setting up SSH. This way, I can connect remotely without ever needing to touch it physically.&lt;/p&gt;
&lt;h4&gt;
  
  
  Install SSH Server
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update &amp;amp;&amp;amp; sudo apt install openssh-server -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Check status: systemctl status sshd&lt;/li&gt;
&lt;li&gt;By default, it should start automatically.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Change Default SSH Port
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Open the SSH config: sudo vi /etc/ssh/sshd_config&lt;/li&gt;
&lt;li&gt;Find the line with #Port 22, uncomment it, and change it to something non-default, e.g.: Port 2222&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This helps avoid random automated attacks.&lt;/p&gt;
&lt;h4&gt;
  
  
  Disable Password Authentication
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Still in sshd_config, set: PasswordAuthentication_,_ This ensures only key-based logins work — much safer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Sometimes you also need to update&lt;/em&gt; &lt;em&gt;/etc/ssh/sshd_config.d/50-cloud-init.conf.&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Add Your Public Key
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;On your &lt;strong&gt;client machine&lt;/strong&gt; , generate a key if you don’t already have one:
ssh-keygen -t ed25519 -C “your_&lt;a href="mailto:email@example.com"&gt;email@example.com&lt;/a&gt;”&lt;/li&gt;
&lt;li&gt;Then copy it to the server:
ssh-copy-id -i ~/.ssh/id_ed25519.pub @server-ip -p 2222 &lt;strong&gt;(Note:&lt;/strong&gt; &lt;strong&gt;The steps below include ways to find your server’s IP address.)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;This adds your key to ~/.ssh/authorized_keys on the server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the End Restart SSH Server sudo systemctl reload sshd, Now you can try connecting like this: ssh @server-ip -p 2222&lt;/p&gt;

&lt;p&gt;After setting up SSH keys, you might still not be able to connect from your client. That’s usually because of the firewall or changing IP addresses. To make things smooth, we’ll:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Assign a &lt;strong&gt;static IP&lt;/strong&gt; to the server.&lt;/li&gt;
&lt;li&gt;Open the correct ports in the firewall.&lt;/li&gt;
&lt;li&gt;Configure the client for easy SSH access.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  Step 1: Find Your Current IP
&lt;/h4&gt;

&lt;p&gt;Before setting a static IP, you need to know your network interface and current IP. Here are a few ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using ip command: ip addr showip addr show. Look for inet under your ethernet interface (e.g., enp4s0).&lt;/li&gt;
&lt;li&gt;Using ifconfig (may need sudo apt install net-tools):&lt;/li&gt;
&lt;li&gt;Using hostname -I:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fygxif5tkam0va5uqdeyy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fygxif5tkam0va5uqdeyy.png" alt="Using ifconfig" width="800" height="178"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Using ifconfig&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This shows all IPs assigned to the machine.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 2: Set a Static IP and Connect to wifi
&lt;/h4&gt;

&lt;p&gt;Connect to your router and pick an available IP. Usually, numbers at the higher end of the subnet (like .7, .100, etc.) are free. Then edit your netplan config (/etc/netplan/01-netcfg.yaml or similar):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;network:
  version: 2
  renderer: networkd
  ethernets:
    enp4s0:
      addresses:
        - 192.168.1.7/24
      gateway4: 192.168.1.1
      nameservers:
        addresses:
          - 8.8.8.8
          - 8.8.4.4
  wifis:
    wlan0:
      addresses:
        - 192.168.1.50/24 # your static Wi-Fi IP
      gateway4: 192.168.1.1 # usually same as router
      nameservers:
        addresses:
          - 8.8.8.8
          - 8.8.4.4
      access-points:
        "Your_SSID":
          password: "YourPassword"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply the new configuration: sudo netplan apply&lt;/p&gt;

&lt;p&gt;Now your server will always have the same IP, which makes connecting much easier.|&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3: Configure the Client
&lt;/h4&gt;

&lt;p&gt;On your client machine, you can simplify SSH access by creating ~/.ssh/config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host MyServer
    HostName 192.168.1.7
    User your-username
    Port 2222 # or the port you set for SSH
    IdentityFile ~/.ssh/id_ed25519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this, you can connect simply with: ssh MyServer&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 4: Configure Firewall
&lt;/h4&gt;

&lt;p&gt;Ubuntu uses &lt;strong&gt;UFW&lt;/strong&gt; to manage access.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable/disable UFW:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo ufw enable
sudo ufw disable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Allow SSH (default port or custom port):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo ufw allow ssh # if using default port 22
sudo ufw allow 2222/tcp # replace 2222 with your custom SSH port
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Check firewall status: sudo ufw status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With a static IP and firewall configured, your SSH connection should work reliably from your client.&lt;/p&gt;

&lt;p&gt;Alright, enough of the setup — let’s get into the good stuff.&lt;/p&gt;

&lt;h3&gt;
  
  
  Network Storage (NAS with Samba)
&lt;/h3&gt;

&lt;p&gt;So, what’s the point of a NAS? Think of it like your personal cloud — a drive you can connect to from multiple devices at the same time, without messing with wires or constantly plugging in drives.&lt;/p&gt;

&lt;p&gt;Before setting up the NAS, I mounted a 50GB external drive on my server. I recommend doing the same so that your operating system runs on a separate drive, and the mounted drive can be fully dedicated for storage.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Identify and Prepare the Drive
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Check available storage: lsblk&lt;/li&gt;
&lt;li&gt;Find the drive you want to use (e.g., /dev/sda).&lt;/li&gt;
&lt;li&gt;If the drive has existing partitions, clear them:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sda
|
--sda1
--sda2

# Command to clear this
sudo wipefs -a &amp;lt;drive path&amp;gt;
# eg.
sudo wipefs -a /dev/sda
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note: Moving Forward Please use your own drive path instead of&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;/dev/sda.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Step 2: Create a Partition
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Start partitioning: sudo gdisk /dev/sda&lt;/li&gt;
&lt;li&gt;Commands in gdisk:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;n → create new partition&lt;/li&gt;
&lt;li&gt;Accept defaults to use the entire drive&lt;/li&gt;
&lt;li&gt;w → write changes&lt;/li&gt;
&lt;li&gt;y → confirm&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Verify the partition: sudo gdisk -l /dev/sda&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Step 3: Format the Partition
&lt;/h4&gt;

&lt;p&gt;Create an ext4 filesystem: sudo mkfs.ext4 /dev/sda1&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If prompted about an existing filesystem, type y.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 4: Mount the Partition
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Go to /mnt and create a folder:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd /mnt
sudo mkdir data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Make the folder immutable (optional, prevents accidental deletion):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo chattr +i data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Find the UUID of your partition:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo blkid /dev/sda1

# eg.
/dev/sda1: UUID="b2e62f4f-d338-470e-9ae7-4fc0e014858c" TYPE="ext4"

# Copy
UUID="b2e62f4f-d338-470e-9ae7-4fc0e014858c"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Edit /etc/fstab to mount automatically on boot: sudo vi /etc/fstab
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UUID="b2e62f4f-d338-470e-9ae7-4fc0e014858c" /mnt/data ext4 defaults 0 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Mount the drive using sudo mount -a&lt;/li&gt;
&lt;li&gt;Check it was mounted properly using df -h&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now the drive will mount automatically on each restart, and you can freely create files in /mnt/data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Set Up Network Storage with Samba
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Install Samba and create a user:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo smbpasswd -a striker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add a share configuration in /etc/samba/smb.conf:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[share]
path = /mnt/data
valid users = striker
read only = no
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Restart the Samba service: sudo systemctl restart smbd.service&lt;/li&gt;
&lt;li&gt;Allow in UFW: sudo ufw allow samba&lt;/li&gt;
&lt;li&gt;Connect from another device (e.g., Mac) via Go &amp;gt; Network Server, mounted at /Volumes/.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you have a NAS you can access anytime on your local network. The steps to connect may vary slightly depending on the device.&lt;/p&gt;

&lt;h3&gt;
  
  
  AdGuard Home with Docker
&lt;/h3&gt;

&lt;p&gt;Next, I wanted my server to act as a &lt;strong&gt;network-wide ad blocker&lt;/strong&gt; using &lt;a href="https://github.com/AdguardTeam/AdGuardHome" rel="noopener noreferrer"&gt;AdGuard Home&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Docker Compose for AdGuard
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Create a directory for AdGuard (create on system storage not attached storage):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir ~/adguard
cd ~/adguard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a docker compose file: vi docker-compose.yaml
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3'
services:
  adguard:
    image: adguard/adguardhome:latest
    container_name: adguard
    restart: unless-stopped
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "67:67/udp"
      - "68:68/tcp"
      - "80:80/tcp"
      - "443:443/tcp"
      - "3000:3000/tcp"
    volumes:
      - ./adguard/work:/opt/adguardhome/work
      - ./adguard/conf:/opt/adguardhome/conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Fixing the Port 53 Conflict
&lt;/h4&gt;

&lt;p&gt;Ubuntu’s &lt;strong&gt;systemd-resolved&lt;/strong&gt; already uses port 53. To free it for AdGuard:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a custom config in /etc/systemd/resolved.conf.d/adguardhome.conf
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Resolve] DNS=127.0.0.1 DNSStubListener=no
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Backup existing config:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mv /etc/resolv.conf /etc/resolv.conf.backup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a symlink:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Restart systemd-resolved:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl reload-or-restart systemd-resolved
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now AdGuard runs on port 53 without issues.&lt;/p&gt;

&lt;h4&gt;
  
  
  Steps
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Run docker-compose up -d to start AdGuard&lt;/li&gt;
&lt;li&gt;On another machine in same network, go to: http://:3000&lt;/li&gt;
&lt;li&gt;You will see the &lt;strong&gt;Get Started&lt;/strong&gt; screen. Select all the default options and create an admin user.&lt;/li&gt;
&lt;li&gt;Log in to your admin account.&lt;/li&gt;
&lt;li&gt;Update DNS settings:
&lt;/li&gt;
&lt;li&gt;Go to **Settings &amp;gt; DNS Settings
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For&lt;/strong&gt; Upstream DNS Server &lt;strong&gt;, clear the field and add&lt;/strong&gt; 1.1.1.1** (Cloudflare)
&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;Fallback DNS Server&lt;/strong&gt; , add &lt;strong&gt;8.8.8.8&lt;/strong&gt; (Google)
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Test Upstreams&lt;/strong&gt; and then  &lt;strong&gt;Apply&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;To use AdGuard, change your router or devices to use this DNS server. It’s best to update at the &lt;strong&gt;network level&lt;/strong&gt; so you don’t have to configure each device individually. If configured only on a device, it will work only for that device. Your server must keep running for AdGuard to function.&lt;/li&gt;
&lt;li&gt;You can explore more settings to customize AdGuard, but the above steps cover the &lt;strong&gt;bare minimum setup&lt;/strong&gt;  needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Jellyfin Media Server with Docker
&lt;/h3&gt;

&lt;p&gt;To manage and stream my media, I installed &lt;strong&gt;Jellyfin&lt;/strong&gt;  — an open-source alternative to Plex.&lt;/p&gt;

&lt;h4&gt;
  
  
  Docker Compose for Jellyfin
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Create a directory for Jellyfin (create on system storage not attached storage):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir ~/jellyfin
cd ~/jellyfin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a docker compose file: vi docker-compose.yaml
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  jellyfin:
    image: jellyfin/jellyfin
    container_name: jellyfin
    user: 1000:1000
    network_mode: 'host'
    volumes:
      - ./src/config:/config
      - ./src/cache:/cache
      - type: bind
        source: /mnt/data/jellyfin-media
        target: /media
    restart: unless-stopped
    extra_hosts:
      - 'host.docker.internal:host-gateway'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Steps
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Run docker-compose up -d to start Jellyfin&lt;/li&gt;
&lt;li&gt;Create the required directories for config, cache, and media:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir -p /mnt/data/config
mkdir -p /mnt/data/cache
mkdir -p /mnt/data/media
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Access Jellyfin from another machine in same network: &lt;a href="http://localhost:8096" rel="noopener noreferrer"&gt;http://&amp;lt;&lt;/a&gt;your-server-static-ip&lt;a href="http://localhost:8096" rel="noopener noreferrer"&gt;&amp;gt;:8096&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Follow the setup guide:
&lt;/li&gt;
&lt;li&gt;Create a user for yourself.
&lt;/li&gt;
&lt;li&gt;For the media folder, leave it as is for now.
&lt;/li&gt;
&lt;li&gt;Select your preferred metadata language (default works fine).
&lt;/li&gt;
&lt;li&gt;In Configure Remote Access, check both checkboxes.&lt;/li&gt;
&lt;li&gt;At this point, the server page will load but show “nothing here” initially.&lt;/li&gt;
&lt;li&gt;Add your media:
&lt;/li&gt;
&lt;li&gt;Inside /mnt/data/media, create folders for your content (e.g., movies, tv).
&lt;/li&gt;
&lt;li&gt;Go to Menu &amp;gt; Dashboard &amp;gt; Library in Jellyfin.
&lt;/li&gt;
&lt;li&gt;Add your media libraries through this page.&lt;/li&gt;
&lt;li&gt;Now your media will appear on the home page. You can:
&lt;/li&gt;
&lt;li&gt;Create different folder structures for movies and TV series.
&lt;/li&gt;
&lt;li&gt;Add multiple users.&lt;/li&gt;
&lt;li&gt;I Suggest to Explore settings to customize further.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Jellyfin automatically downloads metadata for your media.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Currently, everything is available only on your local network. If you want remote access or want to deploy your apps for access from anywhere, check out this blog: &lt;a href="https://dev.to/strikeraryu/host-local-projects-without-static-ip-using-cloudflare-tunnel-45hf"&gt;&lt;em&gt;Host Local Projects Without Static IP Using Cloudflare Tunnel&lt;/em&gt;.&lt;/a&gt; This allows you to move your apps from local-only to remote access.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Setting up a home server on an old laptop has been surprisingly rewarding. From remote SSH access to a NAS for file storage, network-wide ad blocking with AdGuard, and a media server with Jellyfin, I now have a versatile system running multiple services smoothly.&lt;/p&gt;

&lt;p&gt;If you’re curious about taking it further — like hosting your own AI models (think ChatGPT), setting up an automated torrent service with indexers, trackers, and downloaders, or deploying other custom applications — let me know. I can create dedicated blogs diving into each of these topics in detail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;—&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=2Btkx9toufg" rel="noopener noreferrer"&gt;Ubuntu Server Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=YqaDnnREqI8" rel="noopener noreferrer"&gt;AdGuard Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=eJvQKLVrmU8&amp;amp;t=434s" rel="noopener noreferrer"&gt;Jellyfin Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://adguard.com/kb/" rel="noopener noreferrer"&gt;AdGuard Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jellyfin.org/docs/" rel="noopener noreferrer"&gt;Jellyfin Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;202510110450&lt;/p&gt;

</description>
      <category>homelab</category>
      <category>tech</category>
      <category>ubuntu</category>
      <category>servers</category>
    </item>
    <item>
      <title>How We Upgraded PostgreSQL on AWS RDS in Production</title>
      <dc:creator>Aryamaan jain</dc:creator>
      <pubDate>Mon, 22 Sep 2025 21:14:13 +0000</pubDate>
      <link>https://dev.to/strikeraryu/how-we-upgraded-postgresql-on-aws-rds-in-production-5enb</link>
      <guid>https://dev.to/strikeraryu/how-we-upgraded-postgresql-on-aws-rds-in-production-5enb</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AKQiIVyWaWyp3cOs1" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AKQiIVyWaWyp3cOs1" alt="Upgrading PostgreSQL on AWS RDS  " width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Guide to Upgrading PostgreSQL on AWS RDS for Production Environments&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At one of my previous organizations, we were still running a PostgreSQL &lt;strong&gt;12.x&lt;/strong&gt; version on AWS RDS. This database powered our &lt;strong&gt;Metabase instance&lt;/strong&gt; , which was critical for analytics across the org. The catch? Since PostgreSQL 12 had already gone out of mainstream support, we had to rely on &lt;strong&gt;extended support&lt;/strong&gt;  — and pay extra for it.&lt;/p&gt;

&lt;p&gt;Naturally, this sparked conversations about &lt;strong&gt;cost savings&lt;/strong&gt; and &lt;strong&gt;long-term sustainability&lt;/strong&gt;. The decision was clear: we needed to &lt;strong&gt;upgrade PostgreSQL&lt;/strong&gt; to a supported version, for us it was &lt;strong&gt;PostgreSQL 17&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But like any database upgrade, it wasn’t just about clicking an “Upgrade” button. It meant understanding compatibility, preparing for edge cases, and making sure we didn’t bring down production dashboards that half the company relied on.&lt;/p&gt;

&lt;p&gt;That’s when I started drafting a &lt;strong&gt;structured upgrade plan&lt;/strong&gt;  — something I wish I had when we first went through it.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Upgrade Journey: Steps I Followed
&lt;/h3&gt;
&lt;h3&gt;
  
  
  Step 1: Change Log
&lt;/h3&gt;

&lt;p&gt;First, I dug into PostgreSQL’s release notes. This was important to identify &lt;strong&gt;deprecated features&lt;/strong&gt; or &lt;strong&gt;breaking changes&lt;/strong&gt; that could cause Metabase queries to fail.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Compatible Target Version
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;AWS&lt;/strong&gt; doesn’t allow skipping major versions, so I had to check the valid upgrade paths from &lt;strong&gt;12.x&lt;/strong&gt; using the &lt;strong&gt;AWS CLI&lt;/strong&gt;. Luckily, for us, it was a straightforward single-step upgrade.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws rds describe-db-engine-versions \
  --engine postgres \
  --engine-version 12 \
  --query "DBEngineVersions[*].ValidUpgradeTarget[*].{EngineVersion:EngineVersion}" \
  --output text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📖 &lt;strong&gt;Reference:&lt;/strong&gt; &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_UpgradeDBInstance.PostgreSQL.MajorVersion.html" rel="noopener noreferrer"&gt;AWS RDS Upgrade Targets&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzh5b7k9j9rp0jrzan6je.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzh5b7k9j9rp0jrzan6je.png" alt="Eg. Target Versions" width="800" height="205"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Eg. Target Versions&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Instance class compatibility
&lt;/h3&gt;

&lt;p&gt;Not every instance type supports all PostgreSQL versions, so I had to cross-check our RDS class against AWS’s documentation. In our case, we were on db.m6i.large, which turned out to support PostgreSQL 17 perfectly for our needs.&lt;/p&gt;

&lt;p&gt;📖 &lt;strong&gt;Reference:&lt;/strong&gt; &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.DBInstanceClass.Support.html" rel="noopener noreferrer"&gt;AWS RDS Instance Class&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdrs0m2gfc5t4jasz9t9q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdrs0m2gfc5t4jasz9t9q.png" alt="AWS RDS Instance Class" width="800" height="185"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 4: Parameter group
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt; upgrades can introduce new parameters or remove existing ones, which may impact your setup. To be safe, I created a new &lt;strong&gt;parameter group&lt;/strong&gt; for the target version and reviewed it. Since we hadn’t customized much, the defaults worked fine.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 5: Database Content
&lt;/h3&gt;

&lt;p&gt;Clean up potential blockers before upgrading:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prepared transactions:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT count(*) FROM pg_catalog.pg_prepared_xacts;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Replication slots:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT * FROM pg_replication_slots;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;reg* data types:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT count(*) FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n, pg_catalog.pg_attribute a
  WHERE c.oid = a.attrelid
     AND NOT a.attisdropped
     AND a.atttypid IN ('pg_catalog.regproc'::pg_catalog.regtype,
                         'pg_catalog.regprocedure'::pg_catalog.regtype,
                         'pg_catalog.regoper'::pg_catalog.regtype,
                         'pg_catalog.regoperator'::pg_catalog.regtype,
                         'pg_catalog.regconfig'::pg_catalog.regtype,
                         'pg_catalog.regdictionary'::pg_catalog.regtype)
     AND c.relnamespace = n.oid
     AND n.nspname NOT IN ('pg_catalog', 'information_schema');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Invalid databases:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT datname FROM pg_database WHERE datconnlimit = -2;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Unknown data types:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT DISTINCT data_type FROM information_schema.columns 
WHERE data_type ILIKE 'unknown';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📖 &lt;strong&gt;Reference:&lt;/strong&gt; &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_UpgradeDBInstance.PostgreSQL.MajorVersion.Process.html" rel="noopener noreferrer"&gt;Official Postgres Upgrade Docs&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Extension Compatibility
&lt;/h3&gt;

&lt;p&gt;Extensions (like uuid-ossp, pgcrypto, etc.) are &lt;strong&gt;not automatically upgraded&lt;/strong&gt; during a major version upgrade. So I listed all installed extensions and verified their compatibility with the new version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT * 
FROM pg_extension pe
JOIN pg_available_extension_versions pev 
  ON pev.name = pe.extname;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify compatibility in &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/PostgreSQLReleaseNotes/postgresql-extensions.html?source=post_page-----b5da3e713062---------------------------------------" rel="noopener noreferrer"&gt;AWS Postgres Extensions Release Note&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: Pending maintenance
&lt;/h3&gt;

&lt;p&gt;Checked RDS console for any pending maintenance tasks — these can conflict with upgrades, as running them during the upgrade can cause issues. You can view pending maintenance in the AWS RDS console under your DB instance’s “Maintenance &amp;amp; backups” section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8: Take snapshot
&lt;/h3&gt;

&lt;p&gt;Took a &lt;strong&gt;manual snapshot&lt;/strong&gt; before starting. It serves as a fallback in case the upgrade fails or anything goes wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 9: Check Application Compatibility
&lt;/h3&gt;

&lt;p&gt;Finally, I tested &lt;strong&gt;Metabase&lt;/strong&gt; itself with the upgraded &lt;strong&gt;PostgreSQL&lt;/strong&gt; version in staging. This step was crucial because Metabase Docker images often lock supported &lt;strong&gt;PostgreSQL&lt;/strong&gt; versions. That’s when we ran into a new issue: our current Metabase version didn’t support &lt;strong&gt;PostgreSQL 17&lt;/strong&gt;. This meant we had to upgrade Metabase as well. I created a forked Docker image with a few of our custom patches, then tested it against the new database to ensure everything worked smoothly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Checklist
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- [] Change log
- [] Compatible Target version
- [] Instance class compatibility
- [] Parameter group
- [] Database Content
- [] Extension compatibility
- [] Pending maintenance
- [] Take snapshot
- [] Application compatibility with new postgres version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Other Points to Consider
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blue-Green Deployment:&lt;/strong&gt; We used a blue-green deployment approach to test the upgrade on a separate environment, which allowed for a smooth transition to production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Monitoring:&lt;/strong&gt; Throughout the upgrade process, we monitored the RDS database diligently to ensure everything was working smoothly. We kept an eye on CPU, memory, disk usage, and query performance both before and after the upgrade to catch any regressions early.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Driver &amp;amp; Connection Compatibility:&lt;/strong&gt; We made sure that all applications were using PostgreSQL drivers compatible with the new version to avoid connection issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Review:&lt;/strong&gt; We reviewed roles, permissions, and SSL settings to ensure security wasn’t impacted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communicate Downtime:&lt;/strong&gt; I announced the upgrade downtime for Metabase and scheduled it at night, when traffic was minimal, to reduce disruption for users.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For us, the upgrade was about &lt;strong&gt;more than just security or new features&lt;/strong&gt;  — it was about avoiding unnecessary costs while ensuring reliability. What started as a cost-saving initiative turned into a lesson on &lt;strong&gt;systematic upgrades and resilience planning&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you’re in a similar boat with aging PostgreSQL versions on RDS, hopefully, this step-by-step narrative gives you a practical starting point.&lt;/p&gt;

&lt;p&gt;—&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/PostgreSQLReleaseNotes/postgresql-extensions.html?source=post_page-----b5da3e713062---------------------------------------" rel="noopener noreferrer"&gt;AWS Postgres Extensions Release Note&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_UpgradeDBInstance.PostgreSQL.MajorVersion.Process.html" rel="noopener noreferrer"&gt;Official Postgres Upgrade Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.DBInstanceClass.Support.html" rel="noopener noreferrer"&gt;AWS RDS Instance Class&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;202509230243&lt;/p&gt;

</description>
      <category>infrastructure</category>
      <category>postgres</category>
      <category>aws</category>
      <category>devops</category>
    </item>
    <item>
      <title>Rails CSRF Internals Uncovered — And a Real-World Bug We Faced</title>
      <dc:creator>Aryamaan jain</dc:creator>
      <pubDate>Sun, 21 Sep 2025 22:22:07 +0000</pubDate>
      <link>https://dev.to/strikeraryu/rails-csrf-internals-uncovered-and-a-real-world-bug-we-faced-2n02</link>
      <guid>https://dev.to/strikeraryu/rails-csrf-internals-uncovered-and-a-real-world-bug-we-faced-2n02</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AUulgwobSRs6GPyxJ" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AUulgwobSRs6GPyxJ" alt="Rails CSRF Internals Uncovered — And a Real-World Bug We Faced" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a previous post, I explored the &lt;a href="https://dev.to/strikeraryu/behind-the-scenes-of-csrf-a-developers-deep-dive-26ii"&gt;&lt;strong&gt;Fundamentals of CSRF&lt;/strong&gt;&lt;/a&gt; — what it is, how it works, and the standard mechanisms used to prevent it. While these mechanisms often “just work” in most frameworks, things can get tricky when you’re building or debugging something custom.&lt;/p&gt;

&lt;p&gt;At one of my Org, we encountered a puzzling issue: &lt;strong&gt;“Invalid CSRF token”&lt;/strong&gt; errors appearing on two different pages, each caused by a different underlying reason. To debug it, I had to dive deep into how Rails handles CSRF tokens internally.&lt;/p&gt;

&lt;p&gt;This post shares what I learned from that journey — including how Rails generates, masks, and verifies CSRF tokens, how we implemented similar logic in our codebase, and what ultimately caused the issue.&lt;/p&gt;

&lt;h3&gt;
  
  
  💎 How Rails Handles CSRF (Under the Hood)
&lt;/h3&gt;

&lt;p&gt;Rails’ &lt;strong&gt;CSRF&lt;/strong&gt; protection works out of the box, But once you peek under the hood, there’s some really clever stuff going on — especially around how tokens are generated, masked, and verified.&lt;/p&gt;

&lt;p&gt;At its core, Rails generates a &lt;strong&gt;base token&lt;/strong&gt; , stores it in the session, and then sends a &lt;strong&gt;masked version&lt;/strong&gt; of that token in the forms or headers. When a request comes in, Rails unmasks the token from the request and simply compares it with the one stored in the session. If they match, the request is good to go. A little more breakdown for each step.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Generating the CSRF Token
&lt;/h3&gt;

&lt;p&gt;When a new session starts, Rails creates a random string using SecureRandom and stores it in the session under :_csrf_token. This is the &lt;strong&gt;base token&lt;/strong&gt; and stays the same for the entire session (unless something like a login/logout resets it).&lt;br&gt;&lt;br&gt;
File —&lt;a href="https://github.com/rails/rails/blob/19eebf6d33dd15a0172e3ed2481bec57a89a2404/actionpack/lib/action_controller/metal/request_forgery_protection.rb" rel="noopener noreferrer"&gt; request_forgery_protection.rb&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;session[:_csrf_token] ||= SecureRandom.base64(32)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This base token is what everything else is built on.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Masking the Token (The Cool Part)
&lt;/h3&gt;

&lt;p&gt;Rails doesn’t just throw that raw token into your forms or headers. Instead, it &lt;strong&gt;masks&lt;/strong&gt; the token every time it sends it to the client.&lt;/p&gt;

&lt;p&gt;Here’s what that means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It creates a random one-time pad (same length as the token).&lt;/li&gt;
&lt;li&gt;It XORs the base token with the pad to “encrypt” it.&lt;/li&gt;
&lt;li&gt;It then sticks the pad and the encrypted result together.&lt;/li&gt;
&lt;li&gt;Finally, the combined result is Base64-encoded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So even if the base token never changes, the token sent to the frontend &lt;strong&gt;looks different every time&lt;/strong&gt;. This helps protect against attacks like BREACH.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Where the Token Shows Up
&lt;/h3&gt;

&lt;p&gt;The masked token is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Embedded in forms via a hidden input field (authenticity_token)&lt;/li&gt;
&lt;li&gt;Sent in the X-CSRF-Token header for AJAX or fetch requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way, Rails knows to expect it during any state-changing request like POST, PUT, PATCH, or DELETE.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Verifying the Token
&lt;/h3&gt;

&lt;p&gt;When a request comes in, Rails does the reverse dance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It grabs the token from the header or form field.&lt;/li&gt;
&lt;li&gt;Decodes it from Base64.&lt;/li&gt;
&lt;li&gt;If it’s a masked token (i.e., twice the expected length), it splits out the pad and encrypted part and XORs them to get back the original token.&lt;/li&gt;
&lt;li&gt;Compares this unmasked token to what’s stored in the session (:_csrf_token).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If anything’s off — wrong value, malformed format, etc. — Rails throws an InvalidAuthenticityToken error.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;To keep things extra safe, Rails regenerates the CSRF token after login or sign-up. So even though your session is still active, the CSRF token is refreshed. This prevents old tokens from being reused in new sessions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This behaviour is defined in the Devise initialiser:&lt;/em&gt; &lt;em&gt;config/initializers/devise.rb:102&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now let’s get started with the issues I ran into.&lt;br&gt;&lt;br&gt;
To make debugging easier, I first built a few &lt;strong&gt;common utilities&lt;/strong&gt; for decoding and comparing CSRF tokens.&lt;/p&gt;
&lt;h3&gt;
  
  
  Common Utils
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;To parse Session token&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def decrypt_session(cookie_string, mode = 'json')
  serializer = case mode
  when 'json' then JSON
  when 'marshal' then ActiveSupport::MessageEncryptor::NullSerializer
  end
  cookie = CGI::unescape(cookie_string.strip)
  salt = Rails.configuration.action_dispatch.encrypted_cookie_salt
  signed_salt = Rails.configuration.action_dispatch.encrypted_signed_cookie_salt
  key_generator = ActiveSupport::KeyGenerator.new(
    Rails.application.secrets.secret_key_base,
    iterations: 1000
  )
  secret = key_generator.generate_key(salt)[0, 32]
  sign_secret = key_generator.generate_key(signed_salt)
  encryptor = ActiveSupport::MessageEncryptor.new(
    secret,
    sign_secret,
    serializer: serializer
  )
  result = encryptor.decrypt_and_verify(cookie)
  (mode == 'marshal') ? Marshal.load(result) : result
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;To parse CSRF token&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def unmask_token(masked_token)
  token_length = masked_token.length / 2
  one_time_pad = masked_token[0...token_length]
  encrypted_token = masked_token[token_length..-1]
  xor_byte_strings(one_time_pad, encrypted_token)
end

def encode_token(token)
  Base64.strict_encode64(token)
end

def decode_token(token)
  Base64.strict_decode64(token)
end

def xor_byte_strings(s1, s2)
  s1.bytes.zip(s2.bytes).map { |(c1, c2)| c1 ^ c2 }.pack('c*')
end

def due(token)
  d = decode_token(token)
  u = unmask_token(d)
  encode_token(u)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;First Issue: Invalid CSRF Token on Landing Page&lt;/strong&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Issue
&lt;/h3&gt;

&lt;p&gt;There was a recurring problem where users would sometimes face an &lt;strong&gt;Invalid CSRF token&lt;/strong&gt; error on the new landing&lt;/p&gt;

&lt;h3&gt;
  
  
  What I Found
&lt;/h3&gt;

&lt;p&gt;When the error happened, the two tokens didn’t match:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The session contained one _csrf_token&lt;/li&gt;
&lt;li&gt;The CSRF API returned another&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But in Rails, there are really only two valid scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No token exists yet&lt;/strong&gt; → Rails generates one, saves it in the session, and returns it (after masking).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token already exists&lt;/strong&gt; → Rails reuses it, masking it again before sending.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So how could they be different?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmtivawwcxq36iblo8e12.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmtivawwcxq36iblo8e12.jpg" alt="Race Condition" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It turned out to be a &lt;strong&gt;race condition&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two requests landed almost at the same time.&lt;/li&gt;
&lt;li&gt;Both checked the session, saw no CSRF token, and decided to generate one.&lt;/li&gt;
&lt;li&gt;One request won the race and saved its token in the session.&lt;/li&gt;
&lt;li&gt;But the other request had already generated its own token, so it returned a value that was immediately outdated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This explains why the issue appeared when users hit the &lt;strong&gt;new landing page directly&lt;/strong&gt; without any cookies (for example, in incognito mode).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fix
&lt;/h3&gt;

&lt;p&gt;The problem was that we were &lt;strong&gt;eagerly calling the CSRF API&lt;/strong&gt; as soon as the page loaded. That increased the chance of this race condition.&lt;/p&gt;

&lt;p&gt;The solution was simple:&lt;br&gt;&lt;br&gt;
 👉 &lt;strong&gt;Remove the initial CSRF API call and instead fetch the token only when it’s actually needed — at the time of the first POST request.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This way, only one request is responsible for generating and storing the token, eliminating the race.&lt;/p&gt;

&lt;h3&gt;
  
  
  Second Issue: Invalid CSRF Token on Product Page
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Issue
&lt;/h3&gt;

&lt;p&gt;We noticed users sometimes got &lt;strong&gt;“Invalid CSRF token”&lt;/strong&gt; errors when opening the Product landing page from Slack links. The issue was inconsistent — only happening when the page was opened in Chrome’s Slack preview.&lt;/p&gt;

&lt;p&gt;Here’s the request flow that caused the issue:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User clicks a Slack link to open the Product landing page.&lt;/li&gt;
&lt;li&gt;The page makes an initial API call to load content.&lt;/li&gt;
&lt;li&gt;A subsequent API call (e.g., user/v2 for OTP) fails with an invalid CSRF token error.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It seemed like the session was being &lt;strong&gt;dropped between requests&lt;/strong&gt; , causing CSRF verification to fail. This issue was particularly tricky to debug.&lt;/p&gt;

&lt;h3&gt;
  
  
  Debugging Process
&lt;/h3&gt;

&lt;p&gt;I approached this step by step, adding logs and tracing the request flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Inspecting CSRF Verification&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Added logging inside Rails’ request_forgery_protection.rb where the CSRF token is compared with the session.&lt;/li&gt;
&lt;li&gt;Finding: The session was &lt;strong&gt;empty&lt;/strong&gt; at this point, even though the cookie itself was present.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This suggested the session was being created correctly initially, but something later in the middleware chain was removing it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Checking Session Creation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Added loggers in ActionDispatch::Request::Session and CookieStore.&lt;/li&gt;
&lt;li&gt;Found that the &lt;strong&gt;session was properly populated&lt;/strong&gt; from the cookie, including the CSRF token.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the drop wasn’t during session creation — it happened &lt;strong&gt;later in the middleware stack&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Tracing Middleware Calls&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Added logging in SanitizeQueryStringMiddleware.&lt;/li&gt;
&lt;li&gt;Observation: The session was available &lt;strong&gt;before&lt;/strong&gt; the middleware but gone  &lt;strong&gt;after&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Testing middleware order confirmed that &lt;strong&gt;session availability depended on execution order&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Rack-protection middleware, especially session_hijacking.rb, ran after SanitizeQueryStringMiddleware.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Identifying the Root Cause&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In rack-protection/lib/rack/protection/session_hijacking.rb, I found a check comparing the session’s tracking hash with request environment values.&lt;/li&gt;
&lt;li&gt;Specifically, the HTTP_ACCEPT_LANGUAGE header differed between requests:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Request HTTP_ACCEPT_LANGUAGEInitial Product APIen-USOTP API (user/v2)en-US,en;q=0.9,hi;q=0.8&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This difference triggered &lt;strong&gt;rack-protection’s session-hijacking check&lt;/strong&gt; , which dropped the session.&lt;/li&gt;
&lt;li&gt;Dropping the session invalidated the CSRF token, causing the failure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Hypothesis Testing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Replicated the flow using Postman:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, call the Product page to get the CSRF token.&lt;/li&gt;
&lt;li&gt;Then, call user/v2 with the same cookies and CSRF token.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Results:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Same Accept-Language header&lt;/strong&gt; → CSRF passed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Different header&lt;/strong&gt; → CSRF failed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This confirmed that the mismatch in headers caused the session drop and CSRF failure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fix
&lt;/h3&gt;

&lt;p&gt;The fix was straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Remove HTTP_ACCEPT_LANGUAGE tracking&lt;/strong&gt; in session-hijacking (already removed in newer rack-protection versions — &lt;a href="https://github.com/sinatra/sinatra/commit/6cf49c885554fa6265f119e6ad5b8d3707c22f64" rel="noopener noreferrer"&gt;&lt;strong&gt;Commit Link&lt;/strong&gt;&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Upgrade or patch the rack-protection middleware.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, minor differences in request headers no longer drop the session, and the CSRF token remains valid across the flow — even in Slack preview.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CSRF&lt;/strong&gt; protection usually works behind the scenes, and you rarely notice it — until it breaks. Going through Rails’ internals and debugging these real issues reminded me that even small things, like request timing or headers, can trip it up. Understanding how the tokens are generated, masked, and verified made it much easier to find the root cause and fix it properly.&lt;/p&gt;

&lt;p&gt;—&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/rails/rails/blob/19eebf6d33dd15a0172e3ed2481bec57a89a2404/actionpack/lib/action_controller/metal/request_forgery_protection.rb" rel="noopener noreferrer"&gt;CSRF Protection Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sinatra/sinatra/blob/main/rack-protection/lib/rack/protection/session_hijacking.rb" rel="noopener noreferrer"&gt;Session Hijacking Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sinatra/sinatra/commit/6cf49c885554fa6265f119e6ad5b8d3707c22f64" rel="noopener noreferrer"&gt;Removal of ACCEPT_LANGUAGE Commit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;202509220350&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>rails</category>
      <category>ruby</category>
      <category>security</category>
    </item>
    <item>
      <title>Host Local Projects Without Static IP Using Cloudflare Tunnel</title>
      <dc:creator>Aryamaan jain</dc:creator>
      <pubDate>Fri, 11 Apr 2025 13:42:44 +0000</pubDate>
      <link>https://dev.to/strikeraryu/host-local-projects-without-static-ip-using-cloudflare-tunnel-45hf</link>
      <guid>https://dev.to/strikeraryu/host-local-projects-without-static-ip-using-cloudflare-tunnel-45hf</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsb30c2onpxhai7yewtl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsb30c2onpxhai7yewtl.png" alt="Host Local Projects Without Static IP Using Cloudflare Tunnel" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Setting up a public-facing server from your internal network usually comes with a major headache —  &lt;strong&gt;you need a static IP and a DNS provider&lt;/strong&gt;. Even if you’ve got your domain name ready, your ISP may not assign you a static IP, or worse, may charge extra for it.&lt;/p&gt;

&lt;p&gt;But what if I told you there’s a way to bypass that entirely?&lt;/p&gt;

&lt;h3&gt;
  
  
  🔐 Enter Cloudflare Tunnel (aka cloudflared)
&lt;/h3&gt;

&lt;p&gt;Cloudflare Tunnel allows you to expose your local server &lt;strong&gt;securely&lt;/strong&gt; to the internet without needing a static IP or poking holes in your firewall. The magic? It establishes a &lt;strong&gt;persistent outbound connection&lt;/strong&gt; from your machine to Cloudflare’s edge network.&lt;/p&gt;

&lt;p&gt;So, instead of users directly hitting your home IP, their requests go through Cloudflare, which then forwards traffic through this tunnel to your internal server — and sends responses back the same way.&lt;/p&gt;

&lt;p&gt;All you need is a &lt;strong&gt;registered domain on Cloudflare&lt;/strong&gt;. No static IP, no port forwarding, and no exposing your machine directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛠️ Setting Up a Cloudflare Tunnel
&lt;/h3&gt;

&lt;p&gt;Let’s walk through the setup with a simple example.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Go to Cloudflare Zero Trust Dashboard
&lt;/h3&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;Zero Trust&lt;/strong&gt; section in your Cloudflare dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvh1ewf31oxttigk8b9sq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvh1ewf31oxttigk8b9sq.png" alt="Zero Trust Section" width="263" height="287"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Zero Trust Section&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Create Your Team Domain
&lt;/h3&gt;

&lt;p&gt;Pick any name for your team domain. This is just for internal structuring.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftji9ttkjhrioz95jv456.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftji9ttkjhrioz95jv456.png" alt="Team Domain" width="800" height="393"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Team Domain&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Select the Free Plan
&lt;/h3&gt;

&lt;p&gt;Yes, it’s completely free! Just proceed to payment, enter any method (no actual charges).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7n204k5nrj6h54v9nj2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7n204k5nrj6h54v9nj2.png" alt="Free Plan" width="229" height="834"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Free Plan&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Navigate to “Networks &amp;gt; Tunnels”
&lt;/h3&gt;

&lt;p&gt;On the sidebar, head to &lt;strong&gt;Networks → Tunnels&lt;/strong&gt; , then click &lt;strong&gt;“Add a Tunnel.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3nem29cwbysu4tb3wx3z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3nem29cwbysu4tb3wx3z.png" alt="Add A Tunnel" width="800" height="297"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Add A Tunnel&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  6. Start the Tunnel (Pick Your Environment)
&lt;/h3&gt;

&lt;p&gt;You’ll now get setup instructions based on your OS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For macOS:&lt;/strong&gt; brew install cloudflared and run as a service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker:&lt;/strong&gt; You can also run it inside Docker — straightforward and clean.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once done, your tunnel status should show as &lt;strong&gt;Connected&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmp2fnlh6l2as481uwuen.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmp2fnlh6l2as481uwuen.png" alt="Connected Tunnel" width="800" height="127"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Connected Tunnel&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  🌐 Host Something Locally
&lt;/h3&gt;

&lt;p&gt;Let’s test it out by running a simple server:&lt;/p&gt;
&lt;h3&gt;
  
  
  Option 1: NGINX via Docker
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -p 8080:80 nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Option 2: A Tiny Flask App
&lt;/h3&gt;

&lt;p&gt;Save this as app.py and run with python app.py:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from flask import Flask
app = Flask( __name__ )
@app.route('/')
def home():
    return 'Hello, Flask!'
if __name__ == ' __main__':
    app.run(debug=True)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🌍 Expose It with a Public Hostname
&lt;/h3&gt;

&lt;p&gt;Once the tunnel is active:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add a new &lt;strong&gt;public hostname&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Choose any subdomain (e.g., demo.yourdomain.com)&lt;/li&gt;
&lt;li&gt;Type: HTTP&lt;/li&gt;
&lt;li&gt;URL: localhost:8080 (for NGINX) or localhost:5000 (for Flask)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ANSspTWgmxciLcFRJ4w0dtw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ANSspTWgmxciLcFRJ4w0dtw.png" alt="Setting for public Host Name" width="800" height="253"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Setting for public Host Name&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Save it, and boom — you now have a public link to your local server!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ADdk9I0BWUWOhlaPjHbksSQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ADdk9I0BWUWOhlaPjHbksSQ.png" alt="Local Server On Public Domain" width="800" height="357"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Local Server On Public Domain&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🐛 Facing issue with HTTPS request
&lt;/h3&gt;

&lt;p&gt;In my setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js frontend&lt;/strong&gt; ran on port 3000 and was exposed via a tunnel to a subdomain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python Flask backend&lt;/strong&gt; ran on port 5000, also exposed via a tunnel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both were running with &lt;strong&gt;HTTP&lt;/strong&gt; internally.&lt;/p&gt;

&lt;p&gt;Cloudflare provides an &lt;strong&gt;HTTPS URL&lt;/strong&gt; for every tunnel. So even though my servers were running on HTTP, the frontend was making HTTPS requests to the backend.&lt;/p&gt;

&lt;p&gt;This caused CORS or SSL handshake failures. The requests were made over HTTPS but seemed to break mid-way, possibly due to internal HTTP fallback.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Solution&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;I started the Flask server with an &lt;strong&gt;adhoc self-signed SSL certificate&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;flask run --cert=adhoc --host=0.0.0.0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While setting up the public hostname in the tunnel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I used: https://:5000&lt;/li&gt;
&lt;li&gt;Changed the &lt;strong&gt;TLS setting to “No TLS verify”&lt;/strong&gt; , allowing Cloudflare to accept self-signed certs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since Flask was generating the certificate on the fly, this was essential to avoid SSL errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Cloudflare Tunnel is a game-changer for self-hosting. Whether you’re building demos, internal tools, or working on projects that need remote access, you can now skip the static IP drama.&lt;/p&gt;

&lt;p&gt;Try it out, and give your side projects a proper home on the internet — without compromising on security or simplicity.&lt;/p&gt;

&lt;p&gt;If you face any issues while setting this up, feel free to reach out. Always happy to help!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;—&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/" rel="noopener noreferrer"&gt;Cloudflare Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;202504111902&lt;/p&gt;

</description>
      <category>servers</category>
      <category>devops</category>
      <category>cloudflaretunnel</category>
      <category>hosting</category>
    </item>
    <item>
      <title>Behind the Scenes of CSRF: A Developer’s Deep Dive</title>
      <dc:creator>Aryamaan jain</dc:creator>
      <pubDate>Wed, 09 Apr 2025 08:54:44 +0000</pubDate>
      <link>https://dev.to/strikeraryu/behind-the-scenes-of-csrf-a-developers-deep-dive-26ii</link>
      <guid>https://dev.to/strikeraryu/behind-the-scenes-of-csrf-a-developers-deep-dive-26ii</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flffumqtz5yaa8hheooed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flffumqtz5yaa8hheooed.png" alt="Behind the Scenes of CSRF: A Developer’s Deep Dive" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CSRF&lt;/strong&gt; is one of those security issues that’s been around for a long time. There are well-known solutions and standard practices to prevent it — so much so that they’re often taken for granted. But sometimes, it’s worth taking a step back to understand the vulnerability more deeply and how each mitigation actually works under the hood.&lt;/p&gt;

&lt;p&gt;A while back, I encountered a tricky CSRF-related issue that led me down a rabbit hole of understanding how &lt;strong&gt;Ruby on Rails&lt;/strong&gt; internally handles CSRF protection. That experience inspired this post.&lt;/p&gt;

&lt;p&gt;This blog is part one of that journey — focusing on what CSRF really is, the conditions that make it possible, and how different protective mechanisms work. In a future post, I’ll share how Rails tackles CSRF internally, what went wrong in my case, and how I ended up resolving it.&lt;/p&gt;

&lt;p&gt;Let’s dive in.&lt;/p&gt;

&lt;h3&gt;
  
  
  💣 What Even Is CSRF?
&lt;/h3&gt;

&lt;p&gt;At its core, &lt;strong&gt;Cross-Site Request Forgery (CSRF)&lt;/strong&gt; is about abusing trust. If you’re logged into a website (say, your bank), and then unknowingly visit a malicious site, that site might trick your browser into sending a request to the bank on your behalf. The browser includes your cookies (like session tokens) automatically — so the bank thinks it’s you.&lt;/p&gt;

&lt;p&gt;It’s like handing your house keys to a stranger because they asked in the right tone of voice.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;You’re logged into your account.&lt;/li&gt;
&lt;li&gt;A malicious page includes something like:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;img src="https://bank.com/transfer-money?amount=1000&amp;amp;to=attacker" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Your browser sees it and thinks, “Sure! Let me attach your auth cookies and fire that off!”&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;: If your API uses stateless, token-based authentication in headers (like JWT), CSRF isn’t typically an issue. This mainly affects cookie-based auth.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🔑 Conditions That Make CSRF Possible
&lt;/h3&gt;

&lt;p&gt;For a CSRF attack to succeed, a few conditions must align:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Important Action&lt;/strong&gt; : The targeted action must be meaningful — like changing account details or transferring funds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cookie-Based Authentication&lt;/strong&gt; : If the browser automatically attaches auth cookies or headers, the server is easily fooled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictable Parameters&lt;/strong&gt; : If request parameters are easy to guess or fixed, attackers can replicate them in malicious payloads.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🛠️ How a CSRF Attack Happens
&lt;/h3&gt;

&lt;p&gt;Attackers typically set up a webpage that silently triggers a request to a vulnerable endpoint. 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;&amp;lt;img src="https://website.com/risky-path"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or with a form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form action="https://vulnerable.com/update-email" method="POST"&amp;gt;
  &amp;lt;input type="hidden" name="email" value="attacker@example.com" /&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;script&amp;gt;document.forms[0].submit()&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suddenly, your innocent browsing turns into a silent attack.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛡️ Preventing CSRF Attacks
&lt;/h3&gt;

&lt;p&gt;The goal is to &lt;em&gt;break the attack chain&lt;/em&gt; — by introducing randomness or validating the origin of requests. Common strategies include:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. CSRF Tokens
&lt;/h3&gt;

&lt;p&gt;The server generates a unique, random token for each user session. This token must be sent back by the client with every sensitive request — via header or hidden form field.&lt;/p&gt;

&lt;p&gt;But using CSRF tokens incorrectly opens up vulnerabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the token is &lt;strong&gt;not tied to the session&lt;/strong&gt; , attackers can use their own token.&lt;/li&gt;
&lt;li&gt;If the token is stored in cookies and validated by simply matching values (called &lt;strong&gt;Double Submit&lt;/strong&gt; ), an attacker can manipulate both the token and the cookie if the browser allows it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best Practices&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tokens should be &lt;strong&gt;random&lt;/strong&gt; and &lt;strong&gt;session-bound&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Tokens should &lt;strong&gt;not&lt;/strong&gt; be sent in cookies.&lt;/li&gt;
&lt;li&gt;For form-based actions, embed them as &lt;strong&gt;hidden fields&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;For XHR requests, send them in &lt;strong&gt;custom headers&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note: If sent via headers, only AJAX/XHR requests are supported — not native HTML forms.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. SameSite Cookie Attribute
&lt;/h3&gt;

&lt;p&gt;This is a browser-level defense that controls when cookies are sent during cross-site requests.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strict&lt;/strong&gt; : Cookies are only sent in first-party contexts. Completely blocks CSRF but may break SSO and other flows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lax (Default)&lt;/strong&gt;: Cookies are sent on top-level GET navigations (like clicking a link), but blocked in other cross-site cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;None&lt;/strong&gt; : Cookies are sent in all requests, but must include the Secure flag (HTTPS only).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Set-Cookie: sessionId=abc123; SameSite=None; Secure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Vulnerabilities in SameSite Protections:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lax Bypass&lt;/strong&gt; : If the server treats both GET and POST similarly, attackers can misuse Lax rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strict Bypass via Redirection&lt;/strong&gt; : Attacker-controlled redirections using URL parameters can indirectly trigger CSRF.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lax Delays&lt;/strong&gt; : For SSO flows, browsers delay applying Lax for 120 seconds — creating a window of vulnerability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Referrer-Based Validation
&lt;/h3&gt;

&lt;p&gt;Some servers rely on checking the Referrer header to ensure requests come from the right origin.&lt;/p&gt;

&lt;p&gt;This method is &lt;strong&gt;less secure&lt;/strong&gt; , and attackers can bypass it easily:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use meta tags to strip the referrer:&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;Host malicious pages on subdomains like:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="http://vulnerable.com.attacker.com" rel="noopener noreferrer"&gt;http://vulnerable.com.attacker.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Referrer validation should &lt;em&gt;never&lt;/em&gt; be your only line of defense.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔚 Final Thoughts
&lt;/h3&gt;

&lt;p&gt;CSRF is dangerous because it abuses trust — your browser’s trust in cookies, your server’s trust in headers, and the user’s trust in your website.&lt;/p&gt;

&lt;p&gt;The good news? It’s preventable. With the right strategies, you can turn this silent threat into a non-issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;—&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://portswigger.net/web-security/csrf/lab-no-defenses" rel="noopener noreferrer"&gt;Port Swigger Labs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portswigger.net/web-security/csrf#what-is-csrf" rel="noopener noreferrer"&gt;Blog related to CSRF&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;202504091409&lt;/p&gt;

</description>
      <category>websecurity</category>
      <category>web</category>
      <category>csrf</category>
      <category>browsersecurity</category>
    </item>
    <item>
      <title>Hamiltonian Cycle</title>
      <dc:creator>Aryamaan jain</dc:creator>
      <pubDate>Wed, 03 May 2023 18:45:28 +0000</pubDate>
      <link>https://dev.to/strikeraryu/hamiltonian-cycle-1cmi</link>
      <guid>https://dev.to/strikeraryu/hamiltonian-cycle-1cmi</guid>
      <description>&lt;p&gt;Great use of Hamiltonian cycle&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1y864zpizbmd6oe439wf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1y864zpizbmd6oe439wf.png" alt="Hamiltonian cycle" width="800" height="436"&gt;&lt;/a&gt;&lt;br&gt;
Let’s take a step back and let’s see what’s a &lt;a href="https://mathworld.wolfram.com/HamiltonianCycle.html" rel="noopener noreferrer"&gt;hamiltonian&lt;/a&gt; cycle. In the mathematical field, it’s a concept of &lt;a href="https://en.wikipedia.org/wiki/Graph_theory#:~:text=In%20mathematics%2C%20graph%20theory%20is,also%20called%20links%20or%20lines" rel="noopener noreferrer"&gt;graph theory&lt;/a&gt;.). A &lt;a href="https://en.wikipedia.org/wiki/Graph" rel="noopener noreferrer"&gt;graph&lt;/a&gt; is like a map of nodes. Where each node connects with an edge, and this represents that there is a path between those specific nodes. A graph is used in many ways to represent all kinds of data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsruh3ym5io5ca4ogkkvh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsruh3ym5io5ca4ogkkvh.png" alt="Graph" width="491" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The edges between nodes represent a direct connection between them. We can use a combination of these edges to show a path between two nodes that are not directly connected. A Hamiltonian path is a path in a graph where all nodes are only visited once. A hamiltonian cycle is a cycle of the hamiltonian path where the first node and last node are the same.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3j7j5w4b4a8he50d8i6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3j7j5w4b4a8he50d8i6.png" alt="Hamiltonian Cycle" width="786" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to find a Hamiltonian cycle in an undirected graph?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq7gkfta528vlpe110sof.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq7gkfta528vlpe110sof.png" alt="Kinds of graph" width="704" height="288"&gt;&lt;/a&gt;&lt;br&gt;
To find a Hamiltonian cycle in a graph (whenever I use a word graph I am talking about an undirected graph) we can use &lt;a href="https://www.geeksforgeeks.org/introduction-to-backtracking-data-structure-and-algorithm-tutorials/" rel="noopener noreferrer"&gt;backtracking&lt;/a&gt;. In this algorithm, we will keep a log of the path till now, and we will add nodes to it. If the node is not already present in the path we will keep adding nodes. Else we will backtrack to the previous working state and check new nodes with that. In the end, if we can’t find any path with the required conditions we will say there is no possible hamiltonian cycle in this graph.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let see the algorithm in-depth&lt;/strong&gt;&lt;br&gt;
First, let’s create a function to check if a node N is ok to add to the path. We will check if N and the last node have an edge between them. After that, we will see if N is not already present in the path. If both conditions hold, we will return true that it is ok to add N in the path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bool node_check(int node, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; graph, vector&amp;lt;int&amp;gt; path, int pos){

    // node is not conneccted to the prev node of the path
    if(graph[path[pos - 1]][node] == 0)
        return false;

    // check the node is already visited in the path
    for(int i = 0; i &amp;lt; pos; i++)
        if(path[i] == node)
            return false;

    return true;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s create a &lt;a href="https://www.geeksforgeeks.org/recursive-functions/" rel="noopener noreferrer"&gt;recursive function&lt;/a&gt; to find hamiltonian cyclen in the graph. The base case for this function will be if we have added all nodes in the path. Further, we will check if the last node and first node of the path have an edge between them. If an edge is present between those two nodes we will return true, or else we will return false. Let’s have a look over the recursive step. We will try to add each node in the path and check if it is valid or not. If the node is valid we will add it to the path and look further for the hamiltonian cycle using recursive call. If the function returns false, it shows we can’t find any hamiltonian cycle with the specific node. Then we will remove the node from the path and further check with other nodes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bool find_cycle(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; graph, vector&amp;lt;int&amp;gt; &amp;amp;path, int pos, int n)  {  

    // check all node are added to path
    if(pos == n){  
        // And check the is a edge between first node and the last node of the path
        if(graph[path[pos - 1]][path[0]] == 1)  
            return true;  
        else
            return false;  
    }  

    // we will check all node 
    for(int v = 0; v &amp;lt; n; v++)  
    {  
        // check if node is valid to insert in cycle
        if(node_check(v, graph, path, pos))  {  

            // we will add node to the path 
            path[pos] = v;  

            // check further 
            if(find_cycle(graph, path, pos + 1, n) == true)  
                return true;  

            // if no valid cycle with this node we will remove it from cycle
            path[pos] = -1;  
        }  
    } 

    return false; 
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At last, let’s take a graph as an input and find a hamiltonian cycle in it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Include the necessary header file while running the code.&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int main() {

    // to take input of a graph
    int n, q;
    cin&amp;gt;&amp;gt;n&amp;gt;&amp;gt;q;
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; graph(n, vector&amp;lt;int&amp;gt;(n));
    vector&amp;lt;int&amp;gt; path(n, -1);
    path[0]=0;

    for(int i = 0; i &amp;lt; q; i++){
        int a, b;
        cin&amp;gt;&amp;gt;a&amp;gt;&amp;gt;b;

        graph[a][b] = 1;
        graph[b][a] = 1;
    }

 // if there is a valid cycle
 if(find_cycle(graph, path, 1, n)){
     // print the path
     for(int i : path)cout&amp;lt;&amp;lt;i&amp;lt;&amp;lt;" ";
     cout&amp;lt;&amp;lt;path[0];
     cout&amp;lt;&amp;lt;endl;
 }
 else {
     cout&amp;lt;&amp;lt;"No valid cycle \n";
 }

 return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Input&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4hm9sia4xus9mbixe8t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4hm9sia4xus9mbixe8t.png" alt="Input" width="89" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Output&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff39w93o3ywgdmszv11gf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff39w93o3ywgdmszv11gf.png" alt="Output" width="199" height="38"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let’s see what so great about the hamiltonian cycle. The property of touching a node only once, or we can say never crossing the path in a hamiltonian cycle provides high usability and can be used in real-life problems. Let’s look over one example, we can use these cycles to create a perfect path for the snake to follow in the snake game, so it will never collide with its tail or the border. This can work as the AI for the &lt;a href="https://en.wikipedia.org/wiki/Snake_(video_game_genre)" rel="noopener noreferrer"&gt;snake game&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8r8msrnges3nl8n3auic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8r8msrnges3nl8n3auic.png" alt="snake game" width="720" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to contact me🔥. &lt;a href="https://www.linkedin.com/in/aryamaan-jain-9330a8190/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; &lt;a href="https://twitter.com/striker_aryu" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; &lt;a href="https://www.instagram.com/striker_aryu/?hl=en" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can check my other projects and stay tuned for more.👀&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Parallax Images</title>
      <dc:creator>Aryamaan jain</dc:creator>
      <pubDate>Mon, 30 Nov 2020 12:17:05 +0000</pubDate>
      <link>https://dev.to/strikeraryu/parallax-images-12k4</link>
      <guid>https://dev.to/strikeraryu/parallax-images-12k4</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq2fwmxw1vg3dblkwh973.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq2fwmxw1vg3dblkwh973.gif" alt="Alt Text" width="200" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have all seen 3D movies, illusion images, and know-how good they look, and this gave me the idea to make some tool that makes images shift their perspective as the user moves his head. Imagine how cool it will look.&lt;/p&gt;

&lt;h3&gt;
  
  
  What will be the effect?
&lt;/h3&gt;

&lt;p&gt;We are all familiar with the term &lt;a href=""&gt;Parallax&lt;/a&gt; which is simply the different amount of change in the apparent position of the object, which depends on how far we are from it.&lt;br&gt;
So if we can gain the same effect in 2D images that the different layers of images shift differently, then we can have a sense of dept in those images and a cool effect that we want.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let’s break down the process
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3w0dtj10m5154upzc83t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3w0dtj10m5154upzc83t.png" alt="Alt Text" width="527" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So first, we need to break an image into different layers and, we need a depth map of the 2D image for that. A &lt;a href="https://en.wikipedia.org/wiki/Depth_map" rel="noopener noreferrer"&gt;depth map&lt;/a&gt; is simply a black and white image where the whiteness of the images tells how close the object is to POV. After we got the basic layers, we need to &lt;a href="https://en.wikipedia.org/wiki/Inpainting#:~:text=Inpainting%20is%20a%20conservation%20process,to%20present%20a%20complete%20image." rel="noopener noreferrer"&gt;inpaint&lt;/a&gt; the missing parts from each layer. So finally, we have broken a single image into different layers. Now we can show different layers on top of each other, which will look the same as the original image. Now we can use our camera for &lt;b&gt;face detection&lt;/b&gt; and measure how the movement of the user’s head and then shift those layers to match the new POV.&lt;/p&gt;


&lt;h1&gt;
  
  
  Let’s see how to code this tool
&lt;/h1&gt;

&lt;p&gt;So first, we need to import some files, So copy this code into your file.&lt;br&gt;
I recommend using OpenCV of version 4.1.0.25 because there are few bugs in later versions when we use face_cascade. For other libraries, you can use any version but try to use the newer ones.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pygame&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pg&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to load the image and the &lt;b&gt;depth map&lt;/b&gt; and resize them to match the size. For now, we will provide a depth map to our code, but you can generate your own using a model &lt;a href="https://github.com/intel-isl/MiDaS" rel="noopener noreferrer"&gt;MiDaS&lt;/a&gt;, which I have used in my main tool. You can have a look at my &lt;a href="https://github.com/strikeraryu/Parallax_Image" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;moon.jpg&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CV_8UC4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;depth_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;moon_depth_map.png&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;depth_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cvtColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depth_map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COLOR_RGB2GRAY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth_map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffa3ndjazei0w3qghgp1t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffa3ndjazei0w3qghgp1t.png" alt="Alt Text" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, after we have loaded the depth map, we can create masks for different layers by thresholding the depth map at different Thresholds.&lt;br&gt;
While making one layer we need two masks, one of this layer and the second of the previous layer to inpaint the missing parts. We will take the last layer outside the loop so we can extract all the remaining parts in this layer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="n"&gt;layers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;     
&lt;span class="n"&gt;prev_thres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;
&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;thres&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;        
   &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depth_map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thres&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;THRESH_BINARY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prev_mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depth_map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prev_thres&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;THRESH_BINARY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  

   &lt;span class="n"&gt;prev_thres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;thres&lt;/span&gt;        
   &lt;span class="n"&gt;inpaint_img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inpaint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prev_mask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INPAINT_NS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;layer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bitwise_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inpaint_img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inpaint_img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
   &lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;conv_cv_alpha&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  

&lt;span class="c1"&gt;# adding last layer 
&lt;/span&gt;
&lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zeros&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depth_map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uint8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    
&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;[:,:]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;   

&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prev_mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depth_map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prev_thres&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;THRESH_BINARY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;inpaint_img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inpaint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prev_mask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INPAINT_NS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="n"&gt;layer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bitwise_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inpaint_img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inpaint_img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;conv_cv_alpha&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;layers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;[::&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have reversed the layers so we can arrange them in order of &lt;b&gt;last to the first layer&lt;/b&gt;. While we are adding the layer to the list, we are using a function &lt;b&gt;‘conv_cv_alpha’&lt;/b&gt; this will add the alpha value &lt;b&gt;(make RGB to RGBA)&lt;/b&gt; and make parts of the layer transparent using the mask.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;conv_cv_alpha&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cv_image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;    
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cv_image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    
    &lt;span class="n"&gt;rgba&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;    
    &lt;span class="n"&gt;cv_image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cv_image&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now comes the part of face detection and showing the image. For face detection, we will use &lt;a href="http://www.willberger.org/cascade-haar-explained/#:~:text=Haar%20Cascade%20is%20a%20machine,of%20Simple%20Features%22%20in%202001." rel="noopener noreferrer"&gt;haarcascade&lt;/a&gt;. download them from their official &lt;a href="https://github.com/opencv/opencv/blob/master/data/haarcascades/haarcascade_frontalface_default.xml" rel="noopener noreferrer"&gt;Github repository&lt;/a&gt;.&lt;br&gt;
To download them, right-click “Raw” =&amp;gt; “Save link as”. Make sure they are in your working directory.&lt;br&gt;
Now we will load haar cascade for face detection and make a function that will return the face-rect from the image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;face_cascade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CascadeClassifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;haarcascade_frontalface_default.xml&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_face_rect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;    
    &lt;span class="n"&gt;gray_img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cvtColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COLOR_BGR2GRAY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    
    &lt;span class="n"&gt;face_rects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;face_cascade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectMultiScale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gray_img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;face_rects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         
        &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;face_rects&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have to show the image that will shift according to the user’s head. We will use &lt;b&gt;OpenCV&lt;/b&gt; to read cam and then &lt;b&gt;Pygame&lt;/b&gt; to render each frame on top of each other. To calculate the shift for each layer, we will calculate the shift of head from the center of the frame and then scale it down to get a small shift value. After that, we will multiply the index value of each layer to get the shift value for the respective layer, you can also multiply some constant value in that for better results.&lt;/p&gt;

&lt;p&gt;We will create a Pygame window slightly smaller than the original image and load the camera. We have used &lt;b&gt;scale&lt;/b&gt;, so you change its value to make the final result bigger.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;off_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get_width&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get_height&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;        &lt;span class="n"&gt;win&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_mode&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;off_set&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;off_set&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;    
&lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_caption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Parallax_image&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;scaled_layers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;    
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
             &lt;span class="n"&gt;scaled_layers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;span class="n"&gt;cap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;VideoCapture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CAP_DSHOW&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will set some constants. you can play with these constants to get different results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;x_transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;     &lt;span class="c1"&gt;# allow shift in x-axis
&lt;/span&gt;&lt;span class="n"&gt;y_transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;    &lt;span class="c1"&gt;# allow shift in y-axis
&lt;/span&gt;&lt;span class="n"&gt;sens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;              &lt;span class="c1"&gt;# the amount of scale down of shift value
&lt;/span&gt;&lt;span class="n"&gt;show_cam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;       &lt;span class="c1"&gt;# show your face cam
&lt;/span&gt;&lt;span class="n"&gt;shift_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;    
&lt;span class="n"&gt;shift_y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;    
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the main loop to render all layers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QUIT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cvtColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COLOR_BGR2RGB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;initial_pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;face_rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_face_rect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;face_rect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;face_rect&lt;/span&gt;
        &lt;span class="n"&gt;face_rect_frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rectangle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;shift_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initial_pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sens&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;shift_y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initial_pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sens&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;win&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scaled_layers&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;new_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;off_set&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="n"&gt;new_y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;off_set&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x_transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;new_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;shift_x&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;y_transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;new_y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;shift_y&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;
        &lt;span class="n"&gt;win&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 

   &lt;span class="n"&gt;face_rect_frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;face_rect_frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;show_cam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;win&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;conv_cv_pygame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;face_rect_frame&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
   &lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroyAllWindows&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There it is, the final result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F18mi97osh9d74x5zks6t.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F18mi97osh9d74x5zks6t.gif" alt="Alt Text" width="80" height="80"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdpov2wpgtwvupy86zgnw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdpov2wpgtwvupy86zgnw.gif" alt="Alt Text" width="80" height="53"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have created a more advanced version of this tool where you can just choose the image and it will automatically create the parallax image, the depth map will be automatically generated.&lt;br&gt;
You can check more on my &lt;a href="https://github.com/strikeraryu/Parallax_Image" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; repo.&lt;/p&gt;




&lt;p&gt;Feel free to contact me🔥. &lt;a href="https://www.linkedin.com/in/aryamaan-jain-9330a8190/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; &lt;a href="https://twitter.com/striker_aryu" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; &lt;a href="https://www.instagram.com/striker_aryu/?hl=en" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;br&gt;
You can check my other projects and stay tuned for more.👀&lt;/p&gt;

</description>
    </item>
    <item>
      <title>ReVamp — Virtual Renovation</title>
      <dc:creator>Aryamaan jain</dc:creator>
      <pubDate>Tue, 24 Nov 2020 12:01:16 +0000</pubDate>
      <link>https://dev.to/strikeraryu/revamp-virtual-renovation-4j7p</link>
      <guid>https://dev.to/strikeraryu/revamp-virtual-renovation-4j7p</guid>
      <description>&lt;p&gt;The virtual world is getting bigger. Everything from rocket simulation to grocery shopping is virtual now, and now a new thing is getting added to this list. Moving your furniture can be a tough job, and trying a new look on a virtual platform can give a new dimension and make it a little easier to do.&lt;/p&gt;

&lt;p&gt;There are a few layers when you wish to give your room a new look. Usually, you want to add new furniture or move the old one to a new location, but it gets tricky when you want a perfect setting for your furniture or wants to buy a new one that will perfectly fit in your room’s theme. But we can add a virtual element in this and make visualization easier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7dlcv3zd3il5olnoh3rp.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7dlcv3zd3il5olnoh3rp.gif" alt="Alt Text" width="498" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What ReVamp brings to the table?
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;ReVamp&lt;/strong&gt;, you can make a virtual element of your furniture using your camera. You can click on the object, and it will create a virtual overlay that you can move using your camera. It also allows you to use external images so you can use pictures of furniture you wish to buy to make the virtual overlay.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6x02k4zuzyltbxs3kfnm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6x02k4zuzyltbxs3kfnm.png" alt="Alt Text" width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Technical view of ReVamp
&lt;/h3&gt;

&lt;p&gt;I was working on this project and finished a software version of &lt;strong&gt;ReVamp&lt;/strong&gt; using &lt;strong&gt;&lt;a href="https://www.python.org/" rel="noopener noreferrer"&gt;Python&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fm3goov2khh93bulhg59t.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fm3goov2khh93bulhg59t.gif" alt="ReVamp Demo" width="80" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Let’s see how ReVamp works.
&lt;/h4&gt;

&lt;p&gt;I used &lt;a href="https://docs.opencv.org/master/d6/d00/tutorial_py_root.html" rel="noopener noreferrer"&gt;OpenCV&lt;/a&gt; to get the camera feed of the device or the IP cam for better quality. Using this, I get a continuous stream of frames that I use to extract the object.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbe69tygcbi6scopiczed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbe69tygcbi6scopiczed.png" alt="Alt Text" width="800" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Extracting the object from a picture is tricky so, I tried different approaches to do that.&lt;/p&gt;

&lt;h4&gt;
  
  
  Flood fill with the color difference
&lt;/h4&gt;

&lt;p&gt;I tried the flood fill algorithm with different color difference formula.&lt;br&gt;
I used Euclidean distance and Red Mean. Euclidean distance was better for computer-generated images, and Red Mean was good for the pictures taken from the camera.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fyzeron25v3h5qeo3f4yb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fyzeron25v3h5qeo3f4yb.png" alt="Euclidean distance" width="800" height="35"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8jlfrodxisrpvg29fdpb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8jlfrodxisrpvg29fdpb.png" alt="Red Mean" width="800" height="109"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All over the result were not as good as the &lt;a href="https://photographylife.com/what-is-noise-in-photography" rel="noopener noreferrer"&gt;noise in the picture&lt;/a&gt; is left out.&lt;/p&gt;

&lt;h4&gt;
  
  
  Adding edge Detection
&lt;/h4&gt;

&lt;p&gt;The next big step in the results was adding edge-detection. Now to extract an object, I run a flood fill algorithm with low Delta-e on edge and, then I draw the contour on the resulted CV-MAT. Finally, we get a mask of the object, and applying that on the original image gives us the required virtual overlay of the object.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk5q86zivprp2a3qdwe7v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk5q86zivprp2a3qdwe7v.png" alt="Results after adding edge Detection" width="788" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There were a few flaws in this approach.&lt;/p&gt;

&lt;h4&gt;
  
  
  Adding convolution filters and contour smoothing
&lt;/h4&gt;

&lt;p&gt;Adding convolution filters and contour smoothing removes the flaw in the previous approach. Before using the above approach, I preprocess the image with a different convolution filter. I tried many filters and, smooth + edge enhance works the best as it removes the noise and sharpens the edge. After this, I also added contour smoothing while creating the mask.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbtbgw9wwtcochlzq2n5k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbtbgw9wwtcochlzq2n5k.png" alt="Results after adding convolution filters and contour smoothing" width="778" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The results were fine on different images. Still, there were outliers images where results were not up to the mark. While I was trying different approaches, I saw an idea similar to mine getting popular. So I started looking at the technologies used in that. There I found &lt;a href="https://github.com/NathanUA/BASNet" rel="noopener noreferrer"&gt;BASNet&lt;/a&gt;, which provides very good results on object detection and segmentation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Using BASNet
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/NathanUA/BASNet" rel="noopener noreferrer"&gt;BASNet&lt;/a&gt;&lt;/strong&gt; is a deep Convolutional neural network used for salient object detection and accurately predict the fine structures with clear boundaries.&lt;br&gt;
Using BASNet gave the best results for creating the overlay.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Deep Convolutional Neural Networks have been adopted for salient object detection and achieved the state-of-the-art performance. Most of the previous works however focus on region accuracy but not on the boundary quality.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7jt6m2nggyqk8qgh02t6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7jt6m2nggyqk8qgh02t6.png" alt="Alt Text" width="774" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And finally, the tricky part of the ReVamp was ready. Now I have to implement this in ReVamp and create a usable tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffrvtbhyajbmkwu7pllki.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffrvtbhyajbmkwu7pllki.gif" alt="Alt Text" width="498" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Interface/UI
&lt;/h3&gt;

&lt;p&gt;Now to create an interface, I used the Pygame library of python, I didn’t use other interfacing libraries like Tkinter as I want a little flexible UI with a different feature. So, I create my UI library for Pygame that provided me the flexibility I needed. Further, I used multi treading to make the flow of ReVamp seamless.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq6x03gjtjzl60s5yooy9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq6x03gjtjzl60s5yooy9.png" alt="ReVamp Demo" width="800" height="235"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3uwm45sqjqryk98u6wfc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3uwm45sqjqryk98u6wfc.png" alt="Using external images" width="800" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I used different techniques to provide the basic functionality of ReVamp like using external images to create the overlay, save/load overlay, basic camera function, and some basic functions on the overlay.&lt;/p&gt;

&lt;h5&gt;
  
  
  You can check more about ReVamp
&lt;/h5&gt;

&lt;p&gt;&lt;a href="https://github.com/strikeraryu/ReVamp" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>python</category>
      <category>augmentedreality</category>
      <category>renovation</category>
    </item>
  </channel>
</rss>
