{"id":413600,"date":"2024-06-29T23:13:43","date_gmt":"2024-06-29T23:13:43","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=413600"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-06-29T23:13:43","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=413600","title":{"rendered":"<span>\u0424\u043e\u0442\u043e\u0433\u0430\u043b\u0435\u0440\u0435\u044f \u043d\u0430 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u043a\u0430\u0445<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>~1 \u0433\u043e\u0434 \u043d\u0430\u0437\u0430\u0434 \u044f \u043d\u0430\u0447\u0430\u043b \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u0444\u043e\u0442\u043e\u0433\u0430\u043b\u0435\u0440\u0435\u044e (<s>\u043f\u0435\u0441\u043e\u0447\u043d\u0438\u0446\u0443 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0430 \u0432\u0441\u044f\u043a\u0438\u0445 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439<\/s>). \u0414\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u2013 \u044d\u0442\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0435\u0451 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0442\u0432\u0438\u043a\u0438\/\u043b\u0430\u0439\u0444\u0445\u0430\u043a\u0438\/\u043c\u0438\u043a\u0440\u043e\u0433\u0430\u0439\u0434\u044b \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u044f \u0443\u0437\u043d\u0430\u043b \u0437\u0430 \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 + \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c. <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/7bb\/8a2\/ec7\/7bb8a2ec71139e776b117fd5147894e5.png\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/7bb\/8a2\/ec7\/7bb8a2ec71139e776b117fd5147894e5.png\"\/><figcaption><\/figcaption><\/figure>\n<hr\/>\n<h3>\u041c\u043e\u043d\u043e\u043b\u0438\u0442<\/h3>\n<p>\u0412\u043d\u0430\u0447\u0430\u043b\u0435 \u0431\u044b\u043b \u043c\u043e\u043d\u043e\u043b\u0438\u0442.  \u042f \u043f\u044b\u0442\u0430\u043b\u0441\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0432\u0441\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u043d\u0430 javascript \u2013 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u0431\u0438\u043d\u0434\u0438\u043d\u0433\u0438 OpenCV \u0434\u043b\u044f js, \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0434\u043b\u044f \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f phash \u043d\u0430 js, \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435. \u041f\u043e\u0441\u043b\u0435 \u043d\u0435\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0432\u043e\u0439\u043d\u044b \u0441 \u0443\u0442\u0435\u0447\u043a\u0430\u043c\u0438 \u043f\u0430\u043c\u044f\u0442\u0438, \u043f\u043b\u043e\u0445\u043e\u0439 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e \u0438 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u044b\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u043e\u043c, \u0431\u044b\u043b\u043e \u0440\u0435\u0448\u0435\u043d\u043e \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u043f\u043e\u0438\u0441\u043a \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u043d\u0430 python. \u0422\u0430\u043a \u043f\u0440\u043e\u0435\u043a\u0442 \u043e\u0431\u0440\u0435\u043b \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443:<\/p>\n<ul>\n<li>\n<p>scenery \u2013 frontend, backend<\/p>\n<\/li>\n<li>\n<p>ambience \u2013 \u0412\u0441\u0435 \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u043e\u0435 \u0441 \u043f\u043e\u0438\u0441\u043a\u043e\u043c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439<\/p>\n<\/li>\n<li>\n<p>crud_file_server \u2013 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u043b\u044f \u0431\u0435\u043a\u0430\u043f\u0430 \u0438 ipfs. <\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d20\/fbb\/ce0\/d20fbbce0a6088d94e168a4e0098ffd8.png\" alt=\"\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442\" title=\"\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442\" width=\"4564\" height=\"2288\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d20\/fbb\/ce0\/d20fbbce0a6088d94e168a4e0098ffd8.png\"\/><figcaption>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442<\/figcaption><\/figure>\n<p>Nginx \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 reverse proxy \u0441\u0435\u0440\u0432\u0435\u0440\u0430. \u0422\u0430\u043c \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u0435 (https), \u043a\u043e\u043c\u043f\u0440\u0435\u0441\u0441\u0438\u044f (gzip\/brotli) \u0438 \u0440\u0430\u0437\u0434\u0430\u0447\u0430 \u0441\u0442\u0430\u0442\u0438\u043a\u0438, \u0447\u0442\u043e\u0431\u044b \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u0438\u0442\u044c Node.js.<\/p>\n<h2>nginx<\/h2>\n<p>\u041f\u0430\u0440\u0430 \u0442\u0432\u0438\u043a\u043e\u0432 \u0434\u043b\u044f nginx.<\/p>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c cloudflare, \u0440\u0430\u0437\u0443\u043c\u043d\u043e \u0437\u0430\u043f\u0440\u0435\u0442\u0438\u0442\u044c \u0432\u0441\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0434\u0443\u0442 \u043d\u0435 \u043e\u0442 cloudflare. \u0421\u043a\u0440\u0438\u043f\u0442 <a href=\"https:\/\/gist.github.com\/mkg20001\/1a4f0a5a3f55c194cc4998019edfa62a\" rel=\"noopener noreferrer nofollow\">allow_cloudflare.sh<\/a> \u0441\u043e\u0437\u0434\u0430\u0435\u0442 allow-cloudflare-only.conf, whitelist \u0441\u043e\u0441\u0442\u043e\u044f\u0449\u0438\u0439 \u0438\u0437 ip \u0430\u0434\u0440\u0435\u0441\u043e\u0432 cloudflare. \u041e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0433\u043e \u0437\u0430\u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0442\u044c.<\/p>\n<pre><code class=\"nginx\">include \/etc\/nginx\/allow-cloudflare-only.conf;<\/code><\/pre>\n<p>Next.js \u043f\u0440\u0438 \u0441\u0431\u043e\u0440\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0445\u0435\u0448 \u043a \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f\u043c \u0444\u0430\u0439\u043b\u043e\u0432, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u0447\u0442\u043e \u0444\u0430\u0439\u043b \u0441 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u043c \u0438\u043c\u0435\u043d\u0435\u043c \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u043f\u043e\u043c\u0435\u043d\u044f\u0435\u0442 \u0441\u0432\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435. \u042d\u0442\u0443 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0438\u044f \u0441\u0430\u0439\u0442\u0430 \u2013 \u0434\u043b\u044f \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0444\u0430\u0439\u043b\u043e\u0432 \u0432\u044b\u0441\u0442\u0430\u0432\u0438\u043c \u0431\u043e\u043b\u044c\u0448\u043e\u0439 max-age \u0438 \u0430\u0442\u0440\u0438\u0431\u0443\u0442 immutable, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0443 \u043d\u0435 \u0440\u0435\u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u0435\u0448. \u041a\u043b\u0430\u0441\u0441\u043d\u044b\u0439 \u0430\u0442\u0440\u0438\u0431\u0443\u0442, \u043a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, <a href=\"https:\/\/caniuse.com\/?search=immutable\" rel=\"noopener noreferrer nofollow\">\u043c\u0430\u043b\u043e \u0433\u0434\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f<\/a>.<\/p>\n<details class=\"spoiler\">\n<summary>\u0444\u0430\u0439\u043b\u044b<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/837\/e68\/2fe\/837e682fec82444f46a7eddd335ed245.png\" width=\"453\" height=\"601\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/837\/e68\/2fe\/837e682fec82444f46a7eddd335ed245.png\"\/><figcaption><\/figcaption><\/figure>\n<\/div>\n<\/details>\n<pre><code class=\"nginx\">expires 1y; add_header Cache-Control \"public, immutable\";<\/code><\/pre>\n<h2>scenery<\/h2>\n<p>\u0422\u0435\u043a\u0443\u0449\u0438\u0439 \u0441\u0442\u0435\u043a: Node.js + MongoDB + TypeScript + Next.js + Fastify + Material UI<\/p>\n<p>\u042f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e Next.js, \u0442.\u043a \u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u044d\u0442\u043e \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0439 \u0438 \u0443\u0434\u043e\u0431\u043d\u044b\u0439 react \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 CSR(Client-side rendering), SSR (server-side rendering) \u0438 ISR(Incremental Static Regeneration). \u041f\u043e \u0441\u0443\u0442\u0438, ISR \u044d\u0442\u043e SSR \u0441 \u043a\u0435\u0448\u0435\u043c, \u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u0434\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 revalidate, \u0432\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438 \u043a\u0435\u0448\u0430. \u042d\u0442\u043e\u0442 \u043a\u0435\u0448 \u0440\u0435\u0432\u0430\u043b\u0438\u0434\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435, \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0415\u0433\u043e \u0443\u0434\u043e\u0431\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u043c\u043d\u043e\u0433\u043e \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432\/\u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043d\u0430 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044e (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0441 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u043e\u0439)<\/p>\n<details class=\"spoiler\">\n<summary>ISR<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ebc\/d11\/6e8\/ebcd116e8dd5ea4319d06d361475ffc4.png\" width=\"1428\" height=\"906\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ebc\/d11\/6e8\/ebcd116e8dd5ea4319d06d361475ffc4.png\"\/><figcaption><\/figcaption><\/figure>\n<\/div>\n<\/details>\n<h3>\u041f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u0442\u0432\u0438\u043a\u0438 \u0434\u043b\u044f next.js<\/h3>\n<pre><code class=\"javascript\">module.exports = {   poweredByHeader: false,  \/\/\u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a x-powered-by    compress: false,    \/\/\u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c gzip \u0441\u0436\u0430\u0442\u0438\u0435 (\u0442.\u043a \u0441\u0436\u0438\u043c\u0430\u0435\u043c \u043d\u0430 nginx \u0438\u043b\u0438 \u043d\u0430 cdn) }<\/code><\/pre>\n<p>\u041a\u043e\u043c\u043f\u0440\u0435\u0441\u0441\u0438\u044f \u0441\u0442\u0430\u0442\u0438\u043a\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Brotli<\/p>\n<pre><code class=\"javascript\">const zlib = require(\"zlib\") const CompressionPlugin = require(\"compression-webpack-plugin\") const use_brotli = false module.exports = {   webpack: (config, { isServer }) => {     if (use_brotli &amp;&amp; !isServer) {       config.plugins.push(         new CompressionPlugin({           filename: \"[path][base].br\",           algorithm: \"brotliCompress\",           test: \/\\.(js|css|html)$\/,           compressionOptions: {             params: {               [zlib.constants.BROTLI_PARAM_QUALITY]: 11,             },           }         })       )     }     return config   } }<\/code><\/pre>\n<p>\u0421\u0430\u043c\u044b\u0439 \u0442\u044f\u0436\u0435\u043b\u044b\u0439 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0444\u0430\u0439\u043b \u2013 framework.js <br \/>framework-0441fae7fd130f37dee1.js \u2013 131,009 bytes<br \/>framework-0441fae7fd130f37dee1.js.br \u2013 36,934 bytes<\/p>\n<p>\u0414\u043b\u044f \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f (gzip level 9)<br \/>framework-0441fae7fd130f37dee1.js.gz \u2013 42,388 bytes<\/p>\n<p>\u041c\u043e\u0449\u043d\u043e. \u0416\u0430\u043b\u044c, \u0447\u0442\u043e <a href=\"https:\/\/community.cloudflare.com\/t\/cloudflare-and-brotli-compression\/186710\/2\" rel=\"noopener noreferrer nofollow\">Cloudflare \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 brotli \u043d\u0430 \u0441\u0432\u043e\u0438\u0445 Edge \u0441\u0435\u0440\u0432\u0435\u0440\u0430\u0445<\/a>. \u0422\u0430\u043a \u0447\u0442\u043e 11 \u043b\u0435\u0432\u0435\u043b\u043e\u043c brotli \u043f\u043e\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f, \u043d\u043e Cloudflare \u0441\u0430\u043c \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442 \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u0443\u0440\u043e\u0432\u0435\u043d\u044c brotli, \u0442\u0430\u043a \u0447\u0442\u043e \u043f\u043e\u0442\u0435\u0440\u0438 \u043d\u0435 \u0442\u0430\u043a\u0438\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u0435. (\u0421loudflare \u0441\u0436\u0430\u043b \u0442\u043e\u0442 \u0436\u0435 \u0444\u0430\u0439\u043b \u0434\u043e 43.4 \u043a\u0431).<\/p>\n<h4>\u0418\u0437\u043e\u043c\u043e\u0440\u0444\u043d\u044b\u0435 \u043a\u043e\u043d\u0444\u0438\u0433 \u0444\u0430\u0439\u043b\u044b \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043e\u043f\u0430\u0441\u043d\u044b.<\/h4>\n<p>\u041e\u0448\u0438\u0431\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043b\u0435\u0433\u043a\u043e \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c. <\/p>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 config.js \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u043c\u0438 \u0441\u0435\u043a\u0440\u0435\u0442\u0430\u043c\u0438 \u0434\u043b\u044f backend \u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0430\u043c\u0438, \u043a\u0430\u043a \u0434\u043b\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0439, \u0442\u0430\u043a \u0438 \u0434\u043b\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438.<\/p>\n<p>\u0412 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u0430\u043a\u043e\u0435-\u0442\u043e \u0438\u0437 \u0435\u0433\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <code>recaptcha_site_key<\/code>. \u041f\u043e \u0434\u0435\u0444\u043e\u043b\u0442\u0443, webpack \u0438\u043d\u043b\u0430\u0439\u043d\u0438\u0442 config.js \u0446\u0435\u043b\u0438\u043a\u043e\u043c, \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0441\u0435\u043a\u0440\u0435\u0442\u044b \u0431\u0435\u043a\u0435\u043d\u0434\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442. <\/p>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 next.config.js \u043c\u043e\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u0430\u043a\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0438\u043d\u043b\u0430\u0439\u043d\u0438\u0442\u044c (\u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u0447\u0435\u0440\u0435\u0437 DefinePlugin)<\/p>\n<pre><code class=\"javascript\">module.exports = {   env: {  \/\/https:\/\/nextjs.org\/docs\/api-reference\/next.config.js\/environment-variables     recaptcha_site_key: \"6LcqV9QUAAAAAEybBVr0FWnUnFQmOVxGoQ_Muhtb\",     api_domain: \"http:\/\/localhost\/public_api\",     reverse_search_url: \"http:\/\/localhost\",     domain: \"http:\/\/localhost\",     ipns: \"ipns.scenery.cx\"   }, }<\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0432 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>process.env.\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435_\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439<\/code>. \u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0438\u043d\u043b\u0430\u0439\u043d\u0435\u043d\u043e.<\/p>\n<h4>Next.js SSR \u043d\u0435 \u043c\u0438\u043d\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u0442 css, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u043d\u043b\u0430\u0439\u043d\u0438\u0442\u0441\u044f \u0432 html \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 (\u0435\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 _document.js).<\/h4>\n<p>\u041d\u0430\u043f\u0438\u0448\u0435\u043c \u043c\u0438\u043d\u0438\u043a\u0435\u0448 \u2013 \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043c\u0438\u043d\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 css \u0432 Map. \u0415\u0441\u043b\u0438 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 css \u0435\u0441\u0442\u044c \u0432 Map, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0435\u0433\u043e \u043c\u0438\u043d\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e, \u0438\u043d\u0430\u0447\u0435 \u043c\u0438\u043d\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u043c \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0432 Map.<\/p>\n<pre><code class=\"javascript\">let css = sheets.toString()   if (css &amp;&amp; process.env.NODE_ENV === \"production\") {     const min_css = minified_css_cache.get(css)     if (min_css) {       css = min_css     } else {       const old_css = css       css = cleanCSS.minify(css).styles       minified_css_cache.set(old_css, css)     }   }<\/code><\/pre>\n<p>\u0423\u0431\u0438\u0440\u0430\u0435\u0442 \u043f\u0430\u0440\u0443 \u0434\u0435\u0441\u044f\u0442\u044b\u0445 \u043a\u0438\u043b\u043e\u0431\u0430\u0439\u0442\u0430, \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435 \u0432\u043b\u0438\u044f\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c (\u0441\u0442\u0430\u043b\u043e \u0434\u0430\u0436\u0435 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435). \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043c\u043d\u043e\u0433\u043e \u0440\u0430\u0437\u043d\u043e\u0433\u043e \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e css&#8217;a, \u0442\u043e \u043d\u0430\u0434\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043f\u043b\u044e\u0441\u044b \u043e\u0442 \u043c\u0438\u043d\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u0435\u0440\u0435\u043a\u0440\u043e\u044e\u0442\u0441\u044f \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u043c Map\/\u0435\u0433\u043e \u0431\u044b\u0441\u0442\u0440\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\u043c.<\/p>\n<h3>\u0412\u0435\u0431 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a<\/h3>\n<p>\u0412 \u043d\u0430\u0447\u0430\u043b\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u044f \u0432\u044b\u0431\u0440\u0430\u043b Express. \u041f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441, \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432. \u0415\u0441\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e 1 \u043c\u0438\u043d\u0443\u0441 \u2013 <a href=\"https:\/\/www.techempower.com\/benchmarks\/#section=data-r20&amp;hw=ph&amp;test=composite&amp;l=zik0sf-sf&amp;p=zik0zj-zijocf-zik0zj-zik0zj-1ekf&amp;c=c\" rel=\"noopener noreferrer nofollow\">\u043d\u0438\u0437\u043a\u0430\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c<\/a>. <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/341\/ab4\/ed3\/341ab4ed3fbae450e2b93d5c5224f551.png\" width=\"1357\" height=\"227\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/341\/ab4\/ed3\/341ab4ed3fbae450e2b93d5c5224f551.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0420\u0435\u0448\u0438\u043b \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u0442\u044c \u0432\u0441\u0435 \u043d\u0430 fastify, \u0431\u043b\u0430\u0433\u043e \u043a\u043e\u0434\u0430 \u043d\u0435 \u0442\u0430\u043a \u043c\u043d\u043e\u0433\u043e. \u041f\u043e\u043b\u0443\u0447\u0438\u043b \u043f\u0440\u0438\u0440\u043e\u0441\u0442 \u0441 50 req\/s \u0434\u043e 70 req\/s. \u042d\u0442\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043c\u043d\u043e\u0433\u043e, \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u044f \u0447\u0442\u043e \u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u043b \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Next.js, \u0442.\u0435 \u0432\u0441\u0435 \u0447\u0442\u043e \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043e\u0442 \u0432\u0435\u0431 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430, \u044d\u0442\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0443 Next.js, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0441\u0430\u043c\u043e\u0435 \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u2013 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e express \u0441\u044a\u0435\u0434\u0430\u043b \u043e\u043a\u043e\u043b\u043e 30 \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u043e\u0432 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438.<\/p>\n<p>\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043c\u043d\u0435 \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u0430\u0441\u044c \u0441\u0432\u044f\u0437\u043a\u0430 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 (\u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u0432 fastify \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/github.com\/ajv-validator\/ajv\" rel=\"noopener noreferrer nofollow\">ajv<\/a>) \u0438 \u043c\u043e\u0434\u0443\u043b\u044f <a href=\"https:\/\/github.com\/ThomasAribart\/json-schema-to-ts\" rel=\"noopener noreferrer nofollow\">json-schema-to-ts<\/a>. \u042d\u0442\u0430 \u0441\u0432\u044f\u0437\u043a\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u0438\u043f\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u043f\u0440\u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438, \u0432 typescript. \u0422.\u0435. \u0435\u0441\u043b\u0438 \u043c\u044b \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u0447\u0442\u043e body \u043e\u0431\u044f\u0437\u0430\u043d \u0438\u043c\u0435\u0442\u044c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e id \u0442\u0438\u043f\u0430 number, \u0442\u043e \u0432 ts \u0442\u0438\u043f req.body.id \u0442\u043e\u0436\u0435 \u0431\u0443\u0434\u0435\u0442 number.<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u0438\u043d\u0448\u043e\u0442 \u0438\u0437 VS Code<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/df5\/c71\/0b3\/df5c710b3517b97d762df011942f54a3.png\" width=\"1920\" height=\"907\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/df5\/c71\/0b3\/df5c710b3517b97d762df011942f54a3.png\"\/><figcaption><\/figcaption><\/figure>\n<\/div>\n<\/details>\n<p>\u041d\u0438\u0436\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d \u0442\u0438\u043f\u043e\u0432\u043e\u0439 \u043a\u043e\u0434 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043f\u0443\u0442\u0438 (route). \u041d\u0430 \u043c\u043e\u0439 \u0432\u0437\u0433\u043b\u044f\u0434, \u044d\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u043d\u043e,  \u043a\u043e\u0433\u0434\u0430 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u0438 \u0441\u0430\u043c \u043a\u043e\u0434 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0432 \u043e\u0434\u043d\u043e\u043c \u0444\u0430\u0439\u043b\u0435.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u043a\u043e\u0434\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import db_ops from '.\/..\/helpers\/db_ops' import image_ops from '.\/..\/helpers\/image_ops' import { FastifyRequest, FastifyReply } from \"fastify\" import { FromSchema } from \"json-schema-to-ts\"; const body_schema_delete_image = {     type: 'object',     properties: {         id: { type: 'number' },     },     required: ['id'], } as const  async function delete_image(req: FastifyRequest&lt;{ Body: FromSchema&lt;typeof body_schema_delete_image> }>, res: FastifyReply) {     const id = req.body.id     if (req.session?.user_id) {         const user = await db_ops.activated_user.find_user_by_id(req.session.user_id)         if (user &amp;&amp; user.isAdmin) {             const result = await image_ops.delete_image(id)             if (result) {                 res.send({ message: result })             } else {                 res.send({ message: \"fail\" })             }         }     } else {         res.status(404).send()     } }  export default {     schema: {         body: body_schema_delete_image     },     handler: delete_image }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u043c\u0438\u043d\u0443\u0441 \u2013 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432. \u0425\u043e\u0440\u043e\u0448\u043e, \u0447\u0442\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u043b\u0430\u0433\u0438\u043d \u0434\u043b\u044f fastify \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e. \u042f \u043d\u0435 \u043d\u0430\u0448\u0435\u043b \u043f\u043b\u0430\u0433\u0438\u043d \u0434\u043b\u044f \u0432\u0435\u0440\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 Recaptcha, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u0441\u0432\u043e\u0439. \u0421\u0438\u0441\u0442\u0435\u043c\u0430 <a href=\"https:\/\/www.fastify.io\/docs\/latest\/Hooks\/\" rel=\"noopener noreferrer nofollow\">\u0445\u0443\u043a\u043e\u0432 <\/a>\u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u0438\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u0435\u0449\u0435 \u043d\u0430 \u043f\u043e\u0434\u0445\u043e\u0434\u0435 \u043a \u0445\u0435\u043d\u0434\u043b\u0435\u0440\u0443 \u0438 \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443, \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u044f \u0435\u0433\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043a\u043e\u0434. \u0412\u043e\u0442 <a href=\"https:\/\/www.fastify.io\/docs\/master\/Plugins\/#create-a-plugin\" rel=\"noopener noreferrer nofollow\">\u0433\u0430\u0439\u0434<\/a> \u043f\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432. \u042f \u043b\u0438\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u043b \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432 \u0438\u0437 <a href=\"https:\/\/www.fastify.io\/ecosystem\/\" rel=\"noopener noreferrer nofollow\">\u0441\u043f\u0438\u0441\u043a\u0430 \u0432\u0441\u0435\u0445 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432<\/a> \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u0438\u0445 \u043a\u043e\u0434 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 boilerplate.<\/p>\n<h3>\u0421\u0435\u0442\u043a\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439<\/h3>\n<p>\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u0440\u043e \u0442\u043e, \u043a\u0430\u043a \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f image grid. \u0421\u0440\u0435\u0434\u0441\u0442\u0432\u0430\u043c\u0438 css \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0441\u043b\u043e\u0436\u043d\u043e (\u0438\u043b\u0438 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e) \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0443\u044e, \u043e\u0442\u0437\u044b\u0432\u0447\u0438\u0432\u0443\u044e \u0441\u0435\u0442\u043a\u0443 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439, \u0433\u0434\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0441\u043e\u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u044f \u0441\u0442\u043e\u0440\u043e\u043d, \u0430 \u0441\u0442\u0440\u043e\u043a\u0438 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0443\u043f\u0430\u043a\u043e\u0432\u0430\u043d\u044b. <a href=\"https:\/\/www.w3schools.com\/howto\/tryit.asp?filename=tryhow_css_image_grid_responsive\" rel=\"noopener noreferrer nofollow\">\u0412\u043e\u0442 \u043f\u0440\u0438\u043c\u0435\u0440<\/a> css \u0441\u0435\u0442\u043a\u0438.<\/p>\n<p>\u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u2013 \u0431\u0443\u0434\u0435\u043c \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0435\u0442\u043a\u0443 \u0441\u0438\u043b\u0430\u043c\u0438 js. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <a href=\"https:\/\/github.com\/neptunian\/react-photo-gallery\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/neptunian\/react-photo-gallery<\/a>. <\/p>\n<p>\u0414\u0430\u043d\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0430\u0434\u0430\u043f\u0442\u0430\u0446\u0438\u044e \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0430 \u041a\u043d\u0443\u0442\u0430-\u041f\u043b\u0430\u0441\u0441\u0430 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0430 \u0441\u0442\u0440\u043e\u043a. (\u042d\u0442\u043e\u0442 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0442\u0430\u043a\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 TeX \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0430 \u0441\u0442\u0440\u043e\u043a)<\/p>\n<p>\u0410\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0433\u0440\u0430\u0444 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u043e\u0432, \u0433\u0434\u0435 \u0432\u0435\u0440\u0448\u0438\u043d\u0430 \u2013 \u044d\u0442\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u0451\u043b \u043f\u0435\u0440\u0435\u043d\u043e\u0441 \u0441\u0442\u0440\u043e\u043a\u0438, \u0430 \u0432\u0435\u0441 \u0440\u0435\u0431\u0440\u0430\u00a0\u044d\u0442\u043e <code>cost(targetRowHeight, \u0412\u044b\u0441\u043e\u0442\u0430 \u0440\u044f\u0434\u0430 \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0441\u0442\u0440\u043e\u043a\u0443<\/code>). \u041e\u043f\u0442\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u0440\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u2013 \u044d\u0442\u043e \u043a\u0440\u0430\u0442\u0447\u0430\u0439\u0448\u0438\u0439 \u043f\u0443\u0442\u044c \u0432 \u0442\u0430\u043a\u043e\u043c \u0433\u0440\u0430\u0444\u0435. \u0422.\u0435 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0440\u0430\u0432\u043d\u043e\u043c\u0435\u0440\u043d\u043e \u0443\u043f\u0430\u043a\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0431\u043b\u0438\u0437\u043a\u0438\u0435 \u043a \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u043e\u0439 \u0432\u044b\u0441\u043e\u0442\u0435.<\/p>\n<p><a href=\"https:\/\/medium.com\/google-design\/google-photos-45b714dfbed1\" rel=\"noopener noreferrer nofollow\">\u0411\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0437\u0434\u0435\u0441\u044c<\/a> (2\u043f\u0443\u043d\u043a\u0442, Justified Gallery)<br \/> <a href=\"http:\/\/blog.vjeux.com\/2014\/image\/google-plus-layout-find-best-breaks.html\" rel=\"noopener noreferrer nofollow\">http:\/\/blog.vjeux.com\/2014\/image\/google-plus-layout-find-best-breaks.html<\/a><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/3d5\/ed9\/aa7\/3d5ed9aa76c20bafa93f6502ddbf0674.jpeg\" width=\"2000\" height=\"1802\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3d5\/ed9\/aa7\/3d5ed9aa76c20bafa93f6502ddbf0674.jpeg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<h3>\u041e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439<\/h3>\n<p>Lossless \u0441\u0436\u0430\u0442\u0438\u0435, \u0442.\u0435 \u043f\u0438\u043a\u0441\u0435\u043b\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043d\u0435 \u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f. <\/p>\n<p>Jpeg \u0441\u0436\u0438\u043c\u0430\u0435\u043c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e jpegtran (\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u0438\u0437 libjpeg-turbo) \u00a0 <\/p>\n<p><code>apt-get install libjpeg-turbo-progs<\/code><\/p>\n<p>jpegtran \u0438\u0437 mozjpeg \u0436\u043c\u0435\u0442 \u043d\u0430 1,18% \u043b\u0443\u0447\u0448\u0435, \u043d\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u044d\u0442\u043e \u0432 2,3 \u0440\u0430\u0437\u0430 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0435\u0435. <a href=\"https:\/\/gist.github.com\/sergejmueller\/088dce028b6dd120a16e\" rel=\"noopener noreferrer nofollow\">https:\/\/gist.github.com\/sergejmueller\/088dce028b6dd120a16e<\/a><\/p>\n<p>\u0412\u043c\u0435\u0441\u0442\u043e optipng \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <a href=\"https:\/\/github.com\/shssoichiro\/oxipng\/\" rel=\"noopener noreferrer nofollow\">Oxipng <\/a>(\u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u043d\u0430 Rust optipng \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438) <\/p>\n<p>\u0422\u0435\u0441\u0442 jpegtran<\/p>\n<p>\u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 &#8212; 22,785 KB<br \/>\u041f\u043e\u0441\u043b\u0435 jpegtran \u2013 21,420 KB<\/p>\n<p>\u0422\u0435\u0441\u0442 oxipng\/optipng<\/p>\n<p>\u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 &#8212; 8,94 MB <\/p>\n<p>oxipng -o2 \u2013 5.97 MB, 5 \u0441\u0435\u043a\u0443\u043d\u0434<br \/>optipng -o2 \u2013 5.92 MB, 34 \u0441\u0435\u043a\u0443\u043d\u0434\u044b<\/p>\n<h3>\u041d\u0430\u0440\u0435\u0437\u043a\u0430 thumbnail&#8217;\u043e\u0432    <\/h3>\n<p>\u0414\u043b\u044f \u0440\u0435\u0441\u0430\u0439\u0437\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <a href=\"https:\/\/github.com\/lovell\/sharp\" rel=\"noopener noreferrer nofollow\">sharp<\/a>. Sharp \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 libvips, \u044d\u0442\u043e <a href=\"https:\/\/github.com\/libvips\/libvips\/wiki\/Speed-and-memory-use\" rel=\"noopener noreferrer nofollow\">\u043e\u0447\u0435\u043d\u044c \u0431\u044b\u0441\u0442\u0440\u0430\u044f\u00a0\u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u044e\u0449\u0430\u044f \u043c\u0430\u043b\u043e \u043f\u0430\u043c\u044f\u0442\u0438<\/a> \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c\u0438.<\/p>\n<pre><code class=\"javascript\">async function generate_thumbnail(image_src: Buffer | string) {  \/\/buffer or path to the image   const metadata = await sharp(image_src).metadata()   if (metadata &amp;&amp; metadata.height &amp;&amp; metadata.width) {     const x: { width?: number, height?: number } = {}     if (metadata.width >= metadata.height) {       x.width = Math.min(metadata.width, 750)     } else { \/\/metadata.width &lt; metadata.height       x.height = Math.min(metadata.height, 750)     }     const data = await sharp(image_src).resize(x).jpeg({ quality: 80, mozjpeg: true }).toBuffer()     return data   } else {     return null   } } <\/code><\/pre>\n<p>\u041e\u043f\u0446\u0438\u044f mozjpeg \u043d\u0443\u0436\u043d\u0430 \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043e\u0442 mozjpeg.<code>{ trellisQuantisation: true, overshootDeringing: true, optimiseScans: true, quantisationTable: 3 }<\/code><\/p>\n<p>Thumbnail\u2019\u044b \u0432\u044b\u0445\u043e\u0434\u044f\u0442 \u043f\u043e ~50\u043a\u0431. \u041d\u0430 \u043c\u043e\u0438\u0445 \u0442\u0435\u0441\u0442\u0430\u0445, \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0435 \u043f\u043e \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0443 thumbnail&#8217;\u044b \u0432 jpeg \u0432\u0435\u0441\u044f\u0442 \u043c\u0435\u043d\u044c\u0448\u0435, \u0447\u0435\u043c \u0432 webp.<\/p>\n<h3>\u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f sha256<\/h3>\n<p>\u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c sha256 \u0434\u043e \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438(lossless \u0441\u0436\u0430\u0442\u0438\u044f) \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <a href=\"https:\/\/github.com\/ronomon\/crypto-async\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/ronomon\/crypto-async<\/a><\/p>\n<p>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u043c \u043c\u043e\u0434\u0443\u043b\u0435\u043c crypto:<\/p>\n<ol>\n<li>\n<p>Crypto \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442 event loop<\/p>\n<\/li>\n<li>\n<p>Crypto \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 1 \u044f\u0434\u0440\u0430<\/p>\n<\/li>\n<\/ol>\n<p>crypto-async<\/p>\n<ol>\n<li>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432 ThreadPool (\u0442.\u0435 \u043d\u0435 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442 event loop)<\/p>\n<\/li>\n<li>\n<p>\u0422.\u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f ThreadPool, \u0442\u043e \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043e\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u044f\u0434\u0435\u0440 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e Thread&#8217;\u043e\u0432 \u0432 \u043f\u0443\u043b\u0435 \u0440\u0430\u0432\u043d\u043e 4, \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u043d\u043e \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0442\u044c \u0432\u0440\u0443\u0447\u043d\u0443\u044e)<\/p>\n<\/li>\n<li>\n<p>Zero copy<\/p>\n<\/li>\n<\/ol>\n<h3>\u0414\u0435\u0434\u0443\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u044f<\/h3>\n<p>\u0414\u0435\u0434\u0443\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043b\u043e\u0441\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435. \u0415\u0441\u0442\u044c 2 \u0442\u0438\u043f\u0430 \u0438\u043c\u043f\u043e\u0440\u0442\u0430: \u043e\u0434\u0438\u043d\u043e\u0447\u043d\u044b\u0439 (\u0447\u0435\u0440\u0435\u0437 \u0432\u0435\u0431 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441) \u0438 \u043c\u0430\u0441\u0441\u043e\u0432\u044b\u0439 (\u0447\u0435\u0440\u0435\u0437 python \u0441\u043a\u0440\u0438\u043f\u0442)<\/p>\n<p>\u0412\u043e\u0442 \u043a\u0430\u043a \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0434\u0435\u0434\u0443\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u0440\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 \u0432\u0435\u0431 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441:<\/p>\n<p>\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0438\u0449\u0435\u043c sha256 \u0445\u0435\u0448 \u0432 \u0431\u0434. \u0415\u0441\u043b\u0438 \u0445\u0435\u0448\u0430 \u043d\u0435\u0442, \u0442\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0432 ambience. ambience \u043e\u0431\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043a phash \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0443, \u0435\u0441\u043b\u0438 \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d, \u0442\u043e \u043e\u0431\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043a akaze \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0443. \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 scenery. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0432\u044b\u0448\u0435\u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u044b\u0435 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0432 \u0441\u0435\u043a\u0446\u0438\u0438 \u0441 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u044b ambience.<\/p>\n<p>\u0412\u0432\u0438\u0434\u0443 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b \u043b\u043e\u0436\u043d\u043e\u043f\u043e\u043b\u043e\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b, \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u043d\u0430 \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442 \u0438\u0437 \u0432\u0435\u0431 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430.<\/p>\n<h3>\u041f\u043e\u0438\u0441\u043a \u043f\u043e \u0442\u0435\u0433\u0430\u043c<\/h3>\n<p>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 MongoDB<\/p>\n<pre><code class=\"javascript\">db.things.find({     $and: [         {             $or: [{ \"first_name\": \"john\" }, { \"last_name\": \"john\" }]         },         { \"phone\": \"12345678\" }     ] })<\/code><\/pre>\n<p>\u0414\u0430\u043d\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u044d\u043a\u0432\u0438\u0432\u0430\u043b\u0435\u043d\u0442\u0435\u043d <code>({\"first_name\" : \"john\"} ||\u00a0 {\"last_name\" : \"john\"}) &amp;&amp; {\"phone\": \"12345678\"}<\/code>. \u041f\u043e\u0445\u043e\u0436\u0435 \u043d\u0430 AST, \u0437\u043d\u0430\u0447\u0438\u0442 \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0430\u0440\u0441\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043c\u043e\u0436\u0435\u0442 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c AST \u0438\u0437 \u0441\u0442\u0440\u043e\u043a\u0438 \u0441 \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u043c\u0438 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c\u0438.<\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u0430\u0440\u0441\u0435\u0440\u0430 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u043e\u0434\u0438\u043d \u0438\u0437 \u0441\u0430\u043c\u044b\u0445 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u0432 \u2013 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BE%D1%87%D0%BD%D0%BE%D0%B9_%D1%81%D1%82%D0%B0%D0%BD%D1%86%D0%B8%D0%B8\" rel=\"noopener noreferrer nofollow\">\u0410\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043e\u0447\u043d\u043e\u0439 \u0441\u0442\u0430\u043d\u0446\u0438\u0438<\/a> (<a href=\"https:\/\/en.wikipedia.org\/wiki\/Shunting-yard_algorithm\" rel=\"noopener noreferrer nofollow\">Shunting yard algorithm<\/a>). <\/p>\n<p>\u042f \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0434\u043e\u043b\u0433\u043e \u043c\u0443\u0447\u0430\u043b\u0441\u044f \u0441 \u044d\u0442\u0438\u043c \u043f\u0430\u0440\u0441\u0435\u0440\u043e\u043c, \u0438\u0437-\u0437\u0430 \u0447\u0435\u0433\u043e \u043a\u043e\u0434 \u0441\u0442\u0430\u043b \u043d\u0435\u0447\u0438\u0442\u0430\u0435\u043c\u044b\u043c. \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0431\u044b\u043b\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0441\u043a\u043e\u0431\u043a\u0438, \u0437\u043d\u0430\u043a \u0432\u043e\u0441\u043a\u043b\u0438\u0446\u0430\u043d\u0438\u044f\u00a0\u0438 \u043c\u0438\u043d\u0443\u0441 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043a\u0430\u043a \u0447\u0430\u0441\u0442\u044c\u044e \u0442\u0435\u0433\u0430, \u0442\u0430\u043a \u0438 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u043c.  \u0415\u0441\u043b\u0438 \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u043e\u0442\u0440\u0435\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0438 \u043e\u0431\u0444\u0443\u0441\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u2013\u00a0  <a href=\"https:\/\/github.com\/qwertyforce\/scenery\/blob\/master\/components\/parse.ts\" rel=\"noopener noreferrer nofollow\">\u0434\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c<\/a>.<\/p>\n<p>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442:<\/p>\n<ul>\n<li>\n<p>&amp;&amp; ,(\u0437\u0430\u043f\u044f\u0442\u0430\u044f) \u2013 \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0438<\/p>\n<\/li>\n<li>\n<p>|| \u2013 \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0438\u043b\u0438<\/p>\n<\/li>\n<li>\n<p>!, -(\u043c\u0438\u043d\u0443\u0441)  \u2013 \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043d\u0435<\/p>\n<\/li>\n<\/ul>\n<p>\u0410 \u0442\u0430\u043a\u0436\u0435 >,>=,&lt;,&lt;=,== \u0434\u043b\u044f \u0441\u043f\u0435\u0446. \u0442\u0435\u0433\u043e\u0432 (\u0432\u044b\u0441\u043e\u0442\u0430 \u0438 \u0448\u0438\u0440\u0438\u043d\u0430).<\/p>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0432 \u0431\u0434 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0438\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0432\u0432\u043e\u0434\u0430, \u0431\u0443\u0434\u0435\u0442 \u043d\u0435 \u043b\u0438\u0448\u043d\u0438\u043c \u0432\u0432\u0435\u0441\u0442\u0438 \u0437\u0430\u0449\u0438\u0442\u0443 \u043e\u0442 nosql \u0438\u043d\u044a\u0435\u043a\u0446\u0438\u0439.<\/p>\n<pre><code class=\"javascript\">const nosql_injection_regex = new RegExp(\/[|]|$|:|{|}\/gm) \/\/[,],$,:,{,}<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440\u044b<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">\/\/build_ast(\"forest&amp;&amp;height>=1920&amp;&amp;width>=1080\") \/\/=> {    \"$and\":[       {          \"$and\":[             {\"tags\":\"forest\"},             {\"height\":{\"$gte\":1920}}          ]       },       {\"width\":{\"$gte\":1080}}    ] } <\/code><\/pre>\n<pre><code class=\"javascript\">\/\/build_ast(\"forest(wi(nter) || nice!\") \/\/=> {   \"$or\": [     {       \"tags\": \"forest(wi(nter)\"     },     {       \"tags\": \"nice!\"     }   ] } <\/code><\/pre>\n<pre><code class=\"javascript\">build_ast(\"(a||b)&amp;&amp;(-a||-b\") => {   \"$and\": [     {       \"$or\": [         {           \"tags\": \"a\"         },         {           \"tags\": \"b\"         }       ]     },     {       \"$or\": [         {           \"$nor\": [             {               \"tags\": \"a\"             }           ]         },         {           \"$nor\": [             {               \"tags\": \"b\"             }           ]         }       ]     }   ] } <\/code><\/pre>\n<p>\u0412\u043c\u0435\u0441\u0442\u043e Not \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f nor, \u043d\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043d\u0435 \u0432\u043b\u0438\u044f\u0435\u0442, \u043d\u043e \u0444\u0438\u043a\u0441\u0438\u0442 \u043a\u0430\u043a\u0443\u044e-\u0442\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u0441 not \u0432 \u043c\u043e\u043d\u0433\u043e\u0434\u0431.<\/p>\n<\/div>\n<\/details>\n<h3>\u041f\u043e\u0438\u0441\u043a \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0441 \u043f\u043e\u0445\u043e\u0436\u0438\u043c\u0438 \u0442\u0435\u0433\u0430\u043c\u0438<\/h3>\n<p>\u041f\u043e\u0438\u0441\u043a \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439, \u0443 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043d\u0430\u0438\u0431\u043e\u043b\u044c\u0448\u0435\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u0431\u0449\u0438\u0445 \u0442\u0435\u0433\u043e\u0432 c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u043c, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u0438\u0449\u0435\u043c. \u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441\u043e\u0440\u0442\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u043f\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0443 \u043e\u0431\u0449\u0438\u0445 \u0442\u0435\u0433\u043e\u0432.<\/p>\n<p>\u0427\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435 \u0442\u0435\u0433\u043e\u0432 \u0443 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438 \u0447\u0435\u043c \u0431\u043e\u043b\u0435\u0435 \u0442\u043e\u0447\u043d\u043e \u043e\u043d\u0438 \u0435\u0433\u043e \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442, \u0442\u0435\u043c \u043b\u0443\u0447\u0448\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0439 \u0432\u0438\u0434 \u043f\u043e\u0438\u0441\u043a\u0430. <\/p>\n<pre><code class=\"javascript\">async function get_images_with_similar_tags(image_id: number, limit: number) {     const target_tags = (await get_image_tags_by_id(image_id))?.tags     if (!target_tags) {         return []     }     const x = IMAGES_COLLECTION.aggregate([         { $unwind: \"$tags\" },         { $match: { tags: { $in: target_tags } } },         { $group: { _id: { id: \"$id\", height: \"$height\", width: \"$width\" }, count: { $sum: 1 } } },         { $sort: { count: -1 } },         { $limit: limit }     ])     return x.toArray() }<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/17f\/f4c\/ddb\/17ff4cddb408ad4c861e7d81deddc1b8.png\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/17f\/f4c\/ddb\/17ff4cddb408ad4c861e7d81deddc1b8.png\"\/><figcaption><\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a8f\/a6a\/93f\/a8fa6a93faa5bcde4d3870db6af1c9e1.png\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a8f\/a6a\/93f\/a8fa6a93faa5bcde4d3870db6af1c9e1.png\"\/><figcaption><\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8b5\/962\/2cd\/8b59622cda3f4cd9b67506372933c0e0.png\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/8b5\/962\/2cd\/8b59622cda3f4cd9b67506372933c0e0.png\"\/><figcaption><\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ef0\/ffa\/c06\/ef0ffac06d69a8ab0ab286f25465888b.png\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ef0\/ffa\/c06\/ef0ffac06d69a8ab0ab286f25465888b.png\"\/><figcaption><\/figcaption><\/figure>\n<\/div>\n<\/details>\n<h2>Ambience <\/h2>\n<p>\u0421\u0442\u0435\u043a \u2013 Node.js + Fastify + TypeScript (api gateway)<\/p>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438: \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0440\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u043d\u043e\u0432\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 \u0432\u0435\u0431 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u0438\u0434\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 features \u0432 ambience, \u043e\u0442\u043a\u0443\u0434\u0430 \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043f\u043e\u0441\u044b\u043b\u0430\u044e\u0442\u0441\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b.<\/p>\n<pre><code class=\"javascript\">async function calculate_all_image_features(image_id: number, image_buffer: Buffer) {     return Promise.allSettled([         calculate_akaze_features(image_id, image_buffer),         calculate_nn_features(image_id, image_buffer),         calculate_hist_features(image_id, image_buffer),         calculate_phash_features(image_id, image_buffer),     ]) }<\/code><\/pre>\n<p>\u043f\u0440\u043e\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0434\u043e \u043d\u0443\u0436\u043d\u043e\u0433\u043e \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u0442.\u0435 \u0438\u0437 scenery \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0441\u0442\u0443\u0447\u0430\u0442\u044c\u0441\u044f \u0434\u043e \u043b\u044e\u0431\u043e\u0433\u043e \u0445\u0435\u043d\u0434\u043b\u0435\u0440\u0430 \u043b\u044e\u0431\u043e\u0433\u043e \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430. <\/p>\n<pre><code class=\"javascript\">const nn_routes = ['\/nn_get_similar_images_by_image_buffer', '\/nn_get_similar_images_by_text',     '\/nn_get_similar_images_by_id', '\/calculate_nn_features', '\/delete_nn_features', '\/nn_get_image_tags_by_image_buffer'] nn_routes.forEach((r) => server.post(r, async (_req, res) => {     try {         res.from(combineURLs(config.nn_microservice_url,r))     } catch (err) {         res.status(500).send('NN microservice is down')     } }))<\/code><\/pre>\n<p>\u0414\u043b\u044f \u043f\u0440\u043e\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f <a href=\"https:\/\/github.com\/fastify\/fastify-reply-from\" rel=\"noopener noreferrer nofollow\">fastify-reply-from<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 undici, http 1.1 \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f node.js. undici \u0433\u043e\u0440\u0430\u0437\u0434\u043e <a href=\"https:\/\/www.npmjs.com\/package\/undici\" rel=\"noopener noreferrer nofollow\">\u0431\u044b\u0441\u0442\u0440\u0435\u0435<\/a>, \u0447\u0435\u043c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c http. <\/p>\n<p>\u0421\u0442\u0435\u043a \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u043f\u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u043f\u043e\u0438\u0441\u043a\u043e\u043c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439: Python + SQLite + FastAPI \u00a0<\/p>\n<p>SQLite \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0445\u043e\u043b\u043e\u0434\u043d\u043e\u0433\u043e \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445, \u0432\u0435\u0441\u044c \u043f\u043e\u0438\u0441\u043a \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a.<\/p>\n<p>phash \u0438 akaze \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 faiss IVF. rgb_hist \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 faiss L1 Flat. NN \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 hnswlib.<\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043f\u043e\u0439\u0434\u0435\u0442 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u043c\u0435\u0442\u043e\u0434\u044b \u043f\u043e\u0438\u0441\u043a\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0438\u0437 \u043c\u043e\u0435\u0439 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439 <a href=\"https:\/\/habr.com\/ru\/post\/550528\/\" rel=\"noopener noreferrer nofollow\">\u0441\u0442\u0430\u0442\u044c\u0438<\/a>, \u0441 \u0434\u043e\u0440\u0430\u0431\u043e\u0442\u043a\u0430\u043c\u0438 \u0438 \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f\u043c\u0438.<\/p>\n<h3>phash<\/h3>\n<p>\u0417\u0430\u0434\u0430\u0447\u0430 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430: \u043f\u043e\u0438\u0441\u043a \u043f\u043e phash (\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432 \u043a \u0440\u0435\u0441\u0430\u0439\u0437\u0443, \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0445 \u0444\u043e\u0442\u043e \u0440\u0430\u0437\u043d\u043e\u0433\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430, \u043d\u0435\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432 \u043a \u043a\u0440\u043e\u043f\u0443)<\/p>\n<p>\u0412 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 ImageHash. \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044f phash.<\/p>\n<pre><code class=\"python\">def phash(image, hash_size=8, highfreq_factor=4): \"\"\" Perceptual Hash computation. Implementation follows http:\/\/www.hackerfactor.com\/blog\/index.php?\/archives\/432-Looks-Like-It.html @image must be a PIL instance. \"\"\" if hash_size &lt; 2: raise ValueError(\"Hash size must be greater than or equal to 2\")  import scipy.fftpack img_size = hash_size * highfreq_factor image = image.convert(\"L\").resize((img_size, img_size), Image.ANTIALIAS) pixels = numpy.asarray(image) dct = scipy.fftpack.dct(scipy.fftpack.dct(pixels, axis=0), axis=1) dctlowfreq = dct[:hash_size, :hash_size] med = numpy.median(dctlowfreq) diff = dctlowfreq > med return ImageHash(diff)<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432 \u0432\u043e\u0437\u044c\u043c\u0435\u043c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 5919&#215;3946 1.52 MB<\/p>\n<p>\u041e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u0434 \u2013 0.31 \u0441<\/p>\n<p>\u041a\u0430\u043a \u0441\u0434\u0435\u043b\u0430\u0442\u044c python \u043a\u043e\u0434 \u0431\u044b\u0441\u0442\u0440\u044b\u0435? \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043a\u043e\u0434 \u043d\u0435 \u0432 python \ud83d\ude00<\/p>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <a href=\"https:\/\/numba.pydata.org\/\" rel=\"noopener noreferrer nofollow\">Numba <\/a>\u2013 JIT \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u0441 nopython mode \u2013 \u043a\u043e\u0434 \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0432\u043d\u0435 python. \u0422\u0430\u043a\u0436\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u043b\u0438 \u043d\u0435 256 \u0431\u0443\u043b\u0435\u0432\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u0430 32 uint8.<\/p>\n<details class=\"spoiler\">\n<summary>v1<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">@jit(nopython=True, cache=True) def diff(dct_data, hash_size):     dctlowfreq = dct_data[:hash_size, :hash_size]     med = np.median(dctlowfreq)     diff = dctlowfreq > med     return diff.flatten()  def fast_phash(image, hash_size=16, highfreq_factor=4):     img_size = hash_size * highfreq_factor     image = image.convert(\"L\").resize((img_size, img_size), Image.ANTIALIAS)     pixels = np.asarray(image)     dct_data = dct(dct(pixels, axis=0), axis=1)     return diff(dct_data, hash_size)  @jit(nopython=True, cache=True) def bit_list_to_32_uint8(bit_list_256):     uint8_arr = []     for i in range(32):         bit_list = []         for j in range(8):             if(bit_list_256[i*8+j] == True):                 bit_list.append(1)             else:                 bit_list.append(0)         uint8_arr.append(bit_list_to_int(bit_list))     return np.array(uint8_arr, dtype=np.uint8)  @jit(nopython=True, cache=True) def bit_list_to_int(bitlist):     out = 0     for bit in bitlist:          out = (out &lt;&lt; 1) | bit     return out  def get_phash(query_image):     bit_list_256 = fast_phash(query_image)     phash = bit_list_to_32_uint8(bit_list_256)     return phash <\/code><\/pre>\n<\/div>\n<\/details>\n<p>v1 &#8212; 0.26 c<\/p>\n<p>\u0417\u0430\u043c\u0435\u043d\u0438\u043c<\/p>\n<pre><code class=\"python\">def fast_phash(image, hash_size=16, highfreq_factor=4):   img_size = hash_size * highfreq_factor   image = image.convert(\"L\").resize((img_size, img_size), Image.ANTIALIAS)   pixels = np.asarray(image)   dct_data = dct(dct(pixels, axis=0), axis=1)   return diff(dct_data, hash_size) <\/code><\/pre>\n<p>\u043d\u0430<\/p>\n<pre><code class=\"python\">def fast_phash(image, hash_size=16, highfreq_factor=4):     img_size = hash_size * highfreq_factor     image = cv2.resize(image, (img_size, img_size), interpolation=cv2.INTER_LINEAR     dct_data = dct(dct(image, axis=0), axis=1)     return diff(dct_data, hash_size)<\/code><\/pre>\n<p>v2 &#8212; 0.14 \u0441<\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0438\u0435 \u043d\u0435\u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u0442.\u043a \u0432 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u043c \u043a\u043e\u0434\u0435 \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0441\u0438\u043b\u044c\u043d\u043e \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 (Pillow, scipy, numpy). \u041c\u043e\u0436\u043d\u043e \u0443\u0441\u043a\u043e\u0440\u044f\u0442\u044c \u0438 \u0434\u0430\u043b\u044c\u0448\u0435, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c \u0440\u0435\u0441\u0430\u0439\u0437 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f pyvips, \u0438\u043b\u0438 \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c DCT. \u042f \u043d\u0435 \u0437\u043d\u0430\u044e \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0438\u043b\u044c\u043d\u043e \u0432\u043b\u0438\u044f\u0435\u0442 \u0442\u0438\u043f \u0440\u0435\u0441\u0430\u0439\u0437\u0430 \u043d\u0430 \u0441\u0438\u043b\u0443 \u0445\u0435\u0448\u0430(\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u044c), \u043d\u043e \u043d\u0435 \u0434\u0443\u043c\u0430\u044e \u0447\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u0441\u0438\u043b\u044c\u043d\u043e.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0442\u044c cpu bound \u0437\u0430\u0434\u0430\u0447\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/joblib.readthedocs.io\/en\/latest\/\" rel=\"noopener noreferrer nofollow\">joblib<\/a>. \u042d\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0440\u0430\u0441\u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0442\u044c \u043a\u0430\u043a\u0443\u044e-\u043d\u0438\u0431\u0443\u0434\u044c \u0437\u0430\u0434\u0430\u0447\u0443 \u043d\u0430 \u043f\u0438\u0442\u043e\u043d\u0435. <\/p>\n<p>\u0414\u043b\u044f \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0430 \u0445\u0435\u0448\u0435\u0439 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0440\u0438 \u043c\u0430\u0441\u0441\u043e\u0432\u043e\u043c \u0438\u043c\u043f\u043e\u0440\u0442\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e python \u0441\u043a\u0440\u0438\u043f\u0442\u0430), \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c joblib. \u0417\u0430\u0434\u0430\u0447\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432 n \u0440\u0430\u0437, \u0433\u0434\u0435 n \u044d\u0442\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u044f\u0434\u0435\u0440.<\/p>\n<pre><code class=\"python\">phashes = Parallel(n_jobs=-1, verbose=1)(delayed(calc_phash)(file_name) for file_name in batch)<\/code><\/pre>\n<h2>rgb_hist<\/h2>\n<p>\u0417\u0430\u0434\u0430\u0447\u0430: \u043f\u043e\u0438\u0441\u043a \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0441 \u043f\u043e\u0445\u043e\u0436\u0435\u0439 \u0446\u0432\u0435\u0442\u043e\u0432\u043e\u0439 \u043f\u0430\u043b\u0438\u0442\u0440\u043e\u0439.    <\/p>\n<p>\u041f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043c\u0435\u043d\u044f\u043b, \u0442\u043e\u0442 \u0436\u0435 \u043f\u043e\u0438\u0441\u043a \u043f\u043e \u0433\u0438\u0441\u0442\u043e\u0433\u0440\u0430\u043c\u043c\u0430\u043c, \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0435\u043f\u0435\u0440\u044c \u0432 faiss flat L1, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e faiss IVF <a href=\"https:\/\/github.com\/facebookresearch\/faiss\/blob\/a3fae15e66bcbb5c4bf64ce2f0f02e164aad2717\/faiss\/IndexIVF.cpp#L99-L101\" rel=\"noopener noreferrer nofollow\">\u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442<\/a> \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u043a\u0440\u043e\u043c\u0435 L2 \u0438 IP.<\/p>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u0431\u0438\u043d \u0433\u0438\u0441\u0442\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u0434\u0435\u043b\u0438\u043c \u043d\u0430 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439 \u0432 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0438, \u0447\u0442\u043e\u0431\u044b &#171;\u0443\u0440\u0430\u0432\u043d\u044f\u0442\u044c&#187; \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441 \u0440\u0430\u0437\u043d\u044b\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439.<\/p>\n<pre><code class=\"python\">def get_features(query_image):     query_hist_combined = cv2.calcHist([query_image], [0, 1, 2], None, [16, 16, 16], [0, 256, 0, 256, 0, 256])     query_hist_combined = query_hist_combined.flatten()     query_hist_combined = query_hist_combined*10000000     query_hist_combined = np.divide(query_hist_combined, query_image.shape[0]*query_image.shape[1], dtype=np.float32)     return query_hist_combined<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/523\/913\/635\/523913635584c141aeb6559e35e81309.png\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/523\/913\/635\/523913635584c141aeb6559e35e81309.png\"\/><figcaption><\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e7d\/f6a\/518\/e7df6a51878ba366fbb93f9efb6636d0.png\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e7d\/f6a\/518\/e7df6a51878ba366fbb93f9efb6636d0.png\"\/><figcaption><\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f98\/afc\/583\/f98afc58366e125b8b761d7bbb3f7896.png\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f98\/afc\/583\/f98afc58366e125b8b761d7bbb3f7896.png\"\/><figcaption><\/figcaption><\/figure>\n<\/div>\n<\/details>\n<h2>akaze<\/h2>\n<p>\u0417\u0430\u0434\u0430\u0447\u0430: \u043f\u043e\u0438\u0441\u043a \u043e\u0431\u0440\u0435\u0437\u0430\u043d\u043d\u044b\u0445 \u0432\u0435\u0440\u0441\u0438\u0439 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439. <\/p>\n<p>\u0412 \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u043b SIFT\/SURF\/ORB \u0438 \u043f\u043e\u0438\u0441\u043a \u0431\u0440\u0443\u0442\u0444\u043e\u0440\u0441\u043e\u043c. <\/p>\n<blockquote>\n<p>\u041c\u0435\u0434\u043b\u0435\u043d\u043d\u043e \u0438\u0449\u0435\u0442\u0441\u044f (\u0415\u0441\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u0441\u043a\u043e\u0440\u044f\u044e\u0449\u0438\u0435 \u043f\u043e\u0438\u0441\u043a, \u043d\u043e \u044f \u043d\u0435 \u043d\u0430\u0448\u0435\u043b \u043f\u043e\u043d\u044f\u0442\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043d\u0430 python)<\/p>\n<\/blockquote>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440\u0430 \u044f \u0442\u0430\u043a \u0438 \u043d\u0435 \u043d\u0430\u0448\u0435\u043b, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u0441\u0432\u043e\u0439. \u041d\u0430 \u044d\u0442\u043e\u0442 \u0440\u0430\u0437 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e AKAZE \u0438 \u0438\u0449\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e faiss ivf.<\/p>\n<p>\u0414\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440 1 \u043a\u043b\u044e\u0447\u0435\u0432\u043e\u0439 \u0442\u043e\u0447\u043a\u0438 (keypoint) \u0432\u0435\u0441\u0438\u0442 61 \u0431\u0430\u0439\u0442. (61 uint8). \u0411\u0443\u0434\u0435\u043c \u0438\u0441\u043a\u0430\u0442\u044c 256 \u0442\u043e\u0447\u0435\u043a, \u0442\u0435\u043c \u0441\u0430\u043c\u044b\u043c 1 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c\u0441\u044f 15616 \u0431\u0430\u0439\u0442\u0430\u043c\u0438. \u0427\u0438\u0441\u043b\u043e \u0442\u043e\u0447\u0435\u043a \u043c\u043e\u0436\u043d\u043e \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0442\u044c, \u043d\u043e \u044d\u0442\u043e \u0442\u0430\u043a\u0436\u0435 \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0442 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u043a \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438, \u0442.\u043a \u0432\u0441\u0435 \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u044b   \u044d\u0442\u0438\u0445 \u0442\u043e\u0447\u0435\u043a \u043d\u0443\u0436\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 RAM \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430. <\/p>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c 256 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u201c\u0445\u043e\u0440\u043e\u0448\u0438\u0445\u201d \u0442\u043e\u0447\u0435\u043a, \u0441\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043f\u043e \u043f\u043e\u043b\u044e response. \u0427\u0435\u043c \u0432\u044b\u0448\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 response, \u0442\u0435\u043c \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u0435\u0435 \u0442\u043e\u0447\u043a\u0430 (\u0442.\u0435 \u0431\u043e\u043b\u0435\u0435 \u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u0430 \u043a \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u043c).<\/p>\n<pre><code class=\"python\">kps1 = AKAZE.detect(img1,None) kps1 = sorted(kps1, key = lambda x:x.response,reverse=True)[:256]<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/1a3\/d4e\/bde\/1a3d4ebdebb37dca189f119e2d64f33a.jpg\" alt=\" 256 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u201c\u0445\u043e\u0440\u043e\u0448\u0438\u0445\u201d \u0442\u043e\u0447\u0435\u043a\" title=\" 256 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u201c\u0445\u043e\u0440\u043e\u0448\u0438\u0445\u201d \u0442\u043e\u0447\u0435\u043a\" width=\"2449\" height=\"1633\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1a3\/d4e\/bde\/1a3d4ebdebb37dca189f119e2d64f33a.jpg\" data-blurred=\"true\"\/><figcaption> 256 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u201c\u0445\u043e\u0440\u043e\u0448\u0438\u0445\u201d \u0442\u043e\u0447\u0435\u043a<\/figcaption><\/figure>\n<p>\u0412\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u0442\u043e\u0447\u043a\u0438 \u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0435\u043d\u044b \u0432 1 \u043e\u0431\u043b\u0430\u0441\u0442\u0438, \u043d\u0430\u043c \u0431\u044b \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c, \u0447\u0442\u043e\u0431\u044b \u0442\u043e\u0447\u043a\u0438 \u0431\u044b\u043b\u0438 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u044b \u0431\u043e\u043b\u0435\u0435 \u0440\u0430\u0441\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0435\u043d\u043e.<\/p>\n<p>\u041f\u043e\u0434\u0435\u043b\u0438\u043c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 4 \u043a\u0432\u0430\u0434\u0440\u0430\u043d\u0442\u0430 \u0438 \u043d\u0430\u0439\u0434\u0435\u043c 64 \u043b\u0443\u0447\u0448\u0438\u0445 \u0442\u043e\u0447\u043a\u0438 \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u043a\u0432\u0430\u0434\u0440\u0430\u043d\u0442\u0435. \u0421\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u043b\u0435\u0433\u043a\u043e, \u043a\u0430\u0436\u0434\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0438\u043c\u0435\u0435\u0442 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b x \u0438 y, \u043f\u043e \u043d\u0438\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043a\u0432\u0430\u0434\u0440\u0430\u043d\u0442.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/3aa\/f83\/8fd\/3aaf838fd73813ed8827f8631b5cd875.jpg\" alt=\" 64 \u043b\u0443\u0447\u0448\u0438\u0445 \u0442\u043e\u0447\u043a\u0438 \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u043a\u0432\u0430\u0434\u0440\u0430\u043d\u0442\u0435\" title=\" 64 \u043b\u0443\u0447\u0448\u0438\u0445 \u0442\u043e\u0447\u043a\u0438 \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u043a\u0432\u0430\u0434\u0440\u0430\u043d\u0442\u0435\" width=\"2449\" height=\"1633\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3aa\/f83\/8fd\/3aaf838fd73813ed8827f8631b5cd875.jpg\" data-blurred=\"true\"\/><figcaption> 64 \u043b\u0443\u0447\u0448\u0438\u0445 \u0442\u043e\u0447\u043a\u0438 \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u043a\u0432\u0430\u0434\u0440\u0430\u043d\u0442\u0435<\/figcaption><\/figure>\n<p>\u0423\u0436\u0435 \u043b\u0443\u0447\u0448\u0435, \u043d\u043e \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e, \u0442\u043e\u0447\u043a\u0438 \u0440\u0430\u0437\u0431\u0440\u043e\u0441\u0430\u043d\u044b \u043d\u0435 \u0442\u0430\u043a \u0440\u0430\u0432\u043d\u043e\u043c\u0435\u0440\u043d\u043e \u043a\u0430\u043a \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b.  \u0412\u0432\u0435\u0434\u0435\u043c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u2013 \u0432 \u0440\u0430\u0434\u0438\u0443\u0441\u0435 40 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043d\u0435 \u0431\u043e\u043b\u0435\u0435 3 \u0442\u043e\u0447\u0435\u043a.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/e6e\/028\/0e5\/e6e0280e5956f5d1cc7f22a3c7122312.jpg\" alt=\"\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u0432 40 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439\" title=\"\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u0432 40 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439\" width=\"2449\" height=\"1633\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e6e\/028\/0e5\/e6e0280e5956f5d1cc7f22a3c7122312.jpg\" data-blurred=\"true\"\/><figcaption>\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u0432 40 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439<\/figcaption><\/figure>\n<p>\u0412\u044b\u0447\u0438\u0441\u043b\u0438\u043c \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u044b \u044d\u0442\u0438\u0445 \u0442\u043e\u0447\u0435\u043a \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u043c \u0432 sqlite.<\/p>\n<h4>\u041f\u043e\u0438\u0441\u043a<\/h4>\n<p>\u0410\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u043f\u043e\u0438\u0441\u043a\u0430.<\/p>\n<p>\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u043d\u0430\u0448\u0438 \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u044b \u0432 IVF, \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0435\u0433\u043e \u043d\u0430\u0442\u0440\u0435\u043d\u0438\u0440\u043e\u0432\u0430\u0432. \u0418\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0442\u043e\u0447\u043a\u0438 \u0438\u0437 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u0438\u0449\u0435\u043c \u0438 \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u043c \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u044b. \u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e IVF \u043a \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0438\u0437 256 \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u043e\u0432 \u043d\u0430\u0445\u043e\u0434\u0438\u043c \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0438\u0439 \u043f\u043e \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u044e \u0425\u044d\u043c\u043c\u0438\u043d\u0433\u0430. \u0423 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u0430 \u0435\u0441\u0442\u044c ID, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0438\u0437 \u043a\u0430\u043a\u043e\u0433\u043e \u043e\u043d \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f. \u0411\u0443\u0434\u0435\u043c \u0441\u0447\u0438\u0442\u0430\u0442\u044c, \u0447\u0442\u043e \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0445 \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u043e\u0432 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435 \u0431\u044b\u0432\u0430\u0435\u0442 (\u044d\u0442\u043e \u043d\u0435\u043f\u0440\u0430\u0432\u0434\u0430). \u0414\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u044b \u0441 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0425\u044d\u043c\u043c\u0438\u043d\u0433\u0430 > 65 \u043d\u0435 \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0435\u043c.<\/p>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441\u0447\u0438\u0442\u0430\u043b\u043e\u0441\u044c \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u043e\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u044d\u0442\u0438\u0445 \u0443\u0441\u043b\u043e\u0432\u0438\u0439:<\/p>\n<ul>\n<li>\n<p>2 \u0438 \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u0430 \u0441 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0425\u044d\u043c\u043c\u0438\u043d\u0433\u0430 &lt;= 5<\/p>\n<\/li>\n<li>\n<p>4 \u0438 \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u0430 \u0441 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0425\u044d\u043c\u043c\u0438\u043d\u0433\u0430 &lt;= 10<\/p>\n<\/li>\n<li>\n<p>6 \u0438 \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u043e\u0432 \u0441 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0425\u044d\u043c\u043c\u0438\u043d\u0433\u0430 &lt;= 15<\/p>\n<\/li>\n<li>\n<p>8 \u0438 \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u043e\u0432 \u0441 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0425\u044d\u043c\u043c\u0438\u043d\u0433\u0430 &lt;= 32<\/p>\n<\/li>\n<li>\n<p>\u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u043e\u0432 \u0441 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0425\u044d\u043c\u043c\u0438\u043d\u0433\u0430 &lt;=64 \u0431\u043e\u043b\u044c\u0448\u0435 10 \u0438 \u0438\u0445 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0432 >= 3 \u0440\u0430\u0437\u0430 \u0431\u043e\u043b\u044c\u0448\u0435, \u0447\u0435\u043c \u043c\u0435\u0434\u0438\u0430\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043d\u0443\u0436\u043d\u044b \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043b\u043e\u0436\u043d\u043e\u043f\u043e\u043b\u043e\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432, \u0442.\u043a \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0432 \u0434\u0432\u0443\u0445 \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0440\u0430\u0437\u043d\u044b\u0445 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u0445 \u043c\u043e\u0433\u0443\u0442 \u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u0447\u043a\u0438, \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u044b \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0440\u0430\u0432\u043d\u044b, \u043b\u0438\u0431\u043e \u043e\u0447\u0435\u043d\u044c \u0431\u043b\u0438\u0437\u043a\u0438 \u043f\u043e \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u044e \u0425\u044d\u043c\u043c\u0438\u043d\u0433\u0430.<\/p>\n<\/p>\n<details class=\"spoiler\">\n<summary>\u0422\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435, \u043d\u043e \u0432 \u043a\u043e\u0434\u0435<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">def median(lst):     list_length = len(lst)     index = (list_length - 1) \/\/ 2     if (list_length % 2):         return lst[index][1]     else:         return (lst[index][1] + lst[index + 1][1])\/2.0   def _akaze_reverse_search(D, I, file_name):     levels_threshold = [2, 4, 6, 10]     levels = [{}, {}, {}, {}]     all_points = {}     for i in range(len(I)):         point_dist = D[i]         if(point_dist > 65):             continue         point_id = I[i]         image_id = point_id_to_image_id_map[point_id]         if image_id == file_name:  # skip original image             continue         if point_dist &lt;= 5:             levels[0][image_id] = levels[0].get(image_id, 0)+1         if point_dist &lt;= 10:             levels[1][image_id] = levels[1].get(image_id, 0)+1         if point_dist &lt;= 15:             levels[2][image_id] = levels[2].get(image_id, 0)+1         if point_dist &lt;= 32:             levels[3][image_id] = levels[3].get(image_id, 0)+1          all_points[image_id] = all_points.get(image_id, 0)+1      for i in range(4):         if(len(levels[i]) > 0):             sorted_levels = sorted(levels[i].items(), key=lambda item: item[1])             if sorted_levels[-1][1] >= levels_threshold[i]:                 print({\"data\": sorted_levels[-1], \"level\": i})                 return [sorted_levels[-1][0]]      if len(all_points) >= 2:         sorted_all_points = sorted(all_points.items(), key=lambda item: item[1])         median_number_of_points = median(sorted_all_points)         if sorted_all_points[-1][1] >= 10:             if((sorted_all_points[-1][1]\/median_number_of_points) >= 3):                 print(sorted_all_points[-1])                 return [sorted_all_points[-1][0]]     return []<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b<\/summary>\n<div class=\"spoiler__content\">\n<p>\u041d\u0438\u0436\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u0434\u0435\u0442\u0435\u043a\u0446\u0438\u0438 \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u043e\u0432 \u043d\u0430 \u0433\u0440\u0430\u043d\u0438 \u0434\u0435\u0442\u0435\u043a\u0446\u0438\u0438 (\u0435\u0449\u0435 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0438 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0431\u044b \u0438\u0445 \u043d\u0435 \u043d\u0430\u0448\u0435\u043b) \u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u044f\u0442\u043e\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u0435, \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u043e\u0432 \u0441 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0425\u044d\u043c\u043c\u0438\u043d\u0433\u0430 &lt;= 64 \u0431\u043e\u043b\u044c\u0448\u0435 10 \u0438 \u0438\u0445 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0432 >= 3 \u0440\u0430\u0437\u0430 \u0431\u043e\u043b\u044c\u0448\u0435, \u0447\u0435\u043c \u043c\u0435\u0434\u0438\u0430\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435. \u041c\u043e\u0436\u043d\u043e \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u0447\u0442\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u043e\u0447\u043a\u0438 \u0441\u043e\u043e\u0442\u043d\u0435\u0441\u0435\u043d\u044b \u043d\u0435\u0432\u0435\u0440\u043d\u043e.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/61a\/d55\/eeb\/61ad55eeba80d20488a742cf1ed4bd03.png\" width=\"4082\" height=\"2449\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/61a\/d55\/eeb\/61ad55eeba80d20488a742cf1ed4bd03.png\"\/><figcaption><\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/09a\/8bc\/8bc\/09a8bc8bcc2822d13aca718a053f4e2b.png\" width=\"5318\" height=\"1633\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/09a\/8bc\/8bc\/09a8bc8bcc2822d13aca718a053f4e2b.png\"\/><figcaption><\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9b0\/aee\/649\/9b0aee6490a7ebe03d4f482345ab5b3a.png\" width=\"3270\" height=\"1283\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/9b0\/aee\/649\/9b0aee6490a7ebe03d4f482345ab5b3a.png\"\/><figcaption><\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1e5\/c07\/879\/1e5c078796d10143be28252385cc9129.png\" width=\"4898\" height=\"1633\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1e5\/c07\/879\/1e5c078796d10143be28252385cc9129.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043e\u043c\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/115\/0ce\/a40\/1150cea40c4f2ba234bb8578e36da039.png\" width=\"3521\" height=\"2310\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/115\/0ce\/a40\/1150cea40c4f2ba234bb8578e36da039.png\"\/><figcaption><\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3e8\/5ec\/1a5\/3e85ec1a59ed9421214812ab8a26719a.png\" width=\"3285\" height=\"2048\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3e8\/5ec\/1a5\/3e85ec1a59ed9421214812ab8a26719a.png\"\/><figcaption><\/figcaption><\/figure>\n<\/div>\n<\/details>\n<h3>nn<\/h3>\n<p>\u0417\u0430\u0434\u0430\u0447\u0430: \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u043e\u0438\u0441\u043a, \u043f\u043e\u0438\u0441\u043a \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439, \u0430\u0432\u0442\u043e\u0442\u0435\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/p>\n<p>\u0421\u0435\u043c\u0430\u043d\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u043e\u0438\u0441\u043a, \u043f\u043e\u0438\u0441\u043a \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439: \u0432\u0441\u0435 \u0442\u0430\u043a \u0436\u0435, \u043a\u0430\u043a \u0438 \u0432 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439 \u0441\u0442\u0430\u0442\u044c\u0435, \u043d\u043e \u0432 \u044d\u0442\u043e\u0442 \u0440\u0430\u0437 \u0432\u043c\u0435\u0441\u0442\u043e <a href=\"https:\/\/github.com\/openai\/CLIP\" rel=\"noopener noreferrer nofollow\">CLIP<\/a> ViT-B\/32 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f ViT-B\/16<\/p>\n<p>\u0410\u0432\u0442\u043e\u0442\u0435\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435: Resnet50 \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0438 Resnet18 \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u0441\u0446\u0435\u043d\u044b \u0438 indoor\/outdoor. Pre-trained \u043d\u0430 \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u0435 <a href=\"https:\/\/github.com\/CSAILVision\/places365\" rel=\"noopener noreferrer nofollow\">Places365<\/a>. \u0422\u043e\u0447\u043d\u043e\u0441\u0442\u044c \u043e\u0442\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0436\u0435\u043b\u0430\u0442\u044c \u043b\u0443\u0447\u0448\u0435\u0433\u043e, \u043d\u043e \u044d\u0442\u043e \u0445\u043e\u0442\u044c \u043a\u0430\u043a\u0430\u044f-\u0442\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430<\/summary>\n<div class=\"spoiler__content\">\n<p>\u042f \u043d\u0435 \u0443\u0441\u0442\u0430\u044e \u043f\u043e\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u043d\u043e\u0433\u043e \u0432\u0441\u0435\u0433\u043e \u0437\u043d\u0430\u0435\u0442 clip. \u041f\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c pale blue dot\/a photo made by voyager\/voyager-1, \u043f\u0435\u0440\u0432\u043e\u0435 \u0444\u043e\u0442\u043e \u2013 \u0432\u043e\u0441\u0441\u043e\u0437\u0434\u0430\u043d\u043d\u0430\u044f \u0432 blender 8K \u0432\u0435\u0440\u0441\u0438\u044f \u0437\u043d\u0430\u043c\u0435\u043d\u0438\u0442\u043e\u0439 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/Pale_Blue_Dot\" rel=\"noopener noreferrer nofollow\">Pale Blue Dot<\/a>, \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u0438 \u0417\u0435\u043c\u043b\u0438, \u0441\u0434\u0435\u043b\u0430\u043d\u043d\u043e\u0439 \u043a\u043e\u0441\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0437\u043e\u043d\u0434\u043e\u043c \u00ab\u0412\u043e\u044f\u0434\u0436\u0435\u0440-1\u00bb.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/fb5\/eba\/94d\/fb5eba94d83e644dc4c95628f38b91f6.png\" alt=\"a photo made by voyager\" title=\"a photo made by voyager\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/fb5\/eba\/94d\/fb5eba94d83e644dc4c95628f38b91f6.png\"\/><figcaption>a photo made by voyager<\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/4ce\/95e\/ce1\/4ce95ece11f7cbb8634fabf3ddba45d9.png\" alt=\"sand dunes\" title=\"sand dunes\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/4ce\/95e\/ce1\/4ce95ece11f7cbb8634fabf3ddba45d9.png\"\/><figcaption>sand dunes<\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/bb5\/a85\/3c1\/bb5a853c144c17c84de687c41fbda5a4.png\" alt=\"a photo of forest and river\" title=\"a photo of forest and river\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/bb5\/a85\/3c1\/bb5a853c144c17c84de687c41fbda5a4.png\"\/><figcaption>a photo of forest and river<\/figcaption><\/figure>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440\u044b \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 <\/summary>\n<div class=\"spoiler__content\">\n<p>\u041f\u0435\u0440\u0432\u043e\u0435 \u0444\u043e\u0442\u043e \u2013 \u0444\u043e\u0442\u043e \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u0438\u0434\u0435\u0442 \u043f\u043e\u0438\u0441\u043a<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9bc\/2fe\/b66\/9bc2feb66fa60a96bf5e317806e7890b.png\" alt=\"\" title=\"\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/9bc\/2fe\/b66\/9bc2feb66fa60a96bf5e317806e7890b.png\"\/><figcaption><\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/616\/24b\/bf0\/61624bbf07d95c715d52fa3bf31318ad.png\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/616\/24b\/bf0\/61624bbf07d95c715d52fa3bf31318ad.png\"\/><figcaption><\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f53\/2ae\/3b3\/f532ae3b304d277f380917b040c1d892.png\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f53\/2ae\/3b3\/f532ae3b304d277f380917b040c1d892.png\"\/><figcaption><\/figcaption><\/figure>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0442\u0435\u0433\u043e\u0432<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/876\/81c\/5ef\/87681c5ef764b6b51316b34396c86974.jpg\" alt=\"['far-away horizon', 'natural', 'natural light', 'outdoor', 'iceberg', 'ocean', 'boating', 'open area']\" title=\"['far-away horizon', 'natural', 'natural light', 'outdoor', 'iceberg', 'ocean', 'boating', 'open area']\" width=\"3953\" height=\"4941\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/876\/81c\/5ef\/87681c5ef764b6b51316b34396c86974.jpg\" data-blurred=\"true\"\/><figcaption>[&#8216;far-away horizon&#8217;, &#8216;natural&#8217;, &#8216;natural light&#8217;, &#8216;outdoor&#8217;, &#8216;iceberg&#8217;, &#8216;ocean&#8217;, &#8216;boating&#8217;, &#8216;open area&#8217;]<\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/946\/a57\/291\/946a57291857595b4e0c5da2d5d419e6.jpg\" alt=\"['skyscraper', 'vertical components', 'downtown', 'man-made', 'canal\/urban', 'open area', 'outdoor', 'natural light']\" title=\"['skyscraper', 'vertical components', 'downtown', 'man-made', 'canal\/urban', 'open area', 'outdoor', 'natural light']\" width=\"4055\" height=\"2943\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/946\/a57\/291\/946a57291857595b4e0c5da2d5d419e6.jpg\" data-blurred=\"true\"\/><figcaption>[&#8216;skyscraper&#8217;, &#8216;vertical components&#8217;, &#8216;downtown&#8217;, &#8216;man-made&#8217;, &#8216;canal\/urban&#8217;, &#8216;open area&#8217;, &#8216;outdoor&#8217;, &#8216;natural light&#8217;]<\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/259\/802\/8d2\/2598028d29adac46bd71b8df912f231a.jpg\" alt=\"['natural', 'open area', 'foliage', 'outdoor', 'no horizon', 'natural light', 'vegetation', 'waterfall', 'trees', 'rainforest', 'leaves']\" title=\"['natural', 'open area', 'foliage', 'outdoor', 'no horizon', 'natural light', 'vegetation', 'waterfall', 'trees', 'rainforest', 'leaves']\" width=\"1710\" height=\"2138\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/259\/802\/8d2\/2598028d29adac46bd71b8df912f231a.jpg\" data-blurred=\"true\"\/><figcaption>[&#8216;natural&#8217;, &#8216;open area&#8217;, &#8216;foliage&#8217;, &#8216;outdoor&#8217;, &#8216;no horizon&#8217;, &#8216;natural light&#8217;, &#8216;vegetation&#8217;, &#8216;waterfall&#8217;, &#8216;trees&#8217;, &#8216;rainforest&#8217;, &#8216;leaves&#8217;]<\/figcaption><\/figure>\n<\/div>\n<\/details>\n<h2>IPFS<\/h2>\n<p>IPFS (Inter Planetary File system) \u2013 \u044d\u0442\u043e \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u043d\u043e-\u0430\u0434\u0440\u0435\u0441\u0443\u0435\u043c\u0430\u044f p2p \u0444\u0430\u0439\u043b\u043e\u043e\u0431\u043c\u0435\u043d\u043d\u0430\u044f \u0441\u0435\u0442\u044c. \u0411\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0451 \u0434\u043b\u044f \u0434\u0435\u0446\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f\/\u0440\u0430\u0437\u0434\u0430\u0447\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a.<\/p>\n<figure class=\"float\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/0b0\/d9f\/a0c\/0b0d9fa0c54e2237651ef083f6c383aa.png\" width=\"347\" height=\"174\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0b0\/d9f\/a0c\/0b0d9fa0c54e2237651ef083f6c383aa.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0421\u0434\u0435\u043b\u0430\u0435\u043c \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 \u0442\u0443\u043c\u0431\u043b\u0435\u0440, \u043f\u0440\u0438 \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0432\u0441\u0435 \u0441\u0441\u044b\u043b\u043a\u0438 \u0442\u0438\u043f\u0430 <a href=\"https:\/\/example.com\/images\/1.jpg\" rel=\"noopener noreferrer nofollow\">https:\/\/example.com\/images\/1.jpg<\/a> \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432 ipfs \u0441\u0441\u044b\u043b\u043a\u0438.<\/p>\n<p>\u041e\u0431\u044b\u0447\u043d\u043e, \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u0444\u0430\u0439\u043b\u044b \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u0442\u0430\u043a: <code>\/ipfs\/&lt;CID><\/code>, \u0433\u0434\u0435 CID \u044d\u0442\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430 (Content Identifier). \u0422\u043e \u0435\u0441\u0442\u044c, \u0435\u0441\u043b\u0438 \u043c\u044b \u0445\u043e\u0442\u0438\u043c, \u0447\u0442\u043e\u0431\u044b \u043a\u043b\u0438\u0435\u043d\u0442 \u043c\u043e\u0433 \u0441\u043a\u0430\u0447\u0430\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u0447\u0435\u0440\u0435\u0437 ipfs, \u0435\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0434\u0430\u0442\u044c ipfs \u0445\u0435\u0448\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u0434\u043e \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u0431\u0434, \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u043f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0438.\u0442.\u0434. \u041d\u0435 \u043e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u043d\u043e. \u042d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u044d\u043b\u0435\u0433\u0430\u043d\u0442\u043d\u043e \u0440\u0435\u0448\u0438\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e IPNS. IPNS (InterPlanetary Name System) \u2013 \u044d\u0442\u043e \u0434\u0435\u0446\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f dns \u0434\u043b\u044f ipfs. \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043d\u0430 \u0445\u0435\u0448 \u0444\u0430\u0439\u043b\u0430 (\u043f\u0430\u043f\u043a\u0438). \u041c\u043e\u0436\u043d\u043e \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u0445\u0435\u0448, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0441\u044b\u043b\u0430\u0435\u0442\u0441\u044f \u0430\u0434\u0440\u0435\u0441, \u0431\u0435\u0437 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0430\u0434\u0440\u0435\u0441\u0430. \u0422\u0430\u043a \u043c\u044b \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u043b\u0438 content-based \u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u044e \u0432 address-based. IPNS \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0441\u043b\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u043e\u043c\u043d\u0438\u0442\u044c <code>\/ipns\/k51qzi5uqu5dhlil169t1my3wmb5zzwugvtjcq6duc2bhgcbjsbc77wmdqwbsh<\/code>,\u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c DNSLink \u2013 ipfs \u0443\u043c\u0435\u0435\u0442 \u043f\u043e\u0434\u0442\u044f\u0433\u0438\u0432\u0430\u0442\u044c ipns \u0430\u0434\u0440\u0435\u0441 \u0438\u0437 TXT \u0437\u0430\u043f\u0438\u0441\u0438 \u0434\u043e\u043c\u0435\u043d\u0430. <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/104\/305\/721\/104305721710f441aa98013a7408affb.JPG\" width=\"960\" height=\"75\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0444\u0430\u0439\u043b \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a <a href=\"http:\/\/127.0.0.1:8080\/ipns\/ipns.scenery.cx\/thumbnails\/71.jpg\" rel=\"noopener noreferrer nofollow\">http:\/\/127.0.0.1:8080\/ipns\/ipns.scenery.cx\/thumbnails\/71.jpg<\/a> (\u041a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0433\u0430\u0434\u0430\u0442\u044c\u0441\u044f, \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 <a href=\"https:\/\/ipfs.io\/#install\" rel=\"noopener noreferrer nofollow\">IPFS \u043a\u043b\u0438\u0435\u043d\u0442\u0430<\/a>). <\/p>\n<p>\u0418\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u0444\u0430\u043a\u0442: ipfs \u043a\u043b\u0438\u0435\u043d\u0442 30 \u043c\u0438\u043d\u0443\u0442 \u0440\u0430\u0437\u0434\u0430\u0435\u0442 \u0444\u0430\u0439\u043b\u044b \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u0441\u043a\u0430\u0447\u0430\u043b. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0447\u0442\u043e-\u0442\u043e \u043d\u0430\u043f\u043e\u0434\u043e\u0431\u0438\u0435 distributed CDN.<\/p>\n<h3>crud_file_server<\/h3>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <a href=\"https:\/\/docs.ipfs.io\/reference\/http\/api\/\" rel=\"noopener noreferrer nofollow\">IPFS http api<\/a> \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 ipfs\/ipns. \u041c\u043e\u0436\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u043f\u043e http, \u043b\u0438\u0431\u043e \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u0443\u044e \u043f\u0430\u043f\u043a\u0443 \u0441 ipfs.<\/p>\n<h3>\u0414\u043e\u0431\u044b\u0447\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439<\/h3>\n<p>\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432\u0437\u044f\u043b \u0441 \u0441\u0430\u0431\u0440\u0435\u0434\u0434\u0438\u0442\u0430 \/r\/EarthPorn \u0438 \/r\/LandscapePhotography. \u041e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 API \u043d\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 1000 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u043f\u043e\u0441\u0442\u043e\u0432. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0435 Reddit API \u043e\u0442 <a href=\"https:\/\/pushshift.io\/\" rel=\"noopener noreferrer nofollow\">https:\/\/pushshift.io\/<\/a>. \u042d\u0442\u043e \u043d\u0435\u043a\u043e\u043c\u0435\u0440\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043f\u043e \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0443 \u0420\u0435\u0434\u0434\u0438\u0442\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043d\u0430 \u043f\u043e\u0436\u0435\u0440\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u044f. <\/p>\n<h2>Image tools<\/h2>\n<p>\u0420\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u0443\u0442\u0438\u043b\u0438\u0442\u044b \u0434\u043b\u044f \u043e\u0431\u043b\u0435\u0433\u0447\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c\u0438.<\/p>\n<h3>Yandex reverse image search<\/h3>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043f\u043e\u0438\u0441\u043a \u043f\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044e \u044f\u043d\u0434\u0435\u043a\u0441\u0430, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0439\u0442\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 \u0431\u043e\u043b\u044c\u0448\u0435\u043c \u0440\u0430\u0437\u043c\u0435\u0440\u0435. API \u044f \u043d\u0435 \u043d\u0430\u0448\u0435\u043b, \u0437\u0430\u0442\u043e \u043d\u0430\u0448\u0435\u043b <a href=\"https:\/\/yandex.ru\/legal\/pictures_api\/\" rel=\"noopener noreferrer nofollow\">https:\/\/yandex.ru\/legal\/pictures_api\/<\/a>. \u0412\u0438\u0434\u0438\u043c\u043e \u043a\u0430\u043a\u043e\u0435-\u0442\u043e \u0437\u0430\u043a\u0440\u044b\u0442\u043e\u0435 \u0430\u043f\u0438 \u0432\u0441\u0435 \u0436\u0435 \u0435\u0441\u0442\u044c. \u041c\u044b \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0430\u0440\u0441\u0438\u0442\u044c.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/79d\/627\/2f6\/79d6272f6ae08b39669ff58d6f477bec.png\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/79d\/627\/2f6\/79d6272f6ae08b39669ff58d6f477bec.png\"\/><figcaption><\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/7e2\/58b\/a11\/7e258ba11b06e4a674fcaa56e3d13eae.JPG\" width=\"1106\" height=\"395\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u0442\u0430\u0440\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043d\u043e\u0432\u044b\u043c \u043d\u0435\u043b\u044c\u0437\u044f \u2013 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0447\u0430\u0441\u0442\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441 \u0431\u043e\u043b\u0435\u0435 \u0432\u044b\u0441\u043e\u043a\u0438\u043c \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c \u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u043e \u0430\u043f\u0441\u043a\u0435\u0439\u043b\u043e\u043c \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u0430, \u043b\u0438\u0431\u043e \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0431\u043e\u043b\u044c\u0448\u0435, \u0430 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u043d\u0438\u0436\u0435. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0442\u044c \u043d\u043e\u0432\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441\u043e \u0441\u0442\u0430\u0440\u044b\u043c \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0432\u0440\u0443\u0447\u043d\u0443\u044e. <s>\u041e\u0431\u0445\u043e\u0434 rate-limit&#8217;\u0430 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0447\u0438\u0442\u0430\u0442\u0435\u043b\u044f\u043c \u0434\u043b\u044f \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f.<\/s><\/p>\n<h3>Border detection<\/h3>\n<p>\u0410\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u043a\u0446\u0438\u0438 \u0440\u0430\u043c\u043e\u043a \u0443 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439. \u041c\u043d\u043e\u0433\u043e \u043b\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432. <\/p>\n<ol>\n<li>\n<p>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0446\u0432\u0435\u0442 \u043a\u0440\u0430\u0439\u043d\u0435\u0433\u043e \u043b\u0435\u0432\u043e\u0433\u043e \u043f\u0438\u043a\u0441\u0435\u043b\u044f<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u043c \u043f\u043e\u043f\u0438\u043a\u0441\u0435\u043b\u044c\u043d\u0443\u044e \u0440\u0430\u0437\u043d\u0438\u0446\u0443 \u043c\u0435\u0436\u0434\u0443 \u043d\u0430\u0448\u0435\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u043e\u0439 \u0438 \u043b\u0435\u0432\u044b\u043c \u043a\u0440\u0430\u0439\u043d\u0435\u043c \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u043c<\/p>\n<\/li>\n<li>\n<p>&#171;\u0423\u0441\u0438\u043b\u044f\u0435\u043c&#187; \u0440\u0430\u0437\u043d\u0438\u0446\u0443<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u043c bbox (\u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a, \u043d\u0435\u043d\u0443\u043b\u0435\u0432\u044b\u0445 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439)<\/p>\n<\/li>\n<li>\n<p>\u0427\u0442\u043e\u0431\u044b \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0431\u043e\u043b\u044c\u0448\u0435 \u043b\u043e\u0436\u043d\u043e\u043f\u043e\u043b\u043e\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432, \u0441\u0447\u0438\u0442\u0430\u0435\u043c, \u0447\u0442\u043e \u0443 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0435\u0441\u0442\u044c \u0440\u0430\u043c\u043a\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u043e\u043d\u0438 \u043f\u0430\u0440\u043d\u044b\u0435 (\u0442.\u0435 \u0435\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043f\u043e\u043b\u043e\u0441\u0430 \u0441\u0432\u0435\u0440\u0445\u0443, \u0442\u043e \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u043f\u043e\u043b\u043e\u0441\u0430 c\u043d\u0438\u0437\u0443 \u0438.\u0442.\u0434)<\/p>\n<\/li>\n<\/ol>\n<details class=\"spoiler\">\n<summary>\u041e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/e52\/a20\/395\/e52a2039567c274d47165a4efc7b8ef9.jpg\" width=\"2858\" height=\"2446\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e52\/a20\/395\/e52a2039567c274d47165a4efc7b8ef9.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u0441\u043b\u0435 \u0432\u0441\u0435\u0445 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u0439<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/54c\/a5d\/81c\/54ca5d81c57a8e481a334d26748e4665.png\" width=\"2858\" height=\"2446\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/54c\/a5d\/81c\/54ca5d81c57a8e481a334d26748e4665.png\"\/><figcaption><\/figcaption><\/figure>\n<\/div>\n<\/details>\n<h3>\u041f\u043e\u043b\u0443\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0434\u0435\u0442\u0435\u043a\u0446\u0438\u044f \u0432\u043e\u0442\u0435\u0440\u043c\u0430\u0440\u043e\u043a <\/h3>\n<p>\u0418\u0434\u0435\u044f \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043d\u0430\u044f \u2013 \u0434\u0435\u0442\u0435\u043a\u0442\u0438\u043c \u0442\u0435\u043a\u0441\u0442 \u043d\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0435, \u0435\u0441\u043b\u0438 \u0442\u0435\u043a\u0441\u0442 \u0435\u0441\u0442\u044c, \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0443\u0434\u0430\u043b\u044f\u0435\u043c (\u0438\u043b\u0438 \u043d\u0435 \u0443\u0434\u0430\u043b\u044f\u0435\u043c, \u0430 \u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u043c \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u043d\u043d\u044b\u0439 \u0442\u0435\u043a\u0441\u0442 \u0438 \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0432\u0440\u0443\u0447\u043d\u0443\u044e, \u043f\u043e\u0441\u043b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438). \u0422.\u043a \u0434\u0430\u0442\u0430\u0441\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u043d\u044b\u0439 (\u043f\u0435\u0439\u0437\u0430\u0436\u0438, \u043e\u0431\u043e\u0438, \u043f\u0440\u0438\u0440\u043e\u0434\u0430, \u0430\u0442\u043c\u043e\u0441\u0444\u0435\u0440\u043d\u044b\u0435 \u0444\u043e\u0442\u043e), \u0442\u043e \u0442\u0435\u043a\u0441\u0442\u0430 \u0432 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\u0445 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435\u0442. Tesseract \u0434\u043b\u044f \u0437\u0430\u0434\u0430\u0447\u0438 \u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u2013 \u0442\u0435\u043a\u0441\u0442 \u0440\u0430\u0437\u043d\u044b\u0445 \u0448\u0440\u0438\u0444\u0442\u043e\u0432, \u0440\u0430\u0437\u043d\u043e\u0433\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430. EasyOCR \u043e\u0442\u043b\u0438\u0447\u043d\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0438, \u0442\u0430\u043a \u043a\u0430\u043a \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u043b\u0441\u044f \u0434\u043b\u044f ocr \u0432 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0443\u0441\u043b\u043e\u0432\u0438\u044f\u0445 (\u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043a\u0430\u043d\u044b \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u043d\u043e \u0438 \u0442\u0435\u043a\u0441\u0442 in the wild)<\/p>\n<p><a href=\"https:\/\/github.com\/JaidedAI\/EasyOCR\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/JaidedAI\/EasyOCR<\/a> <\/p>\n<h2>\u0427\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c\/\u041c\u044b\u0441\u043b\u0438\/\u0418\u0434\u0435\u0438<\/h2>\n<p>\u0423\u043b\u0443\u0447\u0448\u0438\u0442\u044c \u043c\u043e\u0436\u043d\u043e \u0432\u0441\u0451 \ud83d\ude00<\/p>\n<p>\u041c\u043e\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c phash(DCT) \u043d\u0430 \u0447\u0442\u043e-\u043d\u0438\u0431\u0443\u0434\u044c \u043d\u043e\u0432\u043e\u0435, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043d\u0430 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u044b \u043d\u0430 \u0431\u0430\u0437\u0435 ML (\u043f\u0440\u0438\u0432\u0435\u0442 Apple NeuralHash). \u0426\u0435\u043b\u0435\u0441\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u043c\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u2013 phash(DCT) \u0438 \u0442\u0430\u043a \u043e\u0442\u043b\u0438\u0447\u043d\u043e \u0440\u0435\u0448\u0430\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0443 \u043f\u043e\u0438\u0441\u043a\u0430 near-duplicate \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u0438 \u044f \u0441\u043e\u043c\u043d\u0435\u0432\u0430\u044e\u0441\u044c \u0447\u0442\u043e ML-based \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u044b \u043e\u0431\u043b\u0430\u0434\u0430\u044e\u0442 \u043a\u0430\u043a\u0438\u043c\u0438-\u0442\u043e \u043e\u0442\u043b\u0438\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430\u043c\u0438 (\u0438\u0441\u043a\u0430\u0442\u044c \u043a\u0440\u043e\u043f\u044b \u043e\u043d\u0438 \u0432\u0440\u044f\u0434 \u043b\u0438 \u0441\u043c\u043e\u0433\u0443\u0442)<\/p>\n<p>\u041c\u043e\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c AKAZE \u043d\u0430 \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0435 \u0434\u0435\u0442\u0435\u043a\u0442\u043e\u0440\u044b\/\u0434\u0435\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0440\u044b, \u043d\u043e \u044d\u0442\u043e \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0442 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u043a \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430. <\/p>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0435\u0441\u0442\u044c \u043a\u0430\u043a\u0430\u044f-\u0442\u043e \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044c, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u0449\u0435\u0442 \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043b\u0443\u0447\u0448\u0435, \u0447\u0435\u043c CLIP ViT B\/16. \u0411\u044b\u043b\u043e \u0431\u044b \u043a\u043b\u0430\u0441\u0441\u043d\u043e, \u0435\u0441\u043b\u0438 \u0431\u044b \u0434\u0430\u043d\u043d\u0430\u044f \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044c \u043c\u043e\u0433\u043b\u0430 \u0434\u043e\u043e\u0431\u0443\u0447\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u043d\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u044f \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u0438\u0441\u043a\u0430, \u0432 \u0438\u0434\u0435\u0430\u043b\u0435 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438, \u0431\u0435\u0437 \u0440\u0430\u0437\u043c\u0435\u0442\u043a\u0438 \u043d\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u0422\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u0430\u0437\u0430\u0442\u044c \u0438 \u043f\u0440\u043e \u0430\u0432\u0442\u043e\u0442\u0435\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435.<\/p>\n<p>\u041c\u043e\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c rgb \u0433\u0438\u0441\u0442\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u043d\u0430 \u0447\u0442\u043e-\u0442\u043e \u0431\u043e\u043b\u0435\u0435 \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u043e\u0435, \u0442.\u043a. \u0441\u0435\u0439\u0447\u0430\u0441 \u0432\u0435\u043a\u0442\u043e\u0440 \u0438\u0437 4096 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043d\u0430 60-70 \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u043e\u0432 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u043d\u0443\u043b\u0435\u0439, \u0430 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043d\u0443\u043b\u0438 \u2013 \u044d\u0442\u043e \u043a\u0430\u043a-\u0442\u043e \u043d\u0435 \u043e\u0447\u0435\u043d\u044c. (\u044f \u043f\u0440\u043e\u0431\u043e\u0432\u0430\u043b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c ANNS \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442 sparse \u0432\u0435\u043a\u0442\u043e\u0440\u0430, \u043d\u043e \u0442\u0430\u043c \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0438 \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b). \u0421\u0435\u0439\u0447\u0430\u0441 rgb \u0433\u0438\u0441\u0442\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u043d\u0435 \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u044e\u0442 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439 \u0432 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0438, \u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0445 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e. \u041c\u043e\u0436\u043d\u043e \u0440\u0430\u0437\u0431\u0438\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u0431\u043b\u043e\u043a\u0438, \u043d\u0430\u0439\u0442\u0438 \u0433\u0438\u0441\u0442\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u044d\u0442\u0438\u0445 \u0431\u043b\u043e\u043a\u043e\u0432 \u0438 \u0438\u0441\u043a\u0430\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0443 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0431\u043b\u043e\u043a\u0438 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u0445\u043e\u0436\u0438\u0435. \u042d\u0442\u0430 \u0448\u0442\u0443\u043a\u0430 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f local color histogram \u0438 \u043f\u043e \u0438\u0434\u0435\u0435 \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u043b\u0443\u0447\u0448\u0435, \u0447\u0435\u043c global color histogram (\u0442\u043e \u0447\u0442\u043e \u0435\u0441\u0442\u044c \u0441\u0435\u0439\u0447\u0430\u0441). \u042f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043b \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0446\u0432\u0435\u0442\u043e\u0432\u044b\u043c\u0438 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0430\u043c\u0438 (YUV, HSV, LAB), \u043d\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0431\u044b\u043b\u0438 \u0445\u0443\u0436\u0435, \u0447\u0435\u043c RGB. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u044f \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0438\u0445 &#171;\u043f\u0440\u0438\u0433\u043e\u0442\u043e\u0432\u0438\u043b&#187;, \u043d\u0443\u0436\u043d\u044b \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b.  <\/p>\n<p>\u041c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c jpegXL, \u0442.\u043a \u043e\u043d \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u0442\u044c \u0440\u0430\u0437\u043c\u0435\u0440 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439, \u043d\u043e \u043f\u043e\u043a\u0430 \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438, <a href=\"https:\/\/caniuse.com\/jpegxl\" rel=\"noopener noreferrer nofollow\">\u043e\u043d \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0438 \u043e\u0434\u043d\u0438\u043c \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043e\u043c.<\/a><\/p>\n<h4>\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043c\u0430\u0439\u043d\u0438\u043d\u0433 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439<\/h4>\n<p>\u0414\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043c\u0430\u0439\u043d\u0438\u043d\u0433\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u043d\u0443\u0436\u0435\u043d \u043c\u043e\u0434\u0443\u043b\u044c \u0434\u043b\u044f \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u0441\u0442\u0435\u043f\u0435\u043d\u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u044f \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u0443. \u0421\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e, \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0432\u0435\u043a\u0442\u043e\u0440\u0430 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0432 nn, \u0441\u043e\u0437\u0434\u0430\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u043e\u0432 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e kmeans \u0438 \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0447\u0442\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043d\u0430\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442, \u0435\u0441\u043b\u0438 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043c\u0435\u0436\u0434\u0443 \u043d\u043e\u0432\u043e\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u043e\u0439 \u0438 \u043a\u0430\u043a\u0438\u043c-\u0442\u043e \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u043e\u043c \u043c\u0435\u043d\u044c\u0448\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u0440\u043e\u0433\u043e\u0432\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f. \u042d\u0442\u0443 \u0438\u0434\u0435\u044e \u044f \u043f\u043e\u043a\u0430 \u043d\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u043b.<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f, \u043e\u0434\u043d\u043e\u0433\u043e \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e. \u041f\u043e\u043a\u0430 \u043e\u0446\u0435\u043d\u0438\u0432\u0430\u0442\u044c \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432\u0440\u0443\u0447\u043d\u0443\u044e. \u042d\u0442\u043e\u0442 \u043c\u043e\u0434\u0443\u043b\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043e\u0434\u0438\u043d \u0438\u0437 \u0441\u0430\u043c\u044b\u0445 \u0432\u0430\u0436\u043d\u044b\u0445: \u043e\u043d \u0443\u043b\u0443\u0447\u0448\u0438\u0442 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043d\u0430 \u0432\u0441\u0435\u0445 \u0443\u0440\u043e\u0432\u043d\u044f\u0445, \u043e\u0442 \u0434\u0435\u0434\u0443\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u0438 (\u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0432\u0435\u0440\u0441\u0438\u044e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441 \u043b\u0443\u0447\u0448\u0438\u043c \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e\u043c, \u0430 \u043d\u0435 \u0442\u0443 \u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0431\u043e\u043b\u044c\u0448\u0435 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441\u0435\u0439\u0447\u0430\u0441) \u0434\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043c\u0430\u0439\u043d\u0438\u043d\u0433\u0430.<\/p>\n<p>\u041d\u0430 \u0443\u043c \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0439:<\/p>\n<ul>\n<li>\n<p>\u0421\u043a\u0430\u043d \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u0432\u043e\u0435\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u0433\u043e \u0440\u043e\u0431\u043e\u0442\u0430 (\u0441\u043b\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438)<\/p>\n<\/li>\n<li>\n<p>\u0420\u0443\u0447\u043d\u043e\u0439 \u043f\u043e\u0438\u0441\u043a \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u0432 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 (\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u0441\u0430\u0439\u0442\u044b\/\u0441\u0430\u0431\u0440\u0435\u0434\u0434\u0438\u0442\u044b) \u0438 \u0438\u0445 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0430\u0440\u0441\u0438\u043d\u0433<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u043f\u043e \u0442\u0438\u043f\u0443 Yandex reverse image search. \u0422\u0430\u043c \u0435\u0441\u0442\u044c \u0441\u0435\u043a\u0446\u0438\u044f \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439, \u0435\u0451 \u043c\u043e\u0436\u043d\u043e \u0441\u043f\u0430\u0440\u0441\u0438\u0442\u044c \u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u043d\u0430 \u043f\u043e\u0434\u043e\u0431\u0438\u0438 breadth-first search: \u043d\u0430\u0439\u0434\u0435\u043c \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043a \u0442\u0435\u043c, \u0447\u0442\u043e \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0432 \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u0435, \u0437\u0430\u0442\u0435\u043c \u043d\u0430\u0439\u0434\u0435\u043c \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u043a \u043f\u043e\u0445\u043e\u0436\u0438\u043c, \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435<\/p>\n<\/li>\n<\/ul>\n<hr\/>\n<p>\u0414\u043e\u043b\u0433\u043e \u0434\u0443\u043c\u0430\u043b \u043d\u0430\u0434 \u0442\u0435\u043c, \u043a\u0430\u043a \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u0442\u044c \u044d\u0442\u0443 \u0441\u0442\u0430\u0442\u044c\u044e. \u0417\u0430\u043a\u043e\u043d\u0447\u0443 \u0432\u043e\u0442 \u0442\u0430\u043a\u043e\u0439 \u043c\u044b\u0441\u043b\u044c\u044e. \u0414\u043b\u044f \u043c\u0435\u043d\u044f web 3.0 \u2013 \u044d\u0442\u043e \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u044b\u0435 \u0441\u0430\u043c\u043e\u043e\u0431\u0443\u0447\u0430\u044e\u0449\u0438\u0435\u0441\u044f \u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u0433\u0434\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u044b \u043c\u043e\u0433\u0443\u0442 \u0443\u0447\u0430\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0432 \u0440\u0430\u0437\u0434\u0430\u0447\u0435 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430.<\/p>\n<p><a href=\"https:\/\/github.com\/qwertyforce\/scenery\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/qwertyforce\/scenery<\/a><br \/><a href=\"https:\/\/github.com\/qwertyforce\/ambience\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/qwertyforce\/ambience<\/a><br \/><a href=\"https:\/\/scenery.cx\" rel=\"noopener noreferrer nofollow\">scenery.cx<\/a><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/578254\/\"> https:\/\/habr.com\/ru\/articles\/578254\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>~1 \u0433\u043e\u0434 \u043d\u0430\u0437\u0430\u0434 \u044f \u043d\u0430\u0447\u0430\u043b \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u0444\u043e\u0442\u043e\u0433\u0430\u043b\u0435\u0440\u0435\u044e (<s>\u043f\u0435\u0441\u043e\u0447\u043d\u0438\u0446\u0443 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0430 \u0432\u0441\u044f\u043a\u0438\u0445 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439<\/s>). \u0414\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u2013 \u044d\u0442\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0435\u0451 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0442\u0432\u0438\u043a\u0438\/\u043b\u0430\u0439\u0444\u0445\u0430\u043a\u0438\/\u043c\u0438\u043a\u0440\u043e\u0433\u0430\u0439\u0434\u044b \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u044f \u0443\u0437\u043d\u0430\u043b \u0437\u0430 \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 + \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c. <\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<hr\/>\n<h3>\u041c\u043e\u043d\u043e\u043b\u0438\u0442<\/h3>\n<p>\u0412\u043d\u0430\u0447\u0430\u043b\u0435 \u0431\u044b\u043b \u043c\u043e\u043d\u043e\u043b\u0438\u0442.  \u042f \u043f\u044b\u0442\u0430\u043b\u0441\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0432\u0441\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u043d\u0430 javascript \u2013 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u0431\u0438\u043d\u0434\u0438\u043d\u0433\u0438 OpenCV \u0434\u043b\u044f js, \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0434\u043b\u044f \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f phash \u043d\u0430 js, \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435. \u041f\u043e\u0441\u043b\u0435 \u043d\u0435\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0432\u043e\u0439\u043d\u044b \u0441 \u0443\u0442\u0435\u0447\u043a\u0430\u043c\u0438 \u043f\u0430\u043c\u044f\u0442\u0438, \u043f\u043b\u043e\u0445\u043e\u0439 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e \u0438 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u044b\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u043e\u043c, \u0431\u044b\u043b\u043e \u0440\u0435\u0448\u0435\u043d\u043e \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u043f\u043e\u0438\u0441\u043a \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u043d\u0430 python. \u0422\u0430\u043a \u043f\u0440\u043e\u0435\u043a\u0442 \u043e\u0431\u0440\u0435\u043b \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443:<\/p>\n<ul>\n<li>\n<p>scenery \u2013 frontend, backend<\/p>\n<\/li>\n<li>\n<p>ambience \u2013 \u0412\u0441\u0435 \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u043e\u0435 \u0441 \u043f\u043e\u0438\u0441\u043a\u043e\u043c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439<\/p>\n<\/li>\n<li>\n<p>crud_file_server \u2013 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u043b\u044f \u0431\u0435\u043a\u0430\u043f\u0430 \u0438 ipfs. <\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><figcaption>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442<\/figcaption><\/figure>\n<p>Nginx \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 reverse proxy \u0441\u0435\u0440\u0432\u0435\u0440\u0430. \u0422\u0430\u043c \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u0435 (https), \u043a\u043e\u043c\u043f\u0440\u0435\u0441\u0441\u0438\u044f (gzip\/brotli) \u0438 \u0440\u0430\u0437\u0434\u0430\u0447\u0430 \u0441\u0442\u0430\u0442\u0438\u043a\u0438, \u0447\u0442\u043e\u0431\u044b \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u0438\u0442\u044c Node.js.<\/p>\n<h2>nginx<\/h2>\n<p>\u041f\u0430\u0440\u0430 \u0442\u0432\u0438\u043a\u043e\u0432 \u0434\u043b\u044f nginx.<\/p>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c cloudflare, \u0440\u0430\u0437\u0443\u043c\u043d\u043e \u0437\u0430\u043f\u0440\u0435\u0442\u0438\u0442\u044c \u0432\u0441\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0434\u0443\u0442 \u043d\u0435 \u043e\u0442 cloudflare. \u0421\u043a\u0440\u0438\u043f\u0442 <a href=\"https:\/\/gist.github.com\/mkg20001\/1a4f0a5a3f55c194cc4998019edfa62a\" rel=\"noopener noreferrer nofollow\">allow_cloudflare.sh<\/a> \u0441\u043e\u0437\u0434\u0430\u0435\u0442 allow-cloudflare-only.conf, whitelist \u0441\u043e\u0441\u0442\u043e\u044f\u0449\u0438\u0439 \u0438\u0437 ip \u0430\u0434\u0440\u0435\u0441\u043e\u0432 cloudflare. \u041e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0433\u043e \u0437\u0430\u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0442\u044c.<\/p>\n<pre><code class=\"nginx\">include \/etc\/nginx\/allow-cloudflare-only.conf;<\/code><\/pre>\n<p>Next.js \u043f\u0440\u0438 \u0441\u0431\u043e\u0440\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0445\u0435\u0448 \u043a \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f\u043c \u0444\u0430\u0439\u043b\u043e\u0432, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u0447\u0442\u043e \u0444\u0430\u0439\u043b \u0441 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u043c \u0438\u043c\u0435\u043d\u0435\u043c \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u043f\u043e\u043c\u0435\u043d\u044f\u0435\u0442 \u0441\u0432\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435. \u042d\u0442\u0443 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0438\u044f \u0441\u0430\u0439\u0442\u0430 \u2013 \u0434\u043b\u044f \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0444\u0430\u0439\u043b\u043e\u0432 \u0432\u044b\u0441\u0442\u0430\u0432\u0438\u043c \u0431\u043e\u043b\u044c\u0448\u043e\u0439 max-age \u0438 \u0430\u0442\u0440\u0438\u0431\u0443\u0442 immutable, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0443 \u043d\u0435 \u0440\u0435\u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u0435\u0448. \u041a\u043b\u0430\u0441\u0441\u043d\u044b\u0439 \u0430\u0442\u0440\u0438\u0431\u0443\u0442, \u043a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, <a href=\"https:\/\/caniuse.com\/?search=immutable\" rel=\"noopener noreferrer nofollow\">\u043c\u0430\u043b\u043e \u0433\u0434\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f<\/a>.<\/p>\n<details class=\"spoiler\">\n<summary>\u0444\u0430\u0439\u043b\u044b<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<\/div>\n<\/details>\n<pre><code class=\"nginx\">expires 1y; add_header Cache-Control \"public, immutable\";<\/code><\/pre>\n<h2>scenery<\/h2>\n<p>\u0422\u0435\u043a\u0443\u0449\u0438\u0439 \u0441\u0442\u0435\u043a: Node.js + MongoDB + TypeScript + Next.js + Fastify + Material UI<\/p>\n<p>\u042f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e Next.js, \u0442.\u043a \u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u044d\u0442\u043e \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0439 \u0438 \u0443\u0434\u043e\u0431\u043d\u044b\u0439 react \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 CSR(Client-side rendering), SSR (server-side rendering) \u0438 ISR(Incremental Static Regeneration). \u041f\u043e \u0441\u0443\u0442\u0438, ISR \u044d\u0442\u043e SSR \u0441 \u043a\u0435\u0448\u0435\u043c, \u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u0434\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 revalidate, \u0432\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438 \u043a\u0435\u0448\u0430. \u042d\u0442\u043e\u0442 \u043a\u0435\u0448 \u0440\u0435\u0432\u0430\u043b\u0438\u0434\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435, \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0415\u0433\u043e \u0443\u0434\u043e\u0431\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u043c\u043d\u043e\u0433\u043e \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432\/\u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043d\u0430 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044e (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0441 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u043e\u0439)<\/p>\n<details class=\"spoiler\">\n<summary>ISR<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<\/div>\n<\/details>\n<h3>\u041f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u0442\u0432\u0438\u043a\u0438 \u0434\u043b\u044f next.js<\/h3>\n<pre><code class=\"javascript\">module.exports = {   poweredByHeader: false,  \/\/\u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a x-powered-by    compress: false,    \/\/\u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c gzip \u0441\u0436\u0430\u0442\u0438\u0435 (\u0442.\u043a \u0441\u0436\u0438\u043c\u0430\u0435\u043c \u043d\u0430 nginx \u0438\u043b\u0438 \u043d\u0430 cdn) }<\/code><\/pre>\n<p>\u041a\u043e\u043c\u043f\u0440\u0435\u0441\u0441\u0438\u044f \u0441\u0442\u0430\u0442\u0438\u043a\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Brotli<\/p>\n<pre><code class=\"javascript\">const zlib = require(\"zlib\") const CompressionPlugin = require(\"compression-webpack-plugin\") const use_brotli = false module.exports = {   webpack: (config, { isServer }) => {     if (use_brotli &amp;&amp; !isServer) {       config.plugins.push(         new CompressionPlugin({           filename: \"[path][base].br\",           algorithm: \"brotliCompress\",           test: \/\\.(js|css|html)$\/,           compressionOptions: {             params: {               [zlib.constants.BROTLI_PARAM_QUALITY]: 11,             },           }         })       )     }     return config   } }<\/code><\/pre>\n<p>\u0421\u0430\u043c\u044b\u0439 \u0442\u044f\u0436\u0435\u043b\u044b\u0439 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0444\u0430\u0439\u043b \u2013 framework.js <br \/>framework-0441fae7fd130f37dee1.js \u2013 131,009 bytes<br \/>framework-0441fae7fd130f37dee1.js.br \u2013 36,934 bytes<\/p>\n<p>\u0414\u043b\u044f \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f (gzip level 9)<br \/>framework-0441fae7fd130f37dee1.js.gz \u2013 42,388 bytes<\/p>\n<p>\u041c\u043e\u0449\u043d\u043e. \u0416\u0430\u043b\u044c, \u0447\u0442\u043e <a href=\"https:\/\/community.cloudflare.com\/t\/cloudflare-and-brotli-compression\/186710\/2\" rel=\"noopener noreferrer nofollow\">Cloudflare \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 brotli \u043d\u0430 \u0441\u0432\u043e\u0438\u0445 Edge \u0441\u0435\u0440\u0432\u0435\u0440\u0430\u0445<\/a>. \u0422\u0430\u043a \u0447\u0442\u043e 11 \u043b\u0435\u0432\u0435\u043b\u043e\u043c brotli \u043f\u043e\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f, \u043d\u043e Cloudflare \u0441\u0430\u043c \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442 \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u0443\u0440\u043e\u0432\u0435\u043d\u044c brotli, \u0442\u0430\u043a \u0447\u0442\u043e \u043f\u043e\u0442\u0435\u0440\u0438 \u043d\u0435 \u0442\u0430\u043a\u0438\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u0435. (\u0421loudflare \u0441\u0436\u0430\u043b \u0442\u043e\u0442 \u0436\u0435 \u0444\u0430\u0439\u043b \u0434\u043e 43.4 \u043a\u0431).<\/p>\n<h4>\u0418\u0437\u043e\u043c\u043e\u0440\u0444\u043d\u044b\u0435 \u043a\u043e\u043d\u0444\u0438\u0433 \u0444\u0430\u0439\u043b\u044b \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043e\u043f\u0430\u0441\u043d\u044b.<\/h4>\n<p>\u041e\u0448\u0438\u0431\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043b\u0435\u0433\u043a\u043e \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c. <\/p>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 config.js \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u043c\u0438 \u0441\u0435\u043a\u0440\u0435\u0442\u0430\u043c\u0438 \u0434\u043b\u044f backend \u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0430\u043c\u0438, \u043a\u0430\u043a \u0434\u043b\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0439, \u0442\u0430\u043a \u0438 \u0434\u043b\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438.<\/p>\n<p>\u0412 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u0430\u043a\u043e\u0435-\u0442\u043e \u0438\u0437 \u0435\u0433\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <code>recaptcha_site_key<\/code>. \u041f\u043e \u0434\u0435\u0444\u043e\u043b\u0442\u0443, webpack \u0438\u043d\u043b\u0430\u0439\u043d\u0438\u0442 config.js \u0446\u0435\u043b\u0438\u043a\u043e\u043c, \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0441\u0435\u043a\u0440\u0435\u0442\u044b \u0431\u0435\u043a\u0435\u043d\u0434\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442. <\/p>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 next.config.js \u043c\u043e\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u0430\u043a\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0438\u043d\u043b\u0430\u0439\u043d\u0438\u0442\u044c (\u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u0447\u0435\u0440\u0435\u0437 DefinePlugin)<\/p>\n<pre><code class=\"javascript\">module.exports = {   env: {  \/\/https:\/\/nextjs.org\/docs\/api-reference\/next.config.js\/environment-variables     recaptcha_site_key: \"6LcqV9QUAAAAAEybBVr0FWnUnFQmOVxGoQ_Muhtb\",     api_domain: \"http:\/\/localhost\/public_api\",     reverse_search_url: \"http:\/\/localhost\",     domain: \"http:\/\/localhost\",     ipns: \"ipns.scenery.cx\"   }, }<\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0432 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>process.env.\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435_\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439<\/code>. \u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0438\u043d\u043b\u0430\u0439\u043d\u0435\u043d\u043e.<\/p>\n<h4>Next.js SSR \u043d\u0435 \u043c\u0438\u043d\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u0442 css, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u043d\u043b\u0430\u0439\u043d\u0438\u0442\u0441\u044f \u0432 html \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 (\u0435\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 _document.js).<\/h4>\n<p>\u041d\u0430\u043f\u0438\u0448\u0435\u043c \u043c\u0438\u043d\u0438\u043a\u0435\u0448 \u2013 \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043c\u0438\u043d\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 css \u0432 Map. \u0415\u0441\u043b\u0438 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 css \u0435\u0441\u0442\u044c \u0432 Map, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0435\u0433\u043e \u043c\u0438\u043d\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e, \u0438\u043d\u0430\u0447\u0435 \u043c\u0438\u043d\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u043c \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0432 Map.<\/p>\n<pre><code class=\"javascript\">let css = sheets.toString()   if (css &amp;&amp; process.env.NODE_ENV === \"production\") {     const min_css = minified_css_cache.get(css)     if (min_css) {       css = min_css     } else {       const old_css = css       css = cleanCSS.minify(css).styles       minified_css_cache.set(old_css, css)     }   }<\/code><\/pre>\n<p>\u0423\u0431\u0438\u0440\u0430\u0435\u0442 \u043f\u0430\u0440\u0443 \u0434\u0435\u0441\u044f\u0442\u044b\u0445 \u043a\u0438\u043b\u043e\u0431\u0430\u0439\u0442\u0430, \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435 \u0432\u043b\u0438\u044f\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c (\u0441\u0442\u0430\u043b\u043e \u0434\u0430\u0436\u0435 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435). \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043c\u043d\u043e\u0433\u043e \u0440\u0430\u0437\u043d\u043e\u0433\u043e \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e css&#8217;a, \u0442\u043e \u043d\u0430\u0434\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043f\u043b\u044e\u0441\u044b \u043e\u0442 \u043c\u0438\u043d\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u0435\u0440\u0435\u043a\u0440\u043e\u044e\u0442\u0441\u044f \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u043c Map\/\u0435\u0433\u043e \u0431\u044b\u0441\u0442\u0440\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\u043c.<\/p>\n<h3>\u0412\u0435\u0431 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a<\/h3>\n<p>\u0412 \u043d\u0430\u0447\u0430\u043b\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u044f \u0432\u044b\u0431\u0440\u0430\u043b Express. \u041f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441, \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432. \u0415\u0441\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e 1 \u043c\u0438\u043d\u0443\u0441 \u2013 <a href=\"https:\/\/www.techempower.com\/benchmarks\/#section=data-r20&amp;hw=ph&amp;test=composite&amp;l=zik0sf-sf&amp;p=zik0zj-zijocf-zik0zj-zik0zj-1ekf&amp;c=c\" rel=\"noopener noreferrer nofollow\">\u043d\u0438\u0437\u043a\u0430\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c<\/a>. <\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0420\u0435\u0448\u0438\u043b \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u0442\u044c \u0432\u0441\u0435 \u043d\u0430 fastify, \u0431\u043b\u0430\u0433\u043e \u043a\u043e\u0434\u0430 \u043d\u0435 \u0442\u0430\u043a \u043c\u043d\u043e\u0433\u043e. \u041f\u043e\u043b\u0443\u0447\u0438\u043b \u043f\u0440\u0438\u0440\u043e\u0441\u0442 \u0441 50 req\/s \u0434\u043e 70 req\/s. \u042d\u0442\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043c\u043d\u043e\u0433\u043e, \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u044f \u0447\u0442\u043e \u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u043b \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Next.js, \u0442.\u0435 \u0432\u0441\u0435 \u0447\u0442\u043e \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043e\u0442 \u0432\u0435\u0431 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430, \u044d\u0442\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0443 Next.js, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0441\u0430\u043c\u043e\u0435 \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u2013 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e express \u0441\u044a\u0435\u0434\u0430\u043b \u043e\u043a\u043e\u043b\u043e 30 \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u043e\u0432 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438.<\/p>\n<p>\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043c\u043d\u0435 \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u0430\u0441\u044c \u0441\u0432\u044f\u0437\u043a\u0430 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 (\u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u0432 fastify \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/github.com\/ajv-validator\/ajv\" rel=\"noopener noreferrer nofollow\">ajv<\/a>) \u0438 \u043c\u043e\u0434\u0443\u043b\u044f <a href=\"https:\/\/github.com\/ThomasAribart\/json-schema-to-ts\" rel=\"noopener noreferrer nofollow\">json-schema-to-ts<\/a>. \u042d\u0442\u0430 \u0441\u0432\u044f\u0437\u043a\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u0438\u043f\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u043f\u0440\u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438, \u0432 typescript. \u0422.\u0435. \u0435\u0441\u043b\u0438 \u043c\u044b \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u0447\u0442\u043e body \u043e\u0431\u044f\u0437\u0430\u043d \u0438\u043c\u0435\u0442\u044c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e id \u0442\u0438\u043f\u0430 number, \u0442\u043e \u0432 ts \u0442\u0438\u043f req.body.id \u0442\u043e\u0436\u0435 \u0431\u0443\u0434\u0435\u0442 number.<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u0438\u043d\u0448\u043e\u0442 \u0438\u0437 VS Code<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<\/div>\n<\/details>\n<p>\u041d\u0438\u0436\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d \u0442\u0438\u043f\u043e\u0432\u043e\u0439 \u043a\u043e\u0434 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043f\u0443\u0442\u0438 (route). \u041d\u0430 \u043c\u043e\u0439 \u0432\u0437\u0433\u043b\u044f\u0434, \u044d\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u043d\u043e,  \u043a\u043e\u0433\u0434\u0430 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u0438 \u0441\u0430\u043c \u043a\u043e\u0434 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0432 \u043e\u0434\u043d\u043e\u043c \u0444\u0430\u0439\u043b\u0435.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u043a\u043e\u0434\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import db_ops from '.\/..\/helpers\/db_ops' import image_ops from '.\/..\/helpers\/image_ops' import { FastifyRequest, FastifyReply } from \"fastify\" import { FromSchema } from \"json-schema-to-ts\"; const body_schema_delete_image = {     type: 'object',     properties: {         id: { type: 'number' },     },     required: ['id'], } as const  async function delete_image(req: FastifyRequest&lt;{ Body: FromSchema&lt;typeof body_schema_delete_image> }>, res: FastifyReply) {     const id = req.body.id     if (req.session?.user_id) {         const user = await db_ops.activated_user.find_user_by_id(req.session.user_id)         if (user &amp;&amp; user.isAdmin) {             const result = await image_ops.delete_image(id)             if (result) {                 res.send({ message: result })             } else {                 res.send({ message: \"fail\" })             }         }     } else {         res.status(404).send()     } }  export default {     schema: {         body: body_schema_delete_image     },     handler: delete_image }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u043c\u0438\u043d\u0443\u0441 \u2013 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432. \u0425\u043e\u0440\u043e\u0448\u043e, \u0447\u0442\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u043b\u0430\u0433\u0438\u043d \u0434\u043b\u044f fastify \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e. \u042f \u043d\u0435 \u043d\u0430\u0448\u0435\u043b \u043f\u043b\u0430\u0433\u0438\u043d \u0434\u043b\u044f \u0432\u0435\u0440\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 Recaptcha, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u0441\u0432\u043e\u0439. \u0421\u0438\u0441\u0442\u0435\u043c\u0430 <a href=\"https:\/\/www.fastify.io\/docs\/latest\/Hooks\/\" rel=\"noopener noreferrer nofollow\">\u0445\u0443\u043a\u043e\u0432 <\/a>\u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u0438\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u0435\u0449\u0435 \u043d\u0430 \u043f\u043e\u0434\u0445\u043e\u0434\u0435 \u043a \u0445\u0435\u043d\u0434\u043b\u0435\u0440\u0443 \u0438 \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443, \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u044f \u0435\u0433\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043a\u043e\u0434. \u0412\u043e\u0442 <a href=\"https:\/\/www.fastify.io\/docs\/master\/Plugins\/#create-a-plugin\" rel=\"noopener noreferrer nofollow\">\u0433\u0430\u0439\u0434<\/a> \u043f\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432. \u042f \u043b\u0438\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u043b \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432 \u0438\u0437 <a href=\"https:\/\/www.fastify.io\/ecosystem\/\" rel=\"noopener noreferrer nofollow\">\u0441\u043f\u0438\u0441\u043a\u0430 \u0432\u0441\u0435\u0445 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432<\/a> \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u0438\u0445 \u043a\u043e\u0434 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 boilerplate.<\/p>\n<h3>\u0421\u0435\u0442\u043a\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439<\/h3>\n<p>\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u0440\u043e \u0442\u043e, \u043a\u0430\u043a \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f image grid. \u0421\u0440\u0435\u0434\u0441\u0442\u0432\u0430\u043c\u0438 css \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0441\u043b\u043e\u0436\u043d\u043e (\u0438\u043b\u0438 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e) \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0443\u044e, \u043e\u0442\u0437\u044b\u0432\u0447\u0438\u0432\u0443\u044e \u0441\u0435\u0442\u043a\u0443 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439, \u0433\u0434\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0441\u043e\u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u044f \u0441\u0442\u043e\u0440\u043e\u043d, \u0430 \u0441\u0442\u0440\u043e\u043a\u0438 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0443\u043f\u0430\u043a\u043e\u0432\u0430\u043d\u044b. <a href=\"https:\/\/www.w3schools.com\/howto\/tryit.asp?filename=tryhow_css_image_grid_responsive\" rel=\"noopener noreferrer nofollow\">\u0412\u043e\u0442 \u043f\u0440\u0438\u043c\u0435\u0440<\/a> css \u0441\u0435\u0442\u043a\u0438.<\/p>\n<p>\u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u2013 \u0431\u0443\u0434\u0435\u043c \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0435\u0442\u043a\u0443 \u0441\u0438\u043b\u0430\u043c\u0438 js. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <a href=\"https:\/\/github.com\/neptunian\/react-photo-gallery\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/neptunian\/react-photo-gallery<\/a>. <\/p>\n<p>\u0414\u0430\u043d\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0430\u0434\u0430\u043f\u0442\u0430\u0446\u0438\u044e \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0430 \u041a\u043d\u0443\u0442\u0430-\u041f\u043b\u0430\u0441\u0441\u0430 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0430 \u0441\u0442\u0440\u043e\u043a. (\u042d\u0442\u043e\u0442 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0442\u0430\u043a\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 TeX \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0430 \u0441\u0442\u0440\u043e\u043a)<\/p>\n<p>\u0410\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0433\u0440\u0430\u0444 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u043e\u0432, \u0433\u0434\u0435 \u0432\u0435\u0440\u0448\u0438\u043d\u0430 \u2013 \u044d\u0442\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u0451\u043b \u043f\u0435\u0440\u0435\u043d\u043e\u0441 \u0441\u0442\u0440\u043e\u043a\u0438, \u0430 \u0432\u0435\u0441 \u0440\u0435\u0431\u0440\u0430\u00a0\u044d\u0442\u043e <code>cost(targetRowHeight, \u0412\u044b\u0441\u043e\u0442\u0430 \u0440\u044f\u0434\u0430 \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0441\u0442\u0440\u043e\u043a\u0443<\/code>). \u041e\u043f\u0442\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u0440\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u2013 \u044d\u0442\u043e \u043a\u0440\u0430\u0442\u0447\u0430\u0439\u0448\u0438\u0439 \u043f\u0443\u0442\u044c \u0432 \u0442\u0430\u043a\u043e\u043c \u0433\u0440\u0430\u0444\u0435. \u0422.\u0435 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0440\u0430\u0432\u043d\u043e\u043c\u0435\u0440\u043d\u043e \u0443\u043f\u0430\u043a\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0431\u043b\u0438\u0437\u043a\u0438\u0435 \u043a \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u043e\u0439 \u0432\u044b\u0441\u043e\u0442\u0435.<\/p>\n<p><a href=\"https:\/\/medium.com\/google-design\/google-photos-45b714dfbed1\" rel=\"noopener noreferrer nofollow\">\u0411\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0437\u0434\u0435\u0441\u044c<\/a> (2\u043f\u0443\u043d\u043a\u0442, Justified Gallery)<br \/> <a href=\"http:\/\/blog.vjeux.com\/2014\/image\/google-plus-layout-find-best-breaks.html\" rel=\"noopener noreferrer nofollow\">http:\/\/blog.vjeux.com\/2014\/image\/google-plus-layout-find-best-breaks.html<\/a><\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h3>\u041e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439<\/h3>\n<p>Lossless \u0441\u0436\u0430\u0442\u0438\u0435, \u0442.\u0435 \u043f\u0438\u043a\u0441\u0435\u043b\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043d\u0435 \u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f. <\/p>\n<p>Jpeg \u0441\u0436\u0438\u043c\u0430\u0435\u043c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e jpegtran (\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u0438\u0437 libjpeg-turbo) \u00a0 <\/p>\n<p><code>apt-get install libjpeg-turbo-progs<\/code><\/p>\n<p>jpegtran \u0438\u0437 mozjpeg \u0436\u043c\u0435\u0442 \u043d\u0430 1,18% \u043b\u0443\u0447\u0448\u0435, \u043d\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u044d\u0442\u043e \u0432 2,3 \u0440\u0430\u0437\u0430 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0435\u0435. <a href=\"https:\/\/gist.github.com\/sergejmueller\/088dce028b6dd120a16e\" rel=\"noopener noreferrer nofollow\">https:\/\/gist.github.com\/sergejmueller\/088dce028b6dd120a16e<\/a><\/p>\n<p>\u0412\u043c\u0435\u0441\u0442\u043e optipng \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <a href=\"https:\/\/github.com\/shssoichiro\/oxipng\/\" rel=\"noopener noreferrer nofollow\">Oxipng <\/a>(\u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u043d\u0430 Rust optipng \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438) <\/p>\n<p>\u0422\u0435\u0441\u0442 jpegtran<\/p>\n<p>\u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 &#8212; 22,785 KB<br \/>\u041f\u043e\u0441\u043b\u0435 jpegtran \u2013 21,420 KB<\/p>\n<p>\u0422\u0435\u0441\u0442 oxipng\/optipng<\/p>\n<p>\u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 &#8212; 8,94 MB <\/p>\n<p>oxipng -o2 \u2013 5.97 MB, 5 \u0441\u0435\u043a\u0443\u043d\u0434<br \/>optipng -o2 \u2013 5.92 MB, 34 \u0441\u0435\u043a\u0443\u043d\u0434\u044b<\/p>\n<h3>\u041d\u0430\u0440\u0435\u0437\u043a\u0430 thumbnail&#8217;\u043e\u0432    <\/h3>\n<p>\u0414\u043b\u044f \u0440\u0435\u0441\u0430\u0439\u0437\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <a href=\"https:\/\/github.com\/lovell\/sharp\" rel=\"noopener noreferrer nofollow\">sharp<\/a>. Sharp \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 libvips, \u044d\u0442\u043e <a href=\"https:\/\/github.com\/libvips\/libvips\/wiki\/Speed-and-memory-use\" rel=\"noopener noreferrer nofollow\">\u043e\u0447\u0435\u043d\u044c \u0431\u044b\u0441\u0442\u0440\u0430\u044f\u00a0\u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u044e\u0449\u0430\u044f \u043c\u0430\u043b\u043e \u043f\u0430\u043c\u044f\u0442\u0438<\/a> \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c\u0438.<\/p>\n<pre><code class=\"javascript\">async function generate_thumbnail(image_src: Buffer | string) {  \/\/buffer or path to the image   const metadata = await sharp(image_src).metadata()   if (metadata &amp;&amp; metadata.height &amp;&amp; metadata.width) {     const x: { width?: number, height?: number <\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-413600","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/413600","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=413600"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/413600\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=413600"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=413600"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=413600"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}