[React] OpenCV で画像のトップハット、ブラックハットをしてみる | 心を無にして始める React
この記事は、たぶん優先度が低いです。
にわかには使いどころが、思いつかない (。´・ω・)
準備
React で OpenCV.js を使えるようにしておきます。
今回も、形態を変化させるモルフォロジー処理を使っていきます。
OpenCVで定義されるタイプは、膨張、収縮、オープニング、クロージング、勾配、ブラックハット、トップハット、Hit or Miss があります。
https://docs.opencv.org/5.x/d4/d86/group__imgproc__filter.html#ga7be549266bad7b2e6a04db49827f9f32
今回は、用途のよくわからない
ブラックハット
と トップハット を使ってみます。
(オープニングやクロージングでノイズをどのぐらい除去できるかな?って見れるぐらい?思いつかない。)
トップハット
オープニング(収縮 してから 膨張 することで、白色に近いノイズを消す)と 元の画像 の差が得られます。
つまり、オープニングで除去できたノイズ相当が見れます。
cv.MORPH_TOPHAT を指定して morphologyEx を呼びます。
イメージ
局所的なサンプル
const kernelSize = cv.Mat.ones(5, 5, cv.CV_8U);
cv.morphologyEx(src, dst, cv.MORPH_TOPHAT, kernelSize);
使ってみた
イメージを出すときに書いた App.js です。
import React, { useState } from 'react';
import './App.css';
import Button from './components/Button';
import Dropzone from "./components/DropZone";
const cv = window.cv;
function App() {
const [urls, setUrls] = useState([]);
const onDrop = (acceptedFiles) => {
if (acceptedFiles) {
setUrls(acceptedFiles.map(acceptedFile => URL.createObjectURL(acceptedFile)))
}
}
const handleClick = (cb) => {
if (urls.length <= 0) {
cb();
return;
}
urls.forEach((url, i) => {
const img = new Image();
img.src = url;
img.onload = () => {
img.height = 240;
img.width = img.naturalWidth * (240 / img.naturalHeight);
const imageMat = cv.imread(img);
cv.cvtColor(imageMat, imageMat, cv.COLOR_RGBA2RGB);
const kernelSize = cv.Mat.ones(5, 5, cv.CV_8U);
cv.morphologyEx(imageMat, imageMat, cv.MORPH_TOPHAT, kernelSize);
cv.imshow(`canvas-${i}`, imageMat);
kernelSize.delete();
imageMat.delete();
cb();
}
})
}
return (
<>
<div className="bg-dark p-5" style={{ minHeight: '100vh', height: '100%' }}>
<Dropzone className="w-100" onDrop={onDrop}>
<div className="d-flex justify-content-center align-items-center p-5">
{
urls.length === 0 && (
<span>ファイルをドロップしてください</span>
)
}
{
urls.map(url => (
<div key={url?.toString()} className="border border-dark m-2">
<img src={url} alt="" style={{ maxWidth: 320, maxHeight: 240 }} />
</div>
))
}
</div>
</Dropzone>
<div className="d-flex justify-content-center align-items-center p-5">
<Button valiant="info" onClick={handleClick}>トップハット</Button>
</div>
{
urls.length && (
<div className="d-flex justify-content-center align-items-center p-5 bg-light">
{
urls.map((url, i) => (
<div className="m-2">
<canvas className="border border-dark bg-light" key={url?.toString()} id={`canvas-${i}`} />
</div>
))
}
</div>
)
}
</div>
</>
);
}
export default App;
ブラックハット
クロージング(膨張 してから 収縮 することで、黒色に近いノイズを消す)と 元の画像 の差が得られます。
つまり、クロージングで除去できたノイズ相当が見れます。
cv.MORPH_BLACKHAT を指定して morphologyEx を呼びます。
イメージ
局所的なサンプル
const kernelSize = cv.Mat.ones(5, 5, cv.CV_8U);
cv.morphologyEx(src, dst, cv.MORPH_BLACKHAT, kernelSize);
使ってみた
イメージを出すときに書いた App.js です。
import React, { useState } from 'react';
import './App.css';
import Button from './components/Button';
import Dropzone from "./components/DropZone";
const cv = window.cv;
function App() {
const [urls, setUrls] = useState([]);
const onDrop = (acceptedFiles) => {
if (acceptedFiles) {
setUrls(acceptedFiles.map(acceptedFile => URL.createObjectURL(acceptedFile)))
}
}
const handleClick = (cb) => {
if (urls.length <= 0) {
cb();
return;
}
urls.forEach((url, i) => {
const img = new Image();
img.src = url;
img.onload = () => {
img.height = 240;
img.width = img.naturalWidth * (240 / img.naturalHeight);
const imageMat = cv.imread(img);
cv.cvtColor(imageMat, imageMat, cv.COLOR_RGBA2RGB);
const kernelSize = cv.Mat.ones(5, 5, cv.CV_8U);
cv.morphologyEx(imageMat, imageMat, cv.MORPH_BLACKHAT, kernelSize);
cv.imshow(`canvas-${i}`, imageMat);
kernelSize.delete();
imageMat.delete();
cb();
}
})
}
return (
<>
<div className="bg-dark p-5" style={{ minHeight: '100vh', height: '100%' }}>
<Dropzone className="w-100" onDrop={onDrop}>
<div className="d-flex justify-content-center align-items-center p-5">
{
urls.length === 0 && (
<span>ファイルをドロップしてください</span>
)
}
{
urls.map(url => (
<div key={url?.toString()} className="border border-dark m-2">
<img src={url} alt="" style={{ maxWidth: 320, maxHeight: 240 }} />
</div>
))
}
</div>
</Dropzone>
<div className="d-flex justify-content-center align-items-center p-5">
<Button valiant="info" onClick={handleClick}>ブラックハット</Button>
</div>
{
urls.length && (
<div className="d-flex justify-content-center align-items-center p-5 bg-light">
{
urls.map((url, i) => (
<div className="m-2">
<canvas className="border border-dark bg-light" key={url?.toString()} id={`canvas-${i}`} />
</div>
))
}
</div>
)
}
</div>
</>
);
}
export default App;
はい、できました。
トラブルシュート
結果が真っ白になる
srcに指定するMATにαチャネルが含まれているときは、正しく動作しませんでした。
例えば、次のように RGBA から RGB に変えるとことで、動作するようになりました。
const kernelSize = cv.Mat.ones(5, 5, cv.CV_8U);
cv.cvtColor(imageMat, imageMat, cv.COLOR_RGBA2RGB);
cv.morphologyEx(imageMat, imageMat, cv.MORPH_TOPHAT, kernelSize);
ここにコードがないコンポーネントは、過去の記事にあります ('◇’)ゞ