Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4b0e597a9 | ||
|
|
765df60730 | ||
|
|
7e9f11eaa2 | ||
|
|
3df7c68a3f | ||
|
|
2315c31d8c | ||
|
|
84c433ad19 | ||
|
|
71c48a5a6f | ||
|
|
90120ee2cd | ||
|
|
66ba176204 | ||
|
|
a884b974cc | ||
|
|
4059a3b67b | ||
|
|
e6a8c2f6e5 | ||
|
|
edd9797246 | ||
|
|
234cd96109 | ||
|
|
e323482b2e | ||
|
|
0a781d9cbe | ||
|
|
9fae9410fb | ||
|
|
6e230b39ba | ||
|
|
811773062e | ||
|
|
084aefbbd7 |
@@ -1,2 +1,12 @@
|
||||
/bin
|
||||
/obj
|
||||
/qrCamera/bin
|
||||
/qrCamera/obj
|
||||
/qrCamera/cache
|
||||
/qrCameraAI/bin
|
||||
/qrCameraAI/obj
|
||||
/qrCameraAI/cache
|
||||
/createqrcodes/bin
|
||||
/createqrcodes/obj
|
||||
/cache
|
||||
/qr
|
||||
/createqr
|
||||
/qrcam
|
||||
@@ -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
|
||||
|
Before Width: | Height: | Size: 3.0 MiB |
|
Before Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 19 KiB |
@@ -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 "%s""
|
||||
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>
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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>
|
||||
|
Before Width: | Height: | Size: 850 KiB After Width: | Height: | Size: 850 KiB |
|
Before Width: | Height: | Size: 980 KiB After Width: | Height: | Size: 980 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
@@ -7,6 +7,9 @@ using ZXing.Common;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Drawing;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices.Marshalling;
|
||||
using Emgu.CV.Face;
|
||||
using System.Transactions;
|
||||
|
||||
// using Emgu.CV.CvEnum;
|
||||
|
||||
@@ -15,6 +18,7 @@ namespace BarcodeReaderExample
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static bool draw;
|
||||
static void Main(string[] args)
|
||||
{
|
||||
VideoCapture camera = new VideoCapture("/dev/video0", VideoCaptureAPIs.V4L2);
|
||||
@@ -24,7 +28,9 @@ namespace BarcodeReaderExample
|
||||
|
||||
camera.Set(VideoCaptureProperties.FrameWidth, 1280);
|
||||
camera.Set(VideoCaptureProperties.FrameHeight, 720);
|
||||
camera.Set(VideoCaptureProperties.Fps, 24);
|
||||
int fps = 60;
|
||||
int avalibeImages = 20;
|
||||
camera.Set(VideoCaptureProperties.Fps, fps);
|
||||
if (Directory.Exists("cache"))
|
||||
{
|
||||
Directory.Delete("cache", true);
|
||||
@@ -32,8 +38,11 @@ namespace BarcodeReaderExample
|
||||
|
||||
Directory.CreateDirectory("cache");
|
||||
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();
|
||||
|
||||
@@ -44,35 +53,40 @@ namespace BarcodeReaderExample
|
||||
|
||||
|
||||
Cv2.NamedWindow("Camera preview", WindowFlags.AutoSize);
|
||||
int photocount = 0;
|
||||
Mat src = Cv2.ImRead("test.png");
|
||||
|
||||
|
||||
|
||||
string[] files = new string[10];
|
||||
// Mat src = Cv2.ImRead("test.png");
|
||||
int whenWasLastTimeQrDetected = 0;
|
||||
Mat Lastimage = new Mat();
|
||||
IEnumerable<IEnumerable<OpenCvSharp.Point>> contours = null;
|
||||
// PointF[] location;
|
||||
Mat previue = new Mat();
|
||||
string[] files = new string[avalibeImages];
|
||||
int files_index = 0;
|
||||
while (true)
|
||||
{
|
||||
// if (!camera.Read(frame) || frame.Empty())
|
||||
// {
|
||||
// Console.Error.WriteLine("Nie udało się odczytać klatki");
|
||||
// break;
|
||||
// }
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
whenWasLastTimeQrDetected = 5;
|
||||
List<ResultPoint> points = result.ResultPoints.ToList();
|
||||
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[] pos3 = new float[2] { points[1].X, points[1].Y }; ///lewy górny róg
|
||||
int width = (int)(pos2[0] - pos3[0]);
|
||||
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;
|
||||
try
|
||||
{
|
||||
@@ -83,19 +97,6 @@ namespace BarcodeReaderExample
|
||||
{
|
||||
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>
|
||||
{
|
||||
GetPoint(location[0]),
|
||||
@@ -103,14 +104,19 @@ namespace BarcodeReaderExample
|
||||
GetPoint(location[3]),
|
||||
GetPoint(location[2])
|
||||
};
|
||||
|
||||
// Zagnieźdź w dodatkową kolekcję:
|
||||
IEnumerable<IEnumerable<OpenCvSharp.Point>> contours = new[] { points1 };
|
||||
int qrWidth = GetPoint(location[1]).X - GetPoint(location[0]).X;
|
||||
int qrHeigh = GetPoint(location[2]).Y - GetPoint(location[0]).Y;
|
||||
|
||||
|
||||
// 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))
|
||||
{
|
||||
@@ -121,40 +127,66 @@ namespace BarcodeReaderExample
|
||||
{
|
||||
client.DownloadFile(result.Text, "cache/" + files_index + ".png");
|
||||
// Thread.Sleep(2000);
|
||||
|
||||
}
|
||||
Cached[files_index] = new Mat("cache/" + files_index + ".png");
|
||||
files_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
int toload = files.IndexOf(result.Text);
|
||||
Mat toDraw= new Mat();
|
||||
|
||||
|
||||
InputArray inputArray = toDraw;
|
||||
// Console.WriteLine($"{qrWidth} , {qrHeigh}");
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
int toload = files.IndexOf(result.Text);
|
||||
Mat roi2 = frame[new Rect((int)pos3[0], (int)pos3[1], Cached[toload].Width, Cached[toload].Height)];
|
||||
Cached[toload].CopyTo(roi2);
|
||||
Cv2.Resize(Cached[toload],toDraw,new OpenCvSharp.Size(qrWidth,qrHeigh));
|
||||
}
|
||||
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 { }
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 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);
|
||||
Thread.Sleep(1000 / 24);
|
||||
// Console.WriteLine("tick");
|
||||
Thread.Sleep(1000 / fps);
|
||||
|
||||
int key = Cv2.WaitKey(1);
|
||||
if (key == 27 || key == 'q')
|
||||
break;
|
||||
@@ -163,6 +195,16 @@ namespace BarcodeReaderExample
|
||||
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)
|
||||
{
|
||||
return new OpenCvSharp.Point(pointf.X, pointf.Y);
|
||||
@@ -211,7 +253,11 @@ namespace BarcodeReaderExample
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
static void DrawLine(PointF[] Location)
|
||||
@@ -230,7 +276,7 @@ namespace BarcodeReaderExample
|
||||
}
|
||||
static Mat frame = new Mat();
|
||||
|
||||
static Result GetResult(Mat frame)
|
||||
static Result ReadQrCode(Mat frame)
|
||||
{
|
||||
|
||||
QRCodeReader reader = new QRCodeReader();
|
||||
@@ -284,13 +330,3 @@ namespace BarcodeReaderExample
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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 _);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB |