Computer >> कंप्यूटर ट्यूटोरियल >  >> प्रोग्रामिंग >> Redis

Replicate, Next.js, और Upstash का उपयोग करके फोटो रेस्टोरेशन ऐप विकसित करें

इस ब्लॉग पोस्ट के लिए, हम जारी रखने से पहले कुछ धारणाएं बनाएंगे, लेकिन आपके पास आदर्श रूप से होना चाहिए:

  • एक Upstash अकाउंट जहां आपने एक Redis इंस्टेंस बनाया है
  • अपने API टोकन तक पहुंच के साथ एक Replicate अकाउंट
  • हमारी वांछित कार्यक्षमता को लागू करने के लिए एक Next.js प्रोजेक्ट
  • अपने प्रोजेक्ट को डेप्लॉय करने के लिए एक Vercel अकाउंट

यह क्या है?

क्या आप Replicate पर उपलब्ध मॉडल का उपयोग करके मशीन लर्निंग का उपयोग करके इमेज जेनरेट करना शुरू करना चाहते हैं? खैर, इस ट्यूटोरियल में, हम Replicate की होस्टेड मॉडल की विस्तृत श्रृंखला और Upstash के Redis का पता लगाएंगे। न केवल हम इन मॉडल का पता लगाएंगे, बल्कि हम एक को सेटअप करने की प्रक्रिया के माध्यम से चलेंगे, और इस पर भी चर्चा करेंगे कि आप अन्य मॉडल का उपयोग करने के लिए कार्यान्वयन को आसानी से कैसे अपडेट कर सकते हैं।

इस ट्यूटोरियल में, हम Microsoft के Bringing Old Photos Back to Life मॉडल के उपयोग को कवर करेंगे, जो अनिवार्य रूप से एक पुरानी तस्वीर लेता है, इसे मॉडल के माध्यम से चलाता है, और आपकी तस्वीर का एक संपादित, और उम्मीद है कि बेहतर संस्करण आउटपुट करता है।

Replicate, Next.js, और Upstash का उपयोग करके फोटो रेस्टोरेशन ऐप विकसित करें

आर्किटेक्चर क्या है?

यदि आपके पास कुछ React अनुभव है, तो आपको कोडबेस के माध्यम से पढ़कर ऐप आर्किटेक्चर कैसे काम करता है यह निर्धारित करने में सक्षम होना चाहिए, लेकिन इसे थोड़ा आसान बनाने के लिए, या यदि आप केवल एक अवलोकन देखना पसंद करते हैं, तो नीचे एक प्रदान किया गया है।

Replicate, Next.js, और Upstash का उपयोग करके फोटो रेस्टोरेशन ऐप विकसित करें

शुरू करने के लिए मुझे क्या चाहिए?

शुरू करने के लिए, आपको निश्चित रूप से एक Next.js प्रोजेक्ट की आवश्यकता होगी। यह Next.js सेटअप गाइड का पालन करके किया जा सकता है, या यदि आपके पास पहले से एक सेटअप है, तो यह भी ठीक है। इस ट्यूटोरियल में, हम Tailwind CSS का भी उपयोग कर रहे हैं, लेकिन आप निश्चित रूप से किसी भी स्टाइलिंग सेटअप का उपयोग कर सकते हैं जिसे आप पसंद करते हैं।

अब जब हमारे पास एक बेसिक Next.js प्रोजेक्ट सेटअप है, तो हम कमांड चलाकर Upstash की Redis लाइब्रेरी इंस्टॉल कर सकते हैं:

npm install @upstash/redis

अब, हम अपनी .env.local फाइल को निम्न कुंजियों के साथ भरना चाहेंगे, जिनमें से Redis टोकन आपके Upstash कंसोल में पाया जा सकता है, Replicate API टोकन यहां आपके अकाउंट के तहत, और आपकी साइट URL जहां भी आप इसे डेप्लॉय करते हैं, इस मामले में यह Vercel डेप्लॉयमेंट एंडपॉइंट होगा।

SITE_URL=https://your-project-url.vercel.app
REPLICATE_API_TOKEN=
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=

फ्रंटएंड फॉर्म सेटअप करना

शुरू करने के लिए, हमें एक फॉर्म की आवश्यकता होगी जो फॉर्म, पोलिंग और पूर्ण इमेज के प्रदर्शन को संभालता है।

रिस्टोर इमेज फॉर्म निर्माण

फाइल: pages/index.tsx

import { MouseEvent, RefObject, useRef, useState } from "react";
import Head from "next/head";
import useInterval from "../hooks/useInterval";
export default function Home() {
  const [restoring, setRestoring] = useState<boolean>(false);
  const [messageId, setMessageId] = useState<string | null>(null);
  const [prediction, setPrediction] = useState<any>({});
  const [outputImageUrl, setOutputImageUrl] = useState<string | null>(null);
  const imageUrlRef: RefObject<HTMLInputElement> = useRef(null);
  const hrRef: RefObject<HTMLInputElement> = useRef(null);
  const scratchRef: RefObject<HTMLInputElement> = useRef(null);
  useInterval(
    async () => {
      await fetch(`/api/poll?id=${messageId}`)
        .then((res: any) => res.json())
        .then((data: any) => {
          if (!data.output) {
            return;
          }
          setRestoring(false);
          setMessageId(null);
          setOutputImageUrl(data.output);
        })
        .catch((err: any) => console.error(err));
    },
    messageId ? 1000 : null,
  );
  async function restoreImage(e: any) {
    e.preventDefault();
    setRestoring(true);
    await fetch("/api/create", {
      method: "POST",
      body: JSON.stringify({
        image_url: imageUrlRef.current?.value,
        is_hr: hrRef.current?.value,
        has_scratches: scratchRef.current?.value,
      }),
      headers: {
        "Content-Type": "application/json",
      },
    })
      .then((res: Response) => res.json())
      .then((data: any) => {
        setMessageId(data.data.id);
        setPrediction(data.data);
      })
      .catch((err: Error) => console.error(err));
  }
  async function cancel(e: MouseEvent<HTMLButtonElement>) {
    e.preventDefault();
    await fetch("/api/cancel", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ cancel_url: prediction.urls.cancel }),
    })
      .then((res: Response) => res.json())
      .then((data: any) => {
        setMessageId(null);
        setPrediction({});
        setRestoring(false);
      })
      .catch((err: Error) => console.error(err));
  }
  return (
    <>
      <Head>
        <title>PhotoRescue</title>
        <meta name="description" content="A simple Next.js application that utilizes Replicate to restore old photos." />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main>
        <div className="my-16 flex flex-col items-center justify-center md:my-32">
          <h1 className="text-5xl font-black">PhotoRescue</h1>
          <p className="mt-4">Restore your old photos to their former glory.</p>
          {outputImageUrl && (
            <div className="flex flex-col items-center justify-center">
              <img src={outputImageUrl} alt="Restored Image" className="mt-8 h-auto w-72" />
              <button type="button" onClick={() => setOutputImageUrl(null)} className="mt-8 inline-flex items-center rounded-full border border-transparent bg-gray-900 px-6 py-2.5 text-sm font-medium text-white shadow-sm hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-600 focus:ring-offset-2 disabled:opacity-50">
                Start Again
              </button>
            </div>
          )}
          {!outputImageUrl && (
            <form onSubmit={restoreImage} className="mt-10 flex w-full max-w-lg flex-col items-center">
              <div className="w-full space-y-4">
                <div>
                  <label htmlFor="image_url" className="text-sm font-semibold">
                    Image URL
                  </label>
                  <input name="image_url" id="image_url" type="text" defaultValue="https://replicate.delivery/mgxm/b033ff07-1d2e-4768-a137-6c16b5ed4bed/d_1.png" placeholder="https://example.com/image.png" className="mt-0.5 block w-full rounded-md border border-gray-300 p-2 shadow-sm focus:border-gray-500 focus:ring-gray-500" ref={imageUrlRef} required />
                </div>
                <div className="max-w-lg space-y-4">
                  <div className="relative flex items-start">
                    <div className="flex h-5 items-center">
                      <input name="is_hr" id="is_hr" type="checkbox" className="h-4 w-4 rounded border-gray-300 text-gray-900 focus:ring-gray-500" ref={hrRef} />
                    </div>
                    <div className="ml-3 text-sm">
                      <label htmlFor="is_hr" className="font-medium text-gray-900">
                        Is High Resolution?
                      </label>
                      <p className="text-gray-500">
                        Check this if the input image is a high resolution photo.
                      </p>
                    </div>
                  </div>
                  <div className="relative flex items-start">
                    <div className="flex h-5 items-center">
                      <input name="is_scratched" id="is_scratched" type="checkbox" className="h-4 w-4 rounded border-gray-300 text-gray-900 focus:ring-gray-500" ref={scratchRef} defaultChecked={true} />
                    </div>
                    <div className="ml-3 text-sm">
                      <label htmlFor="is_scratched" className="font-medium text-gray-900">
                        Has Scratches?
                      </label>
                      <p className="text-gray-500">
                        Check this if the input image has visible scratches over it.
                      </p>
                    </div>
                  </div>
                </div>
              </div>
              <div className="mt-6 flex gap-2">
                <button type="submit" disabled={restoring} className="inline-flex items-center rounded-full border border-transparent bg-gray-900 px-6 py-2.5 text-sm font-medium text-white shadow-sm hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-600 focus:ring-offset-2 disabled:opacity-50">
                  {restoring ? "Restoring..." : "Restore"}
                </button>
                {restoring && prediction && (
                  <button type="button" onClick={cancel} className="inline-flex items-center rounded-full border border-gray-900 bg-white px-6 py-2.5 text-sm font-medium text-gray-900 shadow-sm hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-600 focus:ring-offset-2">
                    Cancel
                  </button>
                )}
              </div>
            </form>
          )}
        </div>
      </main>
    </>
  );
}

डिफ़ॉल्ट रूप से, यह कंपोनेंट एक फॉर्म प्रदर्शित करता है जो यूजर को उस इमेज का URL दर्ज करने की अनुमति देता है जिसे वे रिस्टोर करना चाहते हैं, और इसके साथ जाने के लिए कुछ विकल्प जैसे कि इमेज हाई रिज़ॉल्यूशन है या नहीं, या इमेज में खरोंच है जिसे हटाने की आवश्यकता है। एक बार जब यूजर यह जानकारी भरता है, और फॉर्म सबमिट करता है, तो यह फॉर्म डेटा के साथ /api/create पर एक POST अनुरोध भेजता है।

एक बार जब यह अनुरोध API को भेजा जाता है और प्रतिक्रिया प्राप्त होती है जिसमें वापस की गई प्रेडिक्शन जानकारी होती है, तो कंपोनेंट एक पोलिंग स्थिति में प्रवेश करता है जो /api/poll पर एक GET अनुरोध भेजता है प्रति सेकंड एक बार यह जांचने के लिए कि क्या प्रेडिक्शन अभी तक पूरा हो चुका है। एक बार जब एक पोलिंग अनुरोध एक सफल प्रतिक्रिया लौटाता है, जो दर्शाता है कि Replicate ने हमारे कॉलबैक एंडपॉइंट पर एक अनुरोध भेजा है, तो हमारे पास अब प्रेडिक्शन आउटपुट तक पहुंच होगी।

जबकि पोलिंग जारी है, फॉर्म प्रेडिक्शन को कैंसिल करने के विकल्प के साथ एक बटन प्रदर्शित करता है। एक बार दबाने पर, यह /api/cancel पर एक POST अनुरोध भेजता है जिसमें प्रेडिक्शन डेटा से cancel_url होता है जो हमें निर्माण पर प्रारंभिक अनुरोध पर प्राप्त हुआ था।

पोलिंग कार्यान्वयन hooks/useInterval.ts में स्थित एक कस्टम हुक का उपयोग करता है जो हमें React के कंपोनेंट लाइफसाइकल के साथ आसानी से और निर्बाध रूप से काम करने की अनुमति देता है, और किसी भी दिए गए React कंपोनेंट के भीतर कॉलबैक के साथ अंतराल को संभालने का एक अधिक सुविधाजनक तरीका प्रदान करता है। आप इसके बारे में यहां और यहां पढ़ सकते हैं यदि आप इसके बारे में अधिक विस्तार से जानना चाहते हैं।

import { useEffect, useRef } from "react";
function useInterval(callback: () => void, delay: number | null) {
  const savedCallback = useRef(callback);
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);
  useEffect(() => {
    if (!delay && delay !== 0) {
      return;
    }
    const id = setInterval(() => savedCallback.current(), delay);
    return () => clearInterval(id);
  }, [delay]);
}
export default useInterval;

API सेटअप

API सेटअप, कुछ फाइलों से बना है, जो हमें प्रेडिक्शन बनाने और कैंसिल करने, प्रेडिक्शन पूरा होने पर जांच करने के लिए पोल करने, साथ ही कॉलबैक निर्दिष्ट करने की अनुमति देता है जिसका उपयोग Replicate तब करेगा जब उनकी तरफ प्रेडिक्शन पूरा हो जाए।

इमेज प्रेडिक्शन निर्माण

फाइल: pages/api/create.ts

import type { NextApiRequest, NextApiResponse } from "next";
import fetch, { Response } from "node-fetch";
import redis from "../../lib/redis";
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  if (req.method !== "POST") {
    return res.status(400).json({ message: `Invalid request method: ${req.method}.`, });
  }
  const { image_url, is_hr, has_scratches }: any = req.body;
  await fetch("https://api.replicate.com/v1/predictions", {
    method: "POST",
    headers: {
      Authorization: `Token ${process.env.REPLICATE_API_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      version: "c75db81db6cbd809d93cc3b7e7a088a351a3349c9fa02b6d393e35e0d51ba799",
      input: {
        image: image_url,
        HR: is_hr,
        with_scratch: has_scratches,
      },
      webhook_completed: `${process.env.SITE_URL}/api/callback`,
    }),
  })
    .then((res: Response) => res.json())
    .then(async (data: any) => {
      await redis.set(data.id, data);
      return res.status(202).json({ data: data });
    })
    .catch((error: Error) => {
      return res.status(500).json({ message: error.message });
    });
}

हमारे क्रिएट API एंडपॉइंट के लिए, हम पहले यह सुनिश्चित करने के लिए एक साधारण जांच करते हैं कि इनकमिंग अनुरोध मेथड एक POST अनुरोध है, और यदि नहीं तो हम एक साधारण 400 प्रतिक्रिया वापस करेंगे। फिर हम अपने Replicate API टोकन के साथ Replicate पर एक POST अनुरोध भेजने के लिए आगे बढ़ते हैं। अनुरोध बॉडी में दिए गए मॉडल version के पैरामीटर होते हैं जो इंगित करता है कि हम किस मॉडल को अनुरोध भेज रहे हैं (यह आपके द्वारा उपयोग किए जाने वाले मॉडल पर "API" टैब के तहत पाया जाता है)। हम फ्रंटएंड पर फॉर्म से डेटा के साथ मॉडल से जुड़े पैरामीटर भी पास करते हैं।

एक बार अनुरोध भेजे जाने के बाद, हम वापस की गई प्रेडिक्शन id का उपयोग इसे Redis में संग्रहीत करने के लिए करते हैं, और फ्रंटएंड को प्रेडिक्शन डेटा वापस करते हैं ताकि Redis आइटम को पोल किया जा सके जब तक कि इसमें एक पूर्ण प्रेडिक्शन न हो।

कॉलबैक

फाइल: pages/api/callback.ts

import type { NextApiRequest, NextApiResponse } from "next";
import redis from "../../lib/redis";
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  const { body }: any = req;
  try {
    await redis.set(body.id, body);
    return res.status(200).send(body);
  } catch (error) {
    return res.status(500).json({ error });
  }
}

कॉलबैक एंडपॉइंट वह है जिस पर Replicate एक POST अनुरोध भेजेगा ताकि हमें पता चले कि दी गई प्रेडिक्शन का प्रसंस्करण समाप्त हो गया है। जब हम यह अनुरोध प्राप्त करते हैं, तो हम अनुरोध बॉडी से प्रेडिक्शन डेटा प्राप्त करते हैं, और पूर्ण प्रेडिक्शन डेटा के साथ दिए गए Redis आइटम को अपडेट करते हैं।

पोलिंग

फाइल: pages/api/poll.ts

import type { NextApiRequest, NextApiResponse } from "next";
import redis from "../../lib/redis";
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  const { id }: any = req.query;
  try {
    const data = await redis.get(id);
    if (!data) {
      return res
        .status(404)
        .json({ message: "Data for supplied ID not found" });
    }
    return res.status(200).json(data);
  } catch (error: any) {
    return res.status(500).json({ message: error.message });
  }
}

हमारे पोलिंग सेटअप के लिए, हम अनुरोध से id निकालते हैं और फिर उस पहचानकर्ता के तहत Redis में संग्रहीत डेटा को पुनर्प्राप्त करने का प्रयास करते हैं, और यदि कोई डेटा नहीं मिलता है, तो हम 404 प्रतिक्रिया वापस करते हैं, लेकिन यदि डेटा है, तो हम कहा डेटा को 200 प्रतिक्रिया के हिस्से के रूप में वापस करते हैं।

कैंसिल

फाइल: pages/api/cancel.tsx

import type { NextApiRequest, NextApiResponse } from "next";
import fetch, { Response } from "node-fetch";
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  if (req.method !== "POST") {
    return res.status(400).json({ message: `Invalid request method: ${req.method}.`, });
  }
  const { cancel_url }: any = req.body;
  await fetch(cancel_url, {
    method: "POST",
    headers: {
      Authorization: `Token ${process.env.REPLICATE_API_TOKEN}`,
      "Content-Type": "application/json",
    },
  })
    .then((res: Response) => res.json())
    .then((data: any) => {
      return res.status(202).json({ data: data });
    })
    .catch((error: Error) => {
      return res.status(500).json({ message: error.message });
    });
}

शुरू की गई प्रेडिक्शन को कैंसिल करने के लिए API एंडपॉइंट काफी सीधा है। हम बस cancel_url निकालते हैं जिसे फ्रंटएंड से पास किया जाता है जो बदले में उस प्रेडिक्शन से आता है जो तब संग्रहीत किया गया था जब एक निर्माण अनुरोध सबमिट किया गया था, और हम बस उस एंडपॉइंट पर अपने Replicate API टोकन के साथ एक POST अनुरोध भेजते हैं।

लाइब्रेरी

हमारे लाइब्रेरी के लिए, हम एक Redis क्लाइंट बनाएंगे जिसका उपयोग ट्रैकिंग में किया जाता है

फाइल: lib/redis.ts

import { Redis } from "@upstash/redis";
const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL as string,
  token: process.env.UPSTASH_REDIS_REST_TOKEN as string,
});
export default redis;

इस ऑब्जेक्ट का उपयोग एप्लिकेशन के भीतर डेटा संग्रहीत और पुनर्प्राप्त करने के लिए किया जाएगा जब पोलिंग की जा रही हो, ताकि हमें पता चले कि Replicate से वेबहुक पूरा कब हुआ।

निष्कर्ष

Replicate के पास API के माध्यम से उपयोग किए जा सकने वाले विभिन्न प्रकार के मॉडल उपलब्ध हैं। Vercel और Upstash के साथ, मशीन लर्निंग मॉडल का उपयोग करना और उपयोग योग्य वेब एप्लिकेशन डेप्लॉय करना पहले से कहीं अधिक आसान है।

यदि आप पूरा रिपॉजिटरी देखना चाहते हैं, तो आप इसे यहां एक्सेस कर सकते हैं।

आगे विकास

यह Replicate के साथ एक काफी सरल मॉडल का उपयोग करने का सिर्फ एक सरल उदाहरण है। केवल फॉर्म पैरामीटर्स और API में वर्जन को बदलकर, आप आसानी से दूसरे मॉडल पर स्विच कर सकते हैं जब तक आपके पास आपका Replicate API टोकन लिंक है, आप उपलब्ध किसी भी मॉडल का उपभोग कर सकेंगे।

आप यहां Replicate के सभी उपलब्ध मॉडल का पता लगा सकते हैं, और एक बार जब आप एक ऐसा मॉडल ढूंढ लेते हैं जिसे आप आज़माना चाहते हैं, तो आप इसके उपयोग को देखने के लिए "API" टैब पर क्लिक कर सकते हैं। यहां आपको Python, cURL, Cog और Docker के लिए बटन भी मिलेंगे, जो आपको मॉडल को टेस्ट करने की अनुमति देते हैं, लेकिन यह यह जानने के लिए भी उपयोगी है कि कौन से पैरामीटर आवश्यक हैं, और उन्हें कैसे भेजा जाता है।

  1. HTML DOM स्टाइल textDecorationLine प्रॉपर्टी HTML DOM स्टाइल textDecorationLine प्रॉपर्टी

    परिचय HTML DOM स्टाइल textDecorationLine प्रॉपर्टी HTML दस्तावेज़ में किसी एलिमेंट के टेक्स्ट को सजाने वाली लाइन के प्रकार को प्राप्त करती है और संशोधित करती है। सिंटैक्स मान प्राप्त करना object.style.textDecorationLine मान सेट करना object.style.textDecorationLine = value मान मानव्याख्या inhe

  1. पायथन का उपयोग करके Matplotlib से 3D स्कैटर प्लॉट कैसे बनाएं पायथन का उपयोग करके Matplotlib से 3D स्कैटर प्लॉट कैसे बनाएं

    Matplotlib डेटा विज़ुअलाइज़ेशन के लिए एक लोकप्रिय पायथन पैकेज है। डेटा को विज़ुअलाइज़ करना एक महत्वपूर्ण कदम है क्योंकि यह संख्याओं को देखे बिना और जटिल गणना किए बिना डेटा में क्या हो रहा है, यह समझने में मदद करता है। यह दर्शकों को मात्रात्मक अंतर्दृष्टि को प्रभावी ढंग से संप्रेषित करने में मदद करता

  1. Tkinter में Treeview पर मल्टीसेलेक्शन कैसे डिसेबल करें? Tkinter में Treeview पर मल्टीसेलेक्शन कैसे डिसेबल करें?

    Treeview विजेट का उपयोग स्तंभों के रूप में एक से अधिक सुविधा के साथ आइटम की सूची प्रदर्शित करने के लिए किया जाता है। डिफ़ॉल्ट रूप से, Treeview विजेट में सूचीबद्ध आइटम को कई बार चुना जा सकता है, हालांकि आप Treeview विजेट कंस्ट्रक्टर में selectmode=browse का उपयोग करके इस सुविधा को अक्षम कर सकते हैं।