20 Commits
Author SHA1 Message Date
Kq c4b0e597a9 new file: assets/picture21.jpg 2026-08-14 00:59:09 +02:00
Kq 765df60730 assety 2026-08-13 19:32:41 +02:00
Kq 7e9f11eaa2 make 2026-08-12 21:07:53 +02:00
Kq 3df7c68a3f update 2026-08-12 21:06:30 +02:00
Kq 2315c31d8c added AI changes 2026-08-12 20:58:37 +02:00
Kq 84c433ad19 modified: Makefile 2026-08-12 19:50:23 +02:00
Kq 71c48a5a6f modified: qrCamera/Program.cs 2026-08-12 19:44:59 +02:00
Kq 90120ee2cd modified: qrCamera/Program.cs 2026-08-12 19:41:53 +02:00
Kq 66ba176204 update 2026-08-12 19:31:18 +02:00
Kq a884b974cc update 2026-08-12 19:30:40 +02:00
Kq 4059a3b67b update 2026-08-12 19:11:44 +02:00
Kq e6a8c2f6e5 update 2026-08-12 18:12:30 +02:00
Kq edd9797246 modified: .gitignore
new file:   Makefile
2026-08-12 17:46:33 +02:00
Kq 234cd96109 update 2026-08-12 17:12:58 +02:00
Kq e323482b2e update 2026-08-12 17:12:22 +02:00
Kq 0a781d9cbe Merge branch 'main' of https://gitea.kqnori.pl/kq/qrcode 2026-08-12 03:58:44 +02:00
Kq 9fae9410fb both 2026-08-12 03:58:44 +02:00
kq 6e230b39ba Delete directory 'cache' 2026-08-12 03:58:17 +02:00
Kq 811773062e alot 2026-08-12 03:57:27 +02:00
Kq 084aefbbd7 changed screen
shots
2026-08-12 03:46:45 +02:00
39 changed files with 570 additions and 738 deletions
+12 -2
View File
@@ -1,2 +1,12 @@
/bin /qrCamera/bin
/obj /qrCamera/obj
/qrCamera/cache
/qrCameraAI/bin
/qrCameraAI/obj
/qrCameraAI/cache
/createqrcodes/bin
/createqrcodes/obj
/cache
/qr
/createqr
/qrcam
+22
View File
@@ -0,0 +1,22 @@
qr:
dotnet publish createqrcodes/createqrcodes.csproj -p:PublishSingleFile=true
mv createqrcodes/bin/Release/net10.0/linux-x64/publish/createqrcodes ./createqr
clean:
rm -rf qrCamera/bin/
rm -rf qrCamera/obj/
rm -rf qrCamera/cache/
rm -rf cache/
rm -rf createqrcodes/obj/
rm -rf createqrcodes/bin/
rm -f createqr
rm -f qrcam
rm -rf qr/
run:
dotnet run -p:qrCamera/CameraPreview.csproj
runai:
dotnet run -p:qrCameraAI/CameraPreview.csproj
publish:
dotnet publish qrCamera/CameraPreview.csproj --self-contained true
publishai:
dotnet publish qrCameraAI/CameraPreview.csproj --self-contained true
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Vendored
-667
View File
@@ -1,667 +0,0 @@
<!DOCTYPE html>
<html lang="en-US" data-theme="gitea-auto">
<head>
<meta http-equiv="Content-Security-Policy" content="default-src * data: blob:;script-src * 'nonce-d75a8ae0109b1c491a837d6d2055a0d5';style-src * 'unsafe-inline';">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>qrcode/assets/whoa.png at main - qrcode - Gitea: Git with a cup of tea</title>
<link rel="manifest" href="/assets/site-manifest.json">
<meta name="author" content="kq">
<meta name="description" content="qrcode">
<meta name="keywords" content="go,git,self-hosted,gitea">
<meta name="referrer" content="same-origin">
<link rel="alternate" type="application/atom+xml" title="" href="/kq/qrcode.atom">
<link rel="alternate" type="application/rss+xml" title="" href="/kq/qrcode.rss">
<link rel="icon" href="/assets/img/favicon.svg" type="image/svg+xml">
<link rel="alternate icon" href="/assets/img/favicon.png" type="image/png">
<meta property="og:title" content="qrcode/assets/whoa.png at main">
<meta property="og:url" content="https://gitea.kqnori.pl/kq/qrcode/src/branch/main/assets/whoa.png">
<meta property="og:type" content="object">
<meta property="og:image" content="https://gitea.kqnori.pl/avatars/e65bc325cd5363646800bf18bb64db903428dd0da649fb2559896f8d86b4ae36">
<meta property="og:site_name" content="Gitea: Git with a cup of tea">
<link rel="stylesheet" href="/assets/css/index.BmFw8RWO.css">
<link rel="stylesheet" href="/assets/css/theme-gitea-auto.v3RW7v-i.css">
<script nonce="d75a8ae0109b1c491a837d6d2055a0d5">
window.addEventListener('error', function(e) {window._globalHandlerErrors=window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);});
window.addEventListener('unhandledrejection', function(e) {window._globalHandlerErrors=window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);});
window.config = {
appUrl: 'https:\/\/gitea.kqnori.pl\/',
appSubUrl: '',
assetUrlPrefix: '\/assets',
runModeIsProd: true ,
customEmojis: {"codeberg":":codeberg:","git":":git:","gitea":":gitea:","github":":github:","gitlab":":gitlab:","gogs":":gogs:"},
pageData: {},
notificationSettings: {"EventSourceUpdateTime":10000,"MaxTimeout":60000,"MinTimeout":10000,"TimeoutStep":10000},
enableTimeTracking: true ,
mermaidMaxSourceCharacters: 50000 ,
sharedWorkerUri: '\/assets\/js\/eventsource.sharedworker.DV1Z2uJv.js',
i18n: {
error_occurred: "An error occurred",
remove_label_str: "Remove item \"%s\"",
modal_confirm: "Confirm",
modal_cancel: "Cancel",
more_items: "More items",
copy_success: "Copied!",
copy_error: "Copy failed",
},
};
window.config.pageData = window.config.pageData || {};
</script>
<script nonce="d75a8ae0109b1c491a837d6d2055a0d5" src="/assets/js/iife.D2XhYhS2.js"></script>
</head>
<body>
<div class="full height">
<noscript>This website requires JavaScript.</noscript>
<nav id="navbar" aria-label="Navigation Bar">
<div class="navbar-left">
<a class="item" id="navbar-logo" href="/" aria-label="Home">
<img width="30" height="30" src="/assets/img/logo.svg" alt="Logo" aria-hidden="true">
</a>
<div class="ui secondary menu navbar-mobile-right only-mobile">
<button class="item ui icon mini button tw-m-0" id="navbar-expand-toggle" aria-label="Navigation Menu"><svg viewBox="0 0 16 16" class="svg octicon-three-bars" aria-hidden="true" width="16" height="16"><path d="M1 2.75A.75.75 0 0 1 1.75 2h12.5a.75.75 0 0 1 0 1.5H1.75A.75.75 0 0 1 1 2.75m0 5A.75.75 0 0 1 1.75 7h12.5a.75.75 0 0 1 0 1.5H1.75A.75.75 0 0 1 1 7.75M1.75 12h12.5a.75.75 0 0 1 0 1.5H1.75a.75.75 0 0 1 0-1.5"/></svg></button>
</div>
<a class="item" href="/explore/repos">Explore</a>
<a class="item" target="_blank" href="https://docs.gitea.com">Help</a>
</div>
<div class="navbar-right">
<a class="item" href="/user/sign_up">
<svg viewBox="0 0 16 16" class="svg octicon-person" aria-hidden="true" width="16" height="16"><path d="M10.561 8.073a6 6 0 0 1 3.432 5.142.75.75 0 1 1-1.498.07 4.5 4.5 0 0 0-8.99 0 .75.75 0 0 1-1.498-.07 6 6 0 0 1 3.431-5.142 3.999 3.999 0 1 1 5.123 0M10.5 5a2.5 2.5 0 1 0-5 0 2.5 2.5 0 0 0 5 0"/></svg>
<span class="tw-ml-1">Register</span>
</a>
<a class="item" rel="nofollow" href="/user/login">
<svg viewBox="0 0 16 16" class="svg octicon-sign-in" aria-hidden="true" width="16" height="16"><path d="M2 2.75C2 1.784 2.784 1 3.75 1h2.5a.75.75 0 0 1 0 1.5h-2.5a.25.25 0 0 0-.25.25v10.5c0 .138.112.25.25.25h2.5a.75.75 0 0 1 0 1.5h-2.5A1.75 1.75 0 0 1 2 13.25Zm6.56 4.5h5.69a.75.75 0 0 1 0 1.5H8.56l1.97 1.97a.749.749 0 0 1-.326 1.275.75.75 0 0 1-.734-.215L6.22 8.53a.75.75 0 0 1 0-1.06l3.25-3.25a.749.749 0 0 1 1.275.326.75.75 0 0 1-.215.734Z"/></svg>
<span class="tw-ml-1">Sign In</span>
</a>
</div>
</nav>
<div role="main" aria-label="qrcode/assets/whoa.png at main" class="page-content repository file list ">
<div class="secondary-nav">
<div class="ui container">
<div class="repo-header flex-left-right">
<div class="flex-text-block">
<svg viewBox="0 0 16 16" class="svg octicon-repo" aria-hidden="true" width="24" height="24"><path d="M2 2.5A2.5 2.5 0 0 1 4.5 0h8.75a.75.75 0 0 1 .75.75v12.5a.75.75 0 0 1-.75.75h-2.5a.75.75 0 0 1 0-1.5h1.75v-2h-8a1 1 0 0 0-.714 1.7.75.75 0 1 1-1.072 1.05A2.5 2.5 0 0 1 2 11.5Zm10.5-1h-8a1 1 0 0 0-1 1v6.708A2.5 2.5 0 0 1 4.5 9h8ZM5 12.25a.25.25 0 0 1 .25-.25h3.5a.25.25 0 0 1 .25.25v3.25a.25.25 0 0 1-.4.2l-1.45-1.087a.25.25 0 0 0-.3 0L5.4 15.7a.25.25 0 0 1-.4-.2Z"/></svg>
<div class="flex-text-block tw-flex-wrap tw-text-18">
<a class="muted tw-font-normal" href="/kq">kq</a>/<a class="muted" href="/kq/qrcode">qrcode</a>
</div>
<div class="flex-text-block tw-flex-wrap">
</div>
</div>
<div class="flex-text-block tw-flex-wrap">
<a class="ui compact small basic button" href="/kq/qrcode.rss" data-tooltip-content="RSS Feed">
<svg viewBox="0 0 16 16" class="svg octicon-rss" aria-hidden="true" width="16" height="16"><path d="M2.002 2.725a.75.75 0 0 1 .797-.699C8.79 2.42 13.58 7.21 13.974 13.201a.75.75 0 0 1-1.497.098 10.5 10.5 0 0 0-9.776-9.776.747.747 0 0 1-.7-.798ZM2.84 7.05h-.002a7 7 0 0 1 6.113 6.111.75.75 0 0 1-1.49.178 5.5 5.5 0 0 0-4.8-4.8.75.75 0 0 1 .179-1.489M2 13a1 1 0 1 1 2 0 1 1 0 0 1-2 0"/></svg>
</a>
<div class="ui labeled button" data-tooltip-content="Sign in to watch this repository.">
<a role="button" class="ui compact small basic button" aria-label="Watch"
href="/user/login"
>
<svg viewBox="0 0 16 16" class="svg octicon-eye" aria-hidden="true" width="16" height="16"><path d="M8 2c1.981 0 3.671.992 4.933 2.078 1.27 1.091 2.187 2.345 2.637 3.023a1.62 1.62 0 0 1 0 1.798c-.45.678-1.367 1.932-2.637 3.023C11.67 13.008 9.981 14 8 14s-3.671-.992-4.933-2.078C1.797 10.83.88 9.576.43 8.898a1.62 1.62 0 0 1 0-1.798c.45-.677 1.367-1.931 2.637-3.022C4.33 2.992 6.019 2 8 2M1.679 7.932a.12.12 0 0 0 0 .136c.411.622 1.241 1.75 2.366 2.717C5.176 11.758 6.527 12.5 8 12.5s2.825-.742 3.955-1.715c1.124-.967 1.954-2.096 2.366-2.717a.12.12 0 0 0 0-.136c-.412-.621-1.242-1.75-2.366-2.717C10.824 4.242 9.473 3.5 8 3.5s-2.825.742-3.955 1.715c-1.124.967-1.954 2.096-2.366 2.717M8 10a2 2 0 1 1-.001-3.999A2 2 0 0 1 8 10"/></svg>
<span class="not-mobile" aria-hidden="true">Watch</span>
</a>
<a class="ui basic label" href="/kq/qrcode/watchers">
1
</a>
</div>
<div class="ui labeled button" data-tooltip-content="Sign in to star this repository.">
<a role="button" class="ui compact small basic button" aria-label="Star"
href="/user/login"
>
<svg viewBox="0 0 16 16" class="svg octicon-star" aria-hidden="true" width="16" height="16"><path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25m0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41z"/></svg>
<span class="not-mobile" aria-hidden="true">Star</span>
</a>
<a class="ui basic label" href="/kq/qrcode/stars">
0
</a>
</div>
<div class="ui labeled button"
data-tooltip-content="Sign in to fork this repository."
>
<a role="button" class="ui compact small basic button "
href="/user/login"
>
<svg viewBox="0 0 16 16" class="svg octicon-repo-forked" aria-hidden="true" width="16" height="16"><path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0M5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0m6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5m-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0"/></svg><span class="text not-mobile">Fork</span>
</a>
<a class="ui basic label" href="/kq/qrcode/forks">
0
</a>
</div>
</div>
</div>
</div>
<div class="ui container">
<overflow-menu class="ui secondary pointing menu">
<div class="overflow-menu-items">
<a class="active item" href="/kq/qrcode">
<svg viewBox="0 0 16 16" class="svg octicon-code" aria-hidden="true" width="16" height="16"><path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.75.75 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215m-6.56 0a.75.75 0 0 1 1.042.018.75.75 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.75.75 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"/></svg> Code
</a>
<a class="item" href="/kq/qrcode/issues">
<svg viewBox="0 0 16 16" class="svg octicon-issue-opened" aria-hidden="true" width="16" height="16"><path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3"/><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0M1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0"/></svg> Issues
</a>
<a class="item" href="/kq/qrcode/pulls">
<svg viewBox="0 0 16 16" class="svg octicon-git-pull-request" aria-hidden="true" width="16" height="16"><path d="M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25m5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354M3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5m0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5m8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0"/></svg> Pull Requests
</a>
<a class="item" href="/kq/qrcode/actions">
<svg viewBox="0 0 16 16" class="svg octicon-play" aria-hidden="true" width="16" height="16"><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0M1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0m4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215"/></svg> Actions
</a>
<a href="/kq/qrcode/packages" class="item">
<svg viewBox="0 0 16 16" class="svg octicon-package" aria-hidden="true" width="16" height="16"><path d="m8.878.392 5.25 3.045c.54.314.872.89.872 1.514v6.098a1.75 1.75 0 0 1-.872 1.514l-5.25 3.045a1.75 1.75 0 0 1-1.756 0l-5.25-3.045A1.75 1.75 0 0 1 1 11.049V4.951c0-.624.332-1.201.872-1.514L7.122.392a1.75 1.75 0 0 1 1.756 0M7.875 1.69l-4.63 2.685L8 7.133l4.755-2.758-4.63-2.685a.25.25 0 0 0-.25 0M2.5 5.677v5.372c0 .09.047.171.125.216l4.625 2.683V8.432Zm6.25 8.271 4.625-2.683a.25.25 0 0 0 .125-.216V5.677L8.75 8.432Z"/></svg> Packages
</a>
<a href="/kq/qrcode/projects" class="item">
<svg viewBox="0 0 16 16" class="svg octicon-project" aria-hidden="true" width="16" height="16"><path d="M1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25V1.75C0 .784.784 0 1.75 0M1.5 1.75v12.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25M11.75 3a.75.75 0 0 1 .75.75v7.5a.75.75 0 0 1-1.5 0v-7.5a.75.75 0 0 1 .75-.75m-8.25.75a.75.75 0 0 1 1.5 0v5.5a.75.75 0 0 1-1.5 0ZM8 3a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 3"/></svg> Projects
</a>
<a class="item" href="/kq/qrcode/releases">
<svg viewBox="0 0 16 16" class="svg octicon-tag" aria-hidden="true" width="16" height="16"><path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.75 1.75 0 0 1 1 7.775m1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/></svg> Releases
<span class="ui small label">1</span>
</a>
<a class="item" href="/kq/qrcode/wiki">
<svg viewBox="0 0 16 16" class="svg octicon-book" aria-hidden="true" width="16" height="16"><path d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.74 3.74 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574M8.755 4.75l-.004 7.322a3.75 3.75 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25"/></svg> Wiki
</a>
<a class="item" href="/kq/qrcode/activity">
<svg viewBox="0 0 16 16" class="svg octicon-pulse" aria-hidden="true" width="16" height="16"><path d="M6 2c.306 0 .582.187.696.471L10 10.731l1.304-3.26A.75.75 0 0 1 12 7h3.25a.75.75 0 0 1 0 1.5h-2.742l-1.812 4.528a.751.751 0 0 1-1.392 0L6 4.77 4.696 8.03A.75.75 0 0 1 4 8.5H.75a.75.75 0 0 1 0-1.5h2.742l1.812-4.529A.75.75 0 0 1 6 2"/></svg> Activity
</a>
</div>
</overflow-menu>
</div>
<div class="ui tabs divider"></div>
</div>
<div class="ui container fluid padded">
<div class="repo-view-container">
<div class="repo-view-file-tree-container ">
<div class="flex-text-block repo-button-row">
<button class="repo-view-file-tree-toggle ui button"
data-global-click="onRepoViewFileTreeToggle" data-toggle-action="hide"
data-tooltip-content="Hide file tree">
<svg viewBox="0 0 16 16" class="svg octicon-sidebar-expand" aria-hidden="true" width="16" height="16"><path d="m4.177 7.823 2.396-2.396A.25.25 0 0 1 7 5.604v4.792a.25.25 0 0 1-.427.177L4.177 8.177a.25.25 0 0 1 0-.354"/><path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25H9.5v-13Zm12.5 13a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25H11v13Z"/></svg>
</button>
<b>Files</b>
</div>
<div id="view-file-tree" class="tw-overflow-auto tw-h-full is-loading"
data-repo-link="/kq/qrcode"
data-tree-path="assets/whoa.png"
data-current-ref-name-sub-url="branch/main"
></div>
</div>
<div class="repo-view-content">
<div class="repo-view-content-data tw-hidden" data-document-title="qrcode/assets/whoa.png at main" data-document-title-common="qrcode - Gitea: Git with a cup of tea"></div>
<div class="repo-button-row">
<div class="repo-button-row-left">
<button type="button"
class="repo-view-file-tree-toggle ui button not-mobile tw-hidden"
data-global-click="onRepoViewFileTreeToggle" data-toggle-action="show" data-tooltip-content="Show file tree"
>
<svg viewBox="0 0 16 16" class="svg octicon-sidebar-collapse" aria-hidden="true" width="16" height="16"><path d="M6.823 7.823a.25.25 0 0 1 0 .354l-2.396 2.396A.25.25 0 0 1 4 10.396V5.604a.25.25 0 0 1 .427-.177Z"/><path d="M1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25V1.75C0 .784.784 0 1.75 0M1.5 1.75v12.5c0 .138.112.25.25.25H9.5v-13H1.75a.25.25 0 0 0-.25.25M11 14.5h3.25a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25H11Z"/></svg>
</button>
<div class=""
data-global-init="initRepoBranchTagSelector"
data-text-release-compare="Compare"
data-text-branches="Branches"
data-text-tags="Tags"
data-text-filter-branch="Filter branch"
data-text-filter-tag="Find tag"
data-text-default-branch-label="default"
data-text-create-tag="Create tag %s"
data-text-create-branch="Create branch %s"
data-text-create-ref-from="from &#34;%s&#34;"
data-text-no-results="No results found."
data-text-view-all-branches="View all branches"
data-text-view-all-tags="View all tags"
data-current-repo-default-branch="main"
data-current-repo-link="/kq/qrcode"
data-current-tree-path="assets/whoa.png"
data-current-ref-type="branch"
data-current-ref-short-name="main"
data-ref-link-template="{RepoLink}/src/{RefType}/{RefShortName}/{TreePath}"
data-ref-form-action-template=""
data-dropdown-fixed-text=""
data-show-tab-branches="true"
data-show-tab-tags="true"
data-allow-create-new-ref="false"
data-show-view-all-refs-entry="true"
data-enable-feed="true"
>
<div class="ui dropdown custom branch-selector-dropdown ellipsis-text-items">
<div class="ui compact button branch-dropdown-button">
<span class="flex-text-block gt-ellipsis">
<svg viewBox="0 0 16 16" class="svg octicon-git-branch" aria-hidden="true" width="16" height="16"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.5 2.5 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25m-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0m8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5M4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5"/></svg>
<strong class="tw-inline-block gt-ellipsis">main</strong>
</span>
<svg viewBox="0 0 16 16" class="dropdown icon svg octicon-triangle-down" aria-hidden="true" width="14" height="14"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427"/></svg>
</div>
</div>
</div>
<span class="breadcrumb">
<a class="section" href="/kq/qrcode/src/branch/main" title="qrcode">qrcode</a><span class="breadcrumb-divider">/</span><span class="section"><a href="/kq/qrcode/src/branch/main/assets" title="assets">assets</a></span><span class="breadcrumb-divider">/</span><span class="active section" title="whoa.png">whoa.png</span>
<button class="btn interact-fg tw-mx-1" data-clipboard-text="assets/whoa.png" data-tooltip-content="Copy path"><svg viewBox="0 0 16 16" class="svg octicon-copy" aria-hidden="true" width="14" height="14"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"/><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"/></svg></button></span>
</div>
<div class="repo-button-row-right">
<div class="repo-file-search-container"
data-global-init="initRepoFileSearch"
data-repo-link="/kq/qrcode"
data-current-ref-name-sub-url="branch/main"
data-tree-list-url="/kq/qrcode/tree-list/branch/main"
data-no-results-text="No matching file found"
data-placeholder="Go to file"
><div class="ui small input global-shortcut-wrapper"><input placeholder="Go to file"><kbd>T</kbd></div></div>
<button class="ui dropdown basic compact jump button repo-add-file" >
Add File
<svg viewBox="0 0 16 16" class="dropdown icon svg octicon-triangle-down" aria-hidden="true" width="14" height="14"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427"/></svg>
<div class="menu">
<a class="item" href="/kq/qrcode/_new/main/assets">
<svg viewBox="0 0 16 16" class="tw-mr-2 svg octicon-file-added" aria-hidden="true" width="16" height="16"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V4.664a.25.25 0 0 0-.073-.177l-2.914-2.914a.25.25 0 0 0-.177-.073Zm4.48 3.758a.75.75 0 0 1 .755.745l.01 1.497h1.497a.75.75 0 0 1 0 1.5H9v1.507a.75.75 0 0 1-1.5 0V9.005l-1.502.01a.75.75 0 0 1-.01-1.5l1.507-.01-.01-1.492a.75.75 0 0 1 .745-.755"/></svg>New File
</a>
<a class="item" href="/kq/qrcode/_upload/main/assets">
<svg viewBox="0 0 16 16" class="tw-mr-2 svg octicon-upload" aria-hidden="true" width="16" height="16"><path d="M2.75 14A1.75 1.75 0 0 1 1 12.25v-2.5a.75.75 0 0 1 1.5 0v2.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25v-2.5a.75.75 0 0 1 1.5 0v2.5A1.75 1.75 0 0 1 13.25 14Z"/><path d="M11.78 4.72a.749.749 0 1 1-1.06 1.06L8.75 3.811V9.5a.75.75 0 0 1-1.5 0V3.811L5.28 5.78a.749.749 0 1 1-1.06-1.06l3.25-3.25a.75.75 0 0 1 1.06 0z"/></svg>Upload File
</a>
<a class="item" href="/kq/qrcode/_diffpatch/main/assets">
<svg viewBox="0 0 16 16" class="tw-mr-2 svg octicon-diff" aria-hidden="true" width="16" height="16"><path d="M8.75 1.75V5H12a.75.75 0 0 1 0 1.5H8.75v3.25a.75.75 0 0 1-1.5 0V6.5H4A.75.75 0 0 1 4 5h3.25V1.75a.75.75 0 0 1 1.5 0M4 13h8a.75.75 0 0 1 0 1.5H4A.75.75 0 0 1 4 13"/></svg>Apply Patch
</a>
</div>
</button>
</div>
</div>
<div class="tab-size-4 non-diff-file-content"
data-global-init="initRepoFileView" data-raw-file-link="/kq/qrcode/raw/branch/main/assets/whoa.png">
<div id="repo-file-commit-box" class="ui segment list-header tw-mb-4 flex-left-right">
<div class="latest-commit">
<span class="avatar-stack-names"><span class="avatar-stack"><a href="/kq/qrcode/commits/branch/main/search?q=author%3Akq%40kqnori.pl"><img loading="lazy" alt class="ui avatar tw-align-middle" src="/avatars/e65bc325cd5363646800bf18bb64db903428dd0da649fb2559896f8d86b4ae36?size=40" title="kq" width="20" height="20"></a></span><a class="muted" href="/kq/qrcode/commits/branch/main/search?q=author%3Akq%40kqnori.pl">kq</a></span>
<a href="/kq/qrcode/commit/d421fcae3d551dbbc40cc593b10623abba15abe1" class="ui label commit-id-short " rel="nofollow">d421fcae3d</a>
<span class="grey commit-summary" title="Update assets/whoa.png">
<span class="message-wrapper"><span class="title-full-link-hover"><a href="/kq/qrcode/commit/d421fcae3d551dbbc40cc593b10623abba15abe1" class="muted title-full-link">Update assets/whoa.png</a></span></span>
</span>
</div>
<div class="tw-text-text-light age flex-text-block">
<relative-time prefix="" tense="past" datetime="2026-08-12T03:30:02+02:00" data-tooltip-content data-tooltip-interactive="true">2026-08-12 03:30:02 +02:00</relative-time>
</div>
</div>
<h4 class="file-header ui top attached header flex-left-right">
<div class="file-header-left tw-flex tw-items-center tw-py-2 tw-pr-4">
<div class="file-info tw-font-mono">
<div class="file-info-entry">
<span class="file-info-size">100 KiB</span>
</div>
<div class="file-info-entry">
184x184px
</div>
</div>
</div>
<div class="file-header-right file-actions flex-text-block tw-flex-wrap">
<div class="ui compact icon buttons file-view-toggle-buttons tw-hidden">
</div>
<div class="ui buttons tw-mr-1">
<a class="ui mini basic button" href="/kq/qrcode/raw/branch/main/assets/whoa.png">Raw</a>
<a class="ui mini basic button" href="/kq/qrcode/src/commit/44b817fb4be135a438bca471a9984e61990baff3/assets/whoa.png">Permalink</a>
<a class="ui mini basic button" href="/kq/qrcode/commits/branch/main/assets/whoa.png">History</a>
</div>
<a download class="btn-octicon" data-tooltip-content="Download file" href="/kq/qrcode/raw/branch/main/assets/whoa.png"><svg viewBox="0 0 16 16" class="svg octicon-download" aria-hidden="true" width="16" height="16"><path d="M2.75 14A1.75 1.75 0 0 1 1 12.25v-2.5a.75.75 0 0 1 1.5 0v2.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25v-2.5a.75.75 0 0 1 1.5 0v2.5A1.75 1.75 0 0 1 13.25 14Z"/><path d="M7.25 7.689V2a.75.75 0 0 1 1.5 0v5.689l1.97-1.969a.749.749 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 6.78a.749.749 0 1 1 1.06-1.06z"/></svg></a>
<a class="btn-octicon " data-global-click="onCopyContentButtonClick"
data-raw-file-link="/kq/qrcode/raw/branch/main/assets/whoa.png"
data-tooltip-content="Copy content"
><svg viewBox="0 0 16 16" class="svg octicon-copy" aria-hidden="true" width="16" height="16"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"/><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"/></svg></a>
<a class="btn-octicon" href="/kq/qrcode/rss/branch/main/assets/whoa.png" data-tooltip-content="RSS Feed">
<svg viewBox="0 0 16 16" class="svg octicon-rss" aria-hidden="true" width="16" height="16"><path d="M2.002 2.725a.75.75 0 0 1 .797-.699C8.79 2.42 13.58 7.21 13.974 13.201a.75.75 0 0 1-1.497.098 10.5 10.5 0 0 0-9.776-9.776.747.747 0 0 1-.7-.798ZM2.84 7.05h-.002a7 7 0 0 1 6.113 6.111.75.75 0 0 1-1.49.178 5.5 5.5 0 0 0-4.8-4.8.75.75 0 0 1 .179-1.489M2 13a1 1 0 1 1 2 0 1 1 0 0 1-2 0"/></svg>
</a>
<a class="btn-octicon" data-tooltip-content="You must fork this repository to make or propose changes to this file." href="/kq/qrcode/_edit/main/assets/whoa.png"><svg viewBox="0 0 16 16" class="svg octicon-pencil" aria-hidden="true" width="16" height="16"><path d="M11.013 1.427a1.75 1.75 0 0 1 2.474 0l1.086 1.086a1.75 1.75 0 0 1 0 2.474l-8.61 8.61c-.21.21-.47.364-.756.445l-3.251.93a.75.75 0 0 1-.927-.928l.929-3.25c.081-.286.235-.547.445-.758l8.61-8.61Zm.176 4.823L9.75 4.81l-6.286 6.287a.25.25 0 0 0-.064.108l-.558 1.953 1.953-.558a.25.25 0 0 0 .108-.064Zm1.238-3.763a.25.25 0 0 0-.354 0L10.811 3.75l1.439 1.44 1.263-1.263a.25.25 0 0 0 0-.354Z"/></svg></a>
<a class="btn-octicon btn-octicon-danger" data-tooltip-content="You must have write access to make or propose changes to this file." href="/kq/qrcode/_delete/main/assets/whoa.png"><svg viewBox="0 0 16 16" class="svg octicon-trash" aria-hidden="true" width="16" height="16"><path d="M11 1.75V3h2.25a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1 0-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75M4.496 6.675l.66 6.6a.25.25 0 0 0 .249.225h5.19a.25.25 0 0 0 .249-.225l.66-6.6a.75.75 0 0 1 1.492.149l-.66 6.6A1.75 1.75 0 0 1 10.595 15h-5.19a1.75 1.75 0 0 1-1.741-1.575l-.66-6.6a.75.75 0 1 1 1.492-.15M6.5 1.75V3h3V1.75a.25.25 0 0 0-.25-.25h-2.5a.25.25 0 0 0-.25.25"/></svg></a>
</div>
</h4>
<div class="ui bottom attached segment file-view-container">
<div class="file-view ">
<div class="view-raw">
<img alt="/kq/qrcode/raw/branch/main/assets/whoa.png" src="/kq/qrcode/raw/branch/main/assets/whoa.png">
</div>
</div>
<div class="code-line-menu tippy-target">
<a class="item ref-in-new-issue" role="menuitem" data-url-issue-new="/kq/qrcode/issues/new" data-url-param-body-link="/kq/qrcode/src/commit/44b817fb4be135a438bca471a9984e61990baff3/assets/whoa.png" rel="nofollow noindex">Reference in New Issue</a>
<a class="item view_git_blame" role="menuitem" href="/kq/qrcode/blame/commit/44b817fb4be135a438bca471a9984e61990baff3/assets/whoa.png">View Git Blame</a>
<a class="item copy-line-permalink" role="menuitem" data-url="/kq/qrcode/src/commit/44b817fb4be135a438bca471a9984e61990baff3/assets/whoa.png">Copy Permalink</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<footer class="page-footer" role="group" aria-label="Footer">
<div class="left-links" role="contentinfo" aria-label="About Software">
<a target="_blank" href="https://about.gitea.com">Powered by Gitea</a>
<span>
Version:
1.27.1
</span>
<span>
Page: <strong>52ms</strong>
Template: <strong>6ms</strong>
</span>
</div>
<div class="right-links" role="group" aria-label="Links">
<div class="ui dropdown custom" id="footer-theme-selector">
<span class="default-text"><div class="theme-menu-item" data-tooltip-content=""><svg viewBox="490 490 820 820" class="svg gitea-eclipse" aria-hidden="true" width="16" height="16"><path d="M866.7 582.1A321.3 321.3 0 0 0 738.6 623a317.3 317.3 0 0 0-109.1 108.5A418 418 0 0 0 609 772a335.3 335.3 0 0 0-19.6 71.5 205.2 205.2 0 0 0-2.8 45c0 25.8.2 29.3 2.8 45 4.1 25.4 9.9 46.4 19.6 71.5a314.2 314.2 0 0 0 111.6 137.3A306.8 306.8 0 0 0 893 1196a308.6 308.6 0 0 0 303.6-262.5c2.6-15.7 2.8-19.2 2.8-45s-.2-29.3-2.8-45A335.3 335.3 0 0 0 1177 772a314.2 314.2 0 0 0-111.6-137.3A308.3 308.3 0 0 0 918 582c-13-1.1-38.2-1.1-51.3.1M747 663.5l-2.4 16.7c-4 26.4-4.9 41.1-4.3 65.3a323.7 323.7 0 0 0 37.2 145c18.2 36 41.3 66.6 72 95.5a346.4 346.4 0 0 0 208.5 93.1l18 1.6 4.5.5-8.5 8a259.3 259.3 0 0 1-141.5 65.8 281 281 0 0 1-123.9-11.4 267.2 267.2 0 0 1-181.7-269.9c2-27.6 5.7-47.6 13.3-70.7a281.2 281.2 0 0 1 46.4-85c8-10.1 28-30.2 37.9-38.1 13.8-11.1 24.5-18.3 24.5-16.4"/></svg> Auto </div></span>
<div class="menu theme-menu"></div>
</div>
<div class="ui dropdown upward">
<span class="flex-text-inline"><svg viewBox="0 0 16 16" class="svg octicon-globe" aria-hidden="true" width="14" height="14"><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0M5.78 8.75a9.64 9.64 0 0 0 1.363 4.177q.383.64.857 1.215c.245-.296.551-.705.857-1.215A9.64 9.64 0 0 0 10.22 8.75Zm4.44-1.5a9.64 9.64 0 0 0-1.363-4.177c-.307-.51-.612-.919-.857-1.215a10 10 0 0 0-.857 1.215A9.64 9.64 0 0 0 5.78 7.25Zm-5.944 1.5H1.543a6.51 6.51 0 0 0 4.666 5.5q-.184-.271-.352-.552c-.715-1.192-1.437-2.874-1.581-4.948m-2.733-1.5h2.733c.144-2.074.866-3.756 1.58-4.948q.18-.295.353-.552a6.51 6.51 0 0 0-4.666 5.5m10.181 1.5c-.144 2.074-.866 3.756-1.58 4.948q-.18.296-.353.552a6.51 6.51 0 0 0 4.666-5.5Zm2.733-1.5a6.51 6.51 0 0 0-4.666-5.5q.184.272.353.552c.714 1.192 1.436 2.874 1.58 4.948Z"/></svg> English</span>
<div class="menu language-menu">
<a lang="id-ID" data-url="/?lang=id-ID" class="item ">Bahasa Indonesia</a>
<a lang="de-DE" data-url="/?lang=de-DE" class="item ">Deutsch</a>
<a lang="en-US" data-url="/?lang=en-US" class="item selected">English</a>
<a lang="es-ES" data-url="/?lang=es-ES" class="item ">Español</a>
<a lang="fr-FR" data-url="/?lang=fr-FR" class="item ">Français</a>
<a lang="ga-IE" data-url="/?lang=ga-IE" class="item ">Gaeilge</a>
<a lang="it-IT" data-url="/?lang=it-IT" class="item ">Italiano</a>
<a lang="lv-LV" data-url="/?lang=lv-LV" class="item ">Latviešu</a>
<a lang="hu-HU" data-url="/?lang=hu-HU" class="item ">Magyar nyelv</a>
<a lang="nl-NL" data-url="/?lang=nl-NL" class="item ">Nederlands</a>
<a lang="pl-PL" data-url="/?lang=pl-PL" class="item ">Polski</a>
<a lang="pt-PT" data-url="/?lang=pt-PT" class="item ">Português de Portugal</a>
<a lang="pt-BR" data-url="/?lang=pt-BR" class="item ">Português do Brasil</a>
<a lang="fi-FI" data-url="/?lang=fi-FI" class="item ">Suomi</a>
<a lang="sv-SE" data-url="/?lang=sv-SE" class="item ">Svenska</a>
<a lang="tr-TR" data-url="/?lang=tr-TR" class="item ">Türkçe</a>
<a lang="cs-CZ" data-url="/?lang=cs-CZ" class="item ">Čeština</a>
<a lang="el-GR" data-url="/?lang=el-GR" class="item ">Ελληνικά</a>
<a lang="bg-BG" data-url="/?lang=bg-BG" class="item ">Български</a>
<a lang="ru-RU" data-url="/?lang=ru-RU" class="item ">Русский</a>
<a lang="uk-UA" data-url="/?lang=uk-UA" class="item ">Українська</a>
<a lang="fa-IR" data-url="/?lang=fa-IR" class="item ">فارسی</a>
<a lang="ml-IN" data-url="/?lang=ml-IN" class="item ">മലയാളം</a>
<a lang="ja-JP" data-url="/?lang=ja-JP" class="item ">日本語</a>
<a lang="zh-CN" data-url="/?lang=zh-CN" class="item ">简体中文</a>
<a lang="zh-TW" data-url="/?lang=zh-TW" class="item ">繁體中文(台灣)</a>
<a lang="zh-HK" data-url="/?lang=zh-HK" class="item ">繁體中文(香港)</a>
<a lang="ko-KR" data-url="/?lang=ko-KR" class="item ">한국어</a>
</div>
</div>
<a href="/assets/licenses.txt">Licenses</a>
<a href="/api/swagger">API</a>
</div>
</footer>
<script nonce="d75a8ae0109b1c491a837d6d2055a0d5" type="module" src="/assets/js/index.ChkmaRqp.js"></script>
<script nonce="d75a8ae0109b1c491a837d6d2055a0d5" type="module">
if (!window.config?.frontendInited && window.config?.runModeIsProd) alert("Frontend is not initialized, check console errors or asset files.");
</script>
</body>
</html>
+17
View File
@@ -0,0 +1,17 @@
using System.Diagnostics;
string[] files;
files = Directory.GetFiles("assets");
if(Directory.Exists("qr"))
Directory.Delete("qr", true);
Directory.CreateDirectory("qr");
foreach (var item in files)
{
string name = Path.GetFileName(item);
//https://gitea.kqnori.pl/kq/qrcode/raw/branch/main/assets/gay.png
Console.WriteLine(name);
Process.Start("qrencode",$"https://gitea.kqnori.pl/kq/qrcode/raw/branch/main/assets/{name} -o qr/item{name.Split('.').First()}.png");
}
+10
View File
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
View File

Before

Width:  |  Height:  |  Size: 850 KiB

After

Width:  |  Height:  |  Size: 850 KiB

View File

Before

Width:  |  Height:  |  Size: 980 KiB

After

Width:  |  Height:  |  Size: 980 KiB

View File

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

+104 -68
View File
@@ -7,6 +7,9 @@ using ZXing.Common;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Drawing; using System.Drawing;
using System.Net; using System.Net;
using System.Runtime.InteropServices.Marshalling;
using Emgu.CV.Face;
using System.Transactions;
// using Emgu.CV.CvEnum; // using Emgu.CV.CvEnum;
@@ -15,6 +18,7 @@ namespace BarcodeReaderExample
{ {
class Program class Program
{ {
static bool draw;
static void Main(string[] args) static void Main(string[] args)
{ {
VideoCapture camera = new VideoCapture("/dev/video0", VideoCaptureAPIs.V4L2); VideoCapture camera = new VideoCapture("/dev/video0", VideoCaptureAPIs.V4L2);
@@ -24,16 +28,21 @@ namespace BarcodeReaderExample
camera.Set(VideoCaptureProperties.FrameWidth, 1280); camera.Set(VideoCaptureProperties.FrameWidth, 1280);
camera.Set(VideoCaptureProperties.FrameHeight, 720); camera.Set(VideoCaptureProperties.FrameHeight, 720);
camera.Set(VideoCaptureProperties.Fps, 24); int fps = 60;
if(Directory.Exists("cache")) int avalibeImages = 20;
camera.Set(VideoCaptureProperties.Fps, fps);
if (Directory.Exists("cache"))
{ {
Directory.Delete("cache",true); Directory.Delete("cache", true);
} }
Directory.CreateDirectory("cache"); Directory.CreateDirectory("cache");
Mat hey = new Mat("photo.png"); Mat hey = new Mat("photo.png");
Mat[] Cached = new Mat[10]; Mat[] Cached = new Mat[avalibeImages];
draw = true;
int imgX = 0;
int imgY = 0;
using var rgb = new Mat(); using var rgb = new Mat();
@@ -44,35 +53,40 @@ namespace BarcodeReaderExample
Cv2.NamedWindow("Camera preview", WindowFlags.AutoSize); Cv2.NamedWindow("Camera preview", WindowFlags.AutoSize);
int photocount = 0; // Mat src = Cv2.ImRead("test.png");
Mat src = Cv2.ImRead("test.png"); int whenWasLastTimeQrDetected = 0;
Mat Lastimage = new Mat();
IEnumerable<IEnumerable<OpenCvSharp.Point>> contours = null;
// PointF[] location;
string[] files = new string[10]; Mat previue = new Mat();
int files_index =0; string[] files = new string[avalibeImages];
int files_index = 0;
while (true) while (true)
{ {
// if (!camera.Read(frame) || frame.Empty())
// {
// Console.Error.WriteLine("Nie udało się odczytać klatki");
// break;
// }
camera.Read(frame); camera.Read(frame);
Result result = ReadQrCode(frame);
// Console.WriteLine(Lastimage.Empty());
if (!(Lastimage.Empty()))
{
Cv2.Resize(Lastimage, previue, new OpenCvSharp.Size(500, 500));
Mat roi2 = frame[new Rect(0, 0, previue.Width, previue.Height)];
previue.CopyTo(roi2);
}
Result result = GetResult(frame);
if (result != null) if (result != null)
{ {
whenWasLastTimeQrDetected = 5;
List<ResultPoint> points = result.ResultPoints.ToList(); List<ResultPoint> points = result.ResultPoints.ToList();
float[] pos1 = new float[2] { points[0].X, points[0].Y }; /// lewy dolny róg float[] pos1 = new float[2] { points[0].X, points[0].Y }; /// lewy dolny róg
float[] pos2 = new float[2] { points[2].X, points[2].Y }; ///prawy górny róg float[] pos2 = new float[2] { points[2].X, points[2].Y }; ///prawy górny róg
float[] pos3 = new float[2] { points[1].X, points[1].Y }; ///lewy górny róg float[] pos3 = new float[2] { points[1].X, points[1].Y }; ///lewy górny róg
int width = (int)(pos2[0] - pos3[0]); int width = (int)(pos2[0] - pos3[0]);
int heigh = (int)(pos1[1] - pos3[1]); int heigh = (int)(pos1[1] - pos3[1]);
// Cv2.Circle(frame, (int)pos1[0], (int)pos1[1], 5, Scalar.Red, 2);
// Cv2.Circle(frame, (int)pos2[0], (int)pos2[1], 5, Scalar.Green, 2);
// Cv2.Circle(frame, (int)pos3[0], (int)pos3[1], 5, Scalar.Blue, 2);
PointF[] location; PointF[] location;
try try
{ {
@@ -83,19 +97,6 @@ namespace BarcodeReaderExample
{ {
continue; continue;
} }
// Mat roi = frame[new Rect((int)pos3[0],(int)pos3[1], src.Width+3, src.Height)];
Mat roi = frame[new Rect((int)pos3[0], (int)pos3[1], width,heigh)];
src.CopyTo(roi);
// Console.WriteLine($"szerokość:{width}");
// Console.WriteLine($"wysokosc:{heigh}");
// Console.WriteLine($"x:{pos3[0]}");
// Console.WriteLine($"y:{pos3[1]}");
// DrawLine(location); //draw red squre
var points1 = new List<OpenCvSharp.Point> var points1 = new List<OpenCvSharp.Point>
{ {
GetPoint(location[0]), GetPoint(location[0]),
@@ -103,58 +104,89 @@ namespace BarcodeReaderExample
GetPoint(location[3]), GetPoint(location[3]),
GetPoint(location[2]) GetPoint(location[2])
}; };
int qrWidth = GetPoint(location[1]).X - GetPoint(location[0]).X;
// Zagnieźdź w dodatkową kolekcję: int qrHeigh = GetPoint(location[2]).Y - GetPoint(location[0]).Y;
IEnumerable<IEnumerable<OpenCvSharp.Point>> contours = new[] { points1 };
// Cv2.FillPoly(frame, contours, Scalar.White); // fill with white imgX = GetPoint(location[0]).X;
imgY = GetPoint(location[0]).Y;
contours = new[] { points1 };
Cv2.PutText(frame, result.Text, new OpenCvSharp.Point((int)(pos2[0]-pos3[0])/2+(int)pos3[0]-160, (int)(pos2[1]-pos3[1])/2+(int)pos3[1]+60), HersheyFonts.HersheySimplex, 0.8, Scalar.Black, 2); if (draw)
Cv2.FillPoly(frame, contours, Scalar.White); // fill with white
//Write Text
// Cv2.PutText(frame, result.Text, new OpenCvSharp.Point((int)(pos2[0]-pos3[0])/2+(int)pos3[0]-160, (int)(pos2[1]-pos3[1])/2+(int)pos3[1]+60), HersheyFonts.HersheySimplex, 0.8, Scalar.Black, 2);
if (!files.Contains(result.Text)) if (!files.Contains(result.Text))
{ {
files[files_index] = result.Text; files[files_index] = result.Text;
Console.WriteLine("downloading: "+result.Text); Console.WriteLine("downloading: " + result.Text);
using (var client = new WebClient()) using (var client = new WebClient())
{ {
client.DownloadFile(result.Text, "cache/"+files_index + ".png"); client.DownloadFile(result.Text, "cache/" + files_index + ".png");
// Thread.Sleep(2000); // Thread.Sleep(2000);
} }
Cached[files_index] = new Mat("cache/"+files_index+ ".png"); Cached[files_index] = new Mat("cache/" + files_index + ".png");
files_index++;
} }
else else
{ {
int toload = files.IndexOf(result.Text);
Mat toDraw= new Mat();
InputArray inputArray = toDraw;
// Console.WriteLine($"{qrWidth} , {qrHeigh}");
try try
{ {
int toload = files.IndexOf(result.Text); Cv2.Resize(Cached[toload],toDraw,new OpenCvSharp.Size(qrWidth,qrHeigh));
Mat roi2 = frame[new Rect((int)pos3[0], (int)pos3[1], Cached[toload].Width, Cached[toload].Height)]; }
Cached[toload].CopyTo(roi2); catch
{ continue; }
Lastimage = toDraw;
// toDraw.Resize(1,3);
try
{
// Mat roi2 = frame[new Rect(GetPoint(location[0]).X, GetPoint(location[0]).Y, toDraw.Width, toDraw.Height)];
// toDraw.CopyTo(roi2);
DrawImage(toDraw, GetPoint(location[0]).X, GetPoint(location[0]).Y);
} }
catch { } catch { }
} }
// Cv2.draw
// Cv2.Circle(frame, (int)(RightBottomX), (int)(RightBottomY), 5, Scalar.Green, 2);
//Cv2.Rectangle(frame, new Point((int)X, (int)Y), new Point ((int)Width, (int)Height), Scalar.Red, 2);
// Console.WriteLine(result.Text);
} }
else if (whenWasLastTimeQrDetected > 0)
{
if (draw)
Cv2.FillPoly(frame, contours, Scalar.White); // fill with white
try
{
// Mat roi2 = frame[new Rect(imgX, imgY, Lastimage.Width, Lastimage.Height)];
// Lastimage.CopyTo(roi2);
DrawImage(Lastimage,imgX,imgY);
}
catch { }
whenWasLastTimeQrDetected--;
}
// frame.SaveImage($"photos/photo_{photocount}.png");
// photocount++;
Cv2.ImShow("Camera preview", frame); Cv2.ImShow("Camera preview", frame);
Thread.Sleep(1000 / 24); Thread.Sleep(1000 / fps);
// Console.WriteLine("tick");
int key = Cv2.WaitKey(1); int key = Cv2.WaitKey(1);
if (key == 27 || key == 'q') if (key == 27 || key == 'q')
break; break;
@@ -163,9 +195,19 @@ namespace BarcodeReaderExample
Cv2.DestroyAllWindows(); Cv2.DestroyAllWindows();
} }
static void DrawImage(Mat image, int posX, int posY)
{
if(draw)
{
Mat roi2 = frame[new Rect(posX, posY, image.Width, image.Height)];
image.CopyTo(roi2);
}
}
static OpenCvSharp.Point GetPoint(PointF pointf) static OpenCvSharp.Point GetPoint(PointF pointf)
{ {
return new OpenCvSharp.Point(pointf.X,pointf.Y); return new OpenCvSharp.Point(pointf.X, pointf.Y);
} }
static PointF[] Calculate(float[] pos1, float[] pos2, float[] pos3, float[] pos4) static PointF[] Calculate(float[] pos1, float[] pos2, float[] pos3, float[] pos4)
{ {
@@ -203,7 +245,7 @@ namespace BarcodeReaderExample
PointF topRight = Expand(p2); PointF topRight = Expand(p2);
PointF bottomRight = ExpandLB(p4); PointF bottomRight = ExpandLB(p4);
PointF bottomLeft = Expand(p1); PointF bottomLeft = Expand(p1);
PointF[] Location = new PointF[] {topLeft,topRight,bottomLeft,bottomRight}; PointF[] Location = new PointF[] { topLeft, topRight, bottomLeft, bottomRight };
return Location; return Location;
// 4. Now draw the 4 lines // 4. Now draw the 4 lines
// Example with System.Drawing or Unity or whatever you use: // Example with System.Drawing or Unity or whatever you use:
@@ -211,7 +253,11 @@ namespace BarcodeReaderExample
} }
static void DrawLine(PointF p1, PointF p2) static void DrawLine(PointF p1, PointF p2)
{ {
if(draw)
{
Cv2.Line(frame, (int)p1.X, (int)p1.Y, (int)p2.X, (int)p2.Y, Scalar.Red, 2); Cv2.Line(frame, (int)p1.X, (int)p1.Y, (int)p2.X, (int)p2.Y, Scalar.Red, 2);
}
} }
static void DrawLine(PointF[] Location) static void DrawLine(PointF[] Location)
@@ -230,7 +276,7 @@ namespace BarcodeReaderExample
} }
static Mat frame = new Mat(); static Mat frame = new Mat();
static Result GetResult(Mat frame) static Result ReadQrCode(Mat frame)
{ {
QRCodeReader reader = new QRCodeReader(); QRCodeReader reader = new QRCodeReader();
@@ -284,13 +330,3 @@ namespace BarcodeReaderExample
} }
} }
+20
View File
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Emgu.CV" Version="4.13.0.5924" />
<PackageReference Include="Emgu.CV.Bitmap" Version="4.13.0.5924" />
<PackageReference Include="OpenCvSharp4" Version="4.13.0.20260627" />
<PackageReference Include="OpenCvSharp4.Extensions" Version="4.13.0.20260627" />
<PackageReference Include="OpenCvSharp4.official.runtime.linux-x64" Version="4.13.0.20260627" />
<PackageReference Include="ZXing.Net" Version="0.16.11" />
<PackageReference Include="ZXing.Net.Bindings.Windows.Compatibility" Version="0.16.14" />
</ItemGroup>
</Project>
+384
View File
@@ -0,0 +1,384 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using OpenCvSharp;
using ZXing;
using ZXing.Common;
using ZXing.Multi;
using ZXing.QrCode;
namespace BarcodeReaderExample
{
class Program
{
// ---- tunables -------------------------------------------------
const bool EnableOverlay = true; // master draw switch (debug)
const bool TryHarderMultiDecode = true; // more reliable multi-QR detection, costs some speed
const float QuadExpansion = 1.50f; // how much bigger than the QR the overlay is drawn
const int MaxMissedFrames = 8; // keep last known position this many frames if briefly lost
const int TargetFps = 24;
const float LocalRedetectPadding = 0.6f; // extra search margin around last known box, as a fraction of its size
const int LocalRedetectUpscale = 3; // how much to enlarge the crop before decoding it
static readonly HttpClient Http = new HttpClient();
static readonly ConcurrentDictionary<string, Mat> ImageCache = new(); // text -> decoded image (null = failed)
static readonly ConcurrentDictionary<string, byte> Pending = new(); // text currently downloading
static readonly QRCodeReader QrReader = new QRCodeReader();
static readonly GenericMultipleBarcodeReader MultiReader = new GenericMultipleBarcodeReader(QrReader);
static readonly Dictionary<DecodeHintType, object> Hints = new()
{
{ DecodeHintType.POSSIBLE_FORMATS, new List<BarcodeFormat> { BarcodeFormat.QR_CODE } },
{ DecodeHintType.TRY_HARDER, TryHarderMultiDecode },
};
// Used only for the small, targeted local-redetect crop, so the extra cost is negligible there.
static readonly Dictionary<DecodeHintType, object> LocalHints = new()
{
{ DecodeHintType.POSSIBLE_FORMATS, new List<BarcodeFormat> { BarcodeFormat.QR_CODE } },
{ DecodeHintType.TRY_HARDER, true },
{ DecodeHintType.ALSO_INVERTED, true },
};
static readonly CLAHE Clahe = Cv2.CreateCLAHE(clipLimit: 3.0, tileGridSize: new Size(8, 8));
static byte[] _grayBuffer;
static readonly Dictionary<string, TrackedQr> Tracked = new();
class TrackedQr
{
public Point2f[] RawQuad; // TL, TR, BR, BL as detected, unexpanded - used to seed the next search
public Point2f[] Quad; // TL, TR, BR, BL, expanded - used for drawing
public int MissedFrames;
}
static void Main(string[] args)
{
using VideoCapture camera = new VideoCapture("/dev/video0", VideoCaptureAPIs.V4L2);
if (!camera.IsOpened())
throw new Exception("Nie można otworzyć kamery /dev/video0");
camera.Set(VideoCaptureProperties.FrameWidth, 1280);
camera.Set(VideoCaptureProperties.FrameHeight, 720);
camera.Set(VideoCaptureProperties.Fps, TargetFps);
Cv2.NamedWindow("Camera preview", WindowFlags.AutoSize);
using var frame = new Mat();
using var gray = new Mat();
var sw = Stopwatch.StartNew();
var frameTimer = Stopwatch.StartNew();
int frameCount = 0;
double fps = 0;
try
{
while (true)
{
frameTimer.Restart();
if (!camera.Read(frame) || frame.Empty())
continue;
try
{
ProcessFrame(frame, gray);
}
catch (Exception ex)
{
// Never let a single bad frame kill the capture loop.
Console.WriteLine("Frame processing error: " + ex.Message);
}
frameCount++;
if (sw.ElapsedMilliseconds >= 500)
{
fps = frameCount / (sw.ElapsedMilliseconds / 1000.0);
frameCount = 0;
sw.Restart();
}
Cv2.PutText(frame, $"{fps:0.0} fps", new Point(10, frame.Height - 15),
HersheyFonts.HersheySimplex, 0.6, Scalar.Lime, 2);
Cv2.ImShow("Camera preview", frame);
// Only sleep for whatever time is left in the frame budget instead of
// always sleeping the full 1000/fps on top of WaitKey (which halved
// the effective frame rate in the original code).
int budgetMs = 1000 / TargetFps;
int elapsedMs = (int)frameTimer.ElapsedMilliseconds;
int waitMs = Math.Max(1, budgetMs - elapsedMs);
int key = Cv2.WaitKey(waitMs);
if (key == 27 || key == 'q')
break;
}
}
finally
{
foreach (var mat in ImageCache.Values)
mat?.Dispose();
Cv2.DestroyAllWindows();
}
}
static void ProcessFrame(Mat frame, Mat gray)
{
Cv2.CvtColor(frame, gray, ColorConversionCodes.BGR2GRAY);
// Contrast-normalize before decoding - this is the single biggest lever for detection
// reliability under uneven lighting (glare, shadow across part of the code, dim rooms).
using var enhanced = new Mat();
Clahe.Apply(gray, enhanced);
Result[] results = ReadQrCodes(enhanced);
var seenThisFrame = new HashSet<string>();
foreach (var result in results)
{
if (result?.Text == null) continue;
var (pTL, pTR, pBR, pBL) = CornersFromResult(result);
if (pTL == null) continue;
UpdateTracked(result.Text, pTL.Value, pTR.Value, pBR.Value, pBL.Value);
seenThisFrame.Add(result.Text);
EnsureImageLoaded(result.Text);
}
// For codes we're already tracking but the global pass didn't find this frame, actively
// re-search a small, upscaled crop around their last known position instead of just
// freezing them in place. This recovers far more often for codes that are small,
// partially blurred, or at a steep angle - the global full-frame pass is much less
// likely to catch those, but a zoomed-in targeted decode often still can.
foreach (var kv in Tracked)
{
if (seenThisFrame.Contains(kv.Key)) continue;
if (TryLocalRedetect(enhanced, kv.Value.RawQuad, out var pTL, out var pTR, out var pBR, out var pBL))
{
UpdateTracked(kv.Key, pTL, pTR, pBR, pBL);
seenThisFrame.Add(kv.Key);
}
}
// Age out codes that weren't seen this frame; keep them briefly to avoid flicker.
List<string> toRemove = null;
foreach (var kv in Tracked)
{
if (seenThisFrame.Contains(kv.Key)) continue;
kv.Value.MissedFrames++;
if (kv.Value.MissedFrames > MaxMissedFrames)
(toRemove ??= new List<string>()).Add(kv.Key);
}
if (toRemove != null)
foreach (var key in toRemove) Tracked.Remove(key);
if (!EnableOverlay) return;
foreach (var kv in Tracked)
{
if (ImageCache.TryGetValue(kv.Key, out Mat img) && img != null && !img.Empty())
{
OverlayImagePerspective(frame, img, kv.Value.Quad);
}
}
}
static void UpdateTracked(string text, Point2f tl, Point2f tr, Point2f br, Point2f bl)
{
Point2f[] raw = { tl, tr, br, bl };
Point2f[] quad = ExpandQuad(tl, tr, br, bl, QuadExpansion);
if (Tracked.TryGetValue(text, out var t))
{
t.RawQuad = raw;
t.Quad = quad;
t.MissedFrames = 0;
}
else
{
Tracked[text] = new TrackedQr { RawQuad = raw, Quad = quad, MissedFrames = 0 };
}
}
/// <summary>
/// ZXing QR point order: [0]=bottom-left, [1]=top-left, [2]=top-right, optional [3]=alignment
/// pattern. We deliberately do NOT use points[3] for the bottom-right corner: the alignment
/// pattern sits several modules inset from the actual edge of the code, so it's always
/// noticeably closer to the center than the true corner is - using it directly shrinks that
/// whole side of the overlay. Instead derive the true bottom-right corner from the 3
/// finder-pattern corners, which ZXing locates precisely, assuming the QR is (near) square:
/// BR = TR + BL - TL.
/// </summary>
static (Point2f? tl, Point2f? tr, Point2f? br, Point2f? bl) CornersFromResult(Result result)
{
var points = result?.ResultPoints;
if (points == null || points.Length < 3) return (null, null, null, null);
Point2f pBL = new Point2f(points[0].X, points[0].Y);
Point2f pTL = new Point2f(points[1].X, points[1].Y);
Point2f pTR = new Point2f(points[2].X, points[2].Y);
Point2f pBR = new Point2f(pTR.X + pBL.X - pTL.X, pTR.Y + pBL.Y - pTL.Y);
return (pTL, pTR, pBR, pBL);
}
/// <summary>Expand a quad (TL,TR,BR,BL) outward from its centroid by a scale factor.</summary>
static Point2f[] ExpandQuad(Point2f tl, Point2f tr, Point2f br, Point2f bl, float scale)
{
float cx = (tl.X + tr.X + br.X + bl.X) / 4f;
float cy = (tl.Y + tr.Y + br.Y + bl.Y) / 4f;
Point2f Expand(Point2f p) => new Point2f(cx + (p.X - cx) * scale, cy + (p.Y - cy) * scale);
return new[] { Expand(tl), Expand(tr), Expand(br), Expand(bl) };
}
/// <summary>
/// Warp `overlayImage` onto the quadrilateral `quad` (TL,TR,BR,BL) in `frame`, so the
/// pasted image follows the QR code's rotation/perspective instead of a plain axis-aligned resize.
/// Only the quad's bounding box is warped, not the whole frame, to keep this fast with
/// several codes on screen at once.
/// </summary>
static void OverlayImagePerspective(Mat frame, Mat overlayImage, Point2f[] quad)
{
var intPoints = quad.Select(p => new Point((int)p.X, (int)p.Y)).ToArray();
Rect rect = Cv2.BoundingRect(intPoints);
rect = rect.Intersect(new Rect(0, 0, frame.Width, frame.Height));
if (rect.Width <= 1 || rect.Height <= 1) return;
int w = overlayImage.Width, h = overlayImage.Height;
Point2f[] src = { new(0, 0), new(w, 0), new(w, h), new(0, h) };
Point2f[] dstLocal = quad.Select(p => new Point2f(p.X - rect.X, p.Y - rect.Y)).ToArray();
using Mat homography = Cv2.GetPerspectiveTransform(src, dstLocal);
using var warped = new Mat();
Cv2.WarpPerspective(overlayImage, warped, homography, rect.Size);
using var mask = new Mat(rect.Size, MatType.CV_8UC1, Scalar.Black);
var localPoly = dstLocal.Select(p => new Point((int)p.X, (int)p.Y)).ToArray();
Cv2.FillPoly(mask, new[] { localPoly }, Scalar.White);
using var roi = new Mat(frame, rect);
warped.CopyTo(roi, mask);
}
// ---- decoding ---------------------------------------------------
static Result[] ReadQrCodes(Mat gray)
{
var luminanceSource = ToLuminanceSource(gray, ref _grayBuffer);
var bitmap = new BinaryBitmap(new HybridBinarizer(luminanceSource));
try
{
return MultiReader.decodeMultiple(bitmap, Hints) ?? Array.Empty<Result>();
}
catch
{
return Array.Empty<Result>();
}
}
/// <summary>
/// Crop tightly around a code's last known position (with padding for movement), upscale
/// that crop, and try to decode just it. Works much better than a full-frame scan for a
/// code that's already small, tilted, or motion-blurred, since the decoder effectively gets
/// a bigger, cleaner version of just the region that matters.
/// </summary>
static bool TryLocalRedetect(Mat enhanced, Point2f[] lastRaw, out Point2f tl, out Point2f tr, out Point2f br, out Point2f bl)
{
tl = tr = br = bl = default;
if (lastRaw == null) return false;
var intPoints = lastRaw.Select(p => new Point((int)p.X, (int)p.Y)).ToArray();
Rect box = Cv2.BoundingRect(intPoints);
int pad = (int)(Math.Max(box.Width, box.Height) * LocalRedetectPadding);
Rect search = new Rect(box.X - pad, box.Y - pad, box.Width + pad * 2, box.Height + pad * 2);
search = search.Intersect(new Rect(0, 0, enhanced.Width, enhanced.Height));
if (search.Width < 15 || search.Height < 15) return false;
using var crop = new Mat(enhanced, search);
using var scaled = new Mat();
Cv2.Resize(crop, scaled, new Size(crop.Width * LocalRedetectUpscale, crop.Height * LocalRedetectUpscale),
0, 0, InterpolationFlags.Cubic);
byte[] localBuffer = null;
var luminanceSource = ToLuminanceSource(scaled, ref localBuffer);
var bitmap = new BinaryBitmap(new HybridBinarizer(luminanceSource));
Result result;
try { result = QrReader.decode(bitmap, LocalHints); }
catch { return false; }
if (result == null) return false;
var (cTl, cTr, cBr, cBl) = CornersFromResult(result);
if (cTl == null) return false;
// Map corners from the upscaled crop back into full-frame coordinates.
Point2f ToFrame(Point2f p) => new Point2f(
search.X + p.X / LocalRedetectUpscale,
search.Y + p.Y / LocalRedetectUpscale);
tl = ToFrame(cTl.Value);
tr = ToFrame(cTr.Value);
br = ToFrame(cBr.Value);
bl = ToFrame(cBl.Value);
return true;
}
static RGBLuminanceSource ToLuminanceSource(Mat gray, ref byte[] buffer)
{
Mat continuous = gray.IsContinuous() ? gray : gray.Clone();
int size = (int)(continuous.Total() * continuous.ElemSize());
if (buffer == null || buffer.Length != size)
buffer = new byte[size];
Marshal.Copy(continuous.Data, buffer, 0, size);
// Gray8 skips ZXing's internal RGB->luminance conversion entirely - the fastest
// format to hand it, and matches what BGR2GRAY/CLAHE already produced for us.
var source = new RGBLuminanceSource(buffer, continuous.Width, continuous.Height, RGBLuminanceSource.BitmapFormat.Gray8);
if (!ReferenceEquals(continuous, gray))
continuous.Dispose();
return source;
}
// ---- async image download/caching --------------------------------
static void EnsureImageLoaded(string url)
{
if (ImageCache.ContainsKey(url) || Pending.ContainsKey(url)) return;
if (!Uri.TryCreate(url, UriKind.Absolute, out var uri) ||
(uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps))
{
// Not a downloadable URL - remember that so we don't keep retrying every frame.
ImageCache[url] = null;
return;
}
Pending[url] = 0;
_ = Task.Run(async () =>
{
try
{
byte[] bytes = await Http.GetByteArrayAsync(uri);
Mat decoded = Mat.FromImageData(bytes, ImreadModes.Color);
ImageCache[url] = decoded.Empty() ? null : decoded;
}
catch (Exception ex)
{
Console.WriteLine($"Failed to download {url}: {ex.Message}");
ImageCache[url] = null; // negative-cache so we stop retrying a bad URL
}
finally
{
Pending.TryRemove(url, out _);
}
});
}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB