langchain-ollamaがconda対応していない。どうせpipするので全てpipで導入する。
faissを使い、テキストをベクトル化することでテキストデータの距離を検索できるようにする。
data.txtをベクトル化し、faiss_indexという名前でファイル保存する。
from langchain_ollama import OllamaEmbeddings from langchain_community.vectorstores import FAISS from langchain.schema import Document import re output_data_name = "faiss_index" # pip install faiss-cpu # pip install -U langchain-ollama # pip install langchain-community ########################################################### # data.txtを1行ずつ読み込み、1行=1チャンクとして扱う (1チャンク=1ベクトル) documents = [] with open("data.txt", encoding="utf-8") as f: for line in f: line = line.strip() if line: documents.append(Document(page_content=line)) ########################################################### # データを表示 for i, doc in enumerate(documents): print(f"チャンク{i}: {doc.page_content}") ########################################################### # ステップ3: ベクトル化してFAISSに保存 embedding = OllamaEmbeddings(model="nomic-embed-text") # テキスト用埋め込みモデル vectorstore = FAISS.from_documents(documents, embedding) vectorstore.save_local(output_data_name)
from langchain_ollama import OllamaEmbeddings from langchain_community.vectorstores import FAISS from langchain_ollama import OllamaLLM from langchain.chains import RetrievalQA import re usellm = OllamaLLM(model="qwen3:8b") ########################################################### embedding = OllamaEmbeddings(model="nomic-embed-text") # テキスト埋め込み用モデル retriever = FAISS.load_local( "faiss_index" ,embedding # 検索用モデル ,allow_dangerous_deserialization=True ).as_retriever() qa_chain = RetrievalQA.from_chain_type(llm=usellm, retriever=retriever) ########################################################### # 質問する query = "解説文から、関西地方に関係する物語であることがわかる作品を教えてください" answer = qa_chain.invoke(query) raw = answer["result"] response_without_think = re.sub(r"<think>.*?</think>", "", raw, flags=re.DOTALL) print(response_without_think)
文字列をスクリプト単位に分離する。スクリプトはHarfBuzzで描画するときに使う。
#include <iostream> #include <unicode/ubrk.h> #include <unicode/ustring.h> #include <unicode/uscript.h> #include <unicode/uchar.h> #include <vector> #include <fstream> #include <algorithm> #include <Windows.h> #include <fcntl.h> #include <io.h> // 要リンク #if defined(_DEBUG) #pragma comment(lib, "icuucd.lib") #else #pragma comment(lib, "icuuc.lib") #endif struct Grapheme { int32_t start; int32_t end; };
// 文字列を書記素単位でアクセスできるようにするリストを作成 std::vector<Grapheme> createGraphemeList(const char16_t* text, const size_t length) { UErrorCode status = U_ZERO_ERROR; std::vector<Grapheme> graphemes; // イテレータ作成 UBreakIterator* bi = ubrk_open(UBRK_CHARACTER, "ja_JP", nullptr, 0, &status); if (U_FAILURE(status)) { return std::vector<Grapheme>(); // エラーが発生 } // テキストを設定 ubrk_setText(bi, (const UChar*)text, length, &status); if (U_FAILURE(status)) { ubrk_close(bi); return std::vector<Grapheme>(); // エラーが発生 } // 最初の書記素の位置を取得 int32_t start = ubrk_first(bi); int32_t end; // 書記素リストを作成 while ((end = ubrk_next(bi)) != UBRK_DONE) { graphemes.push_back(Grapheme{ start, end }); start = end; } // 終了処理 ubrk_close(bi); return graphemes; }
// 指定した書記素をutf16 からコードポイントに変換 UChar32 getCodepoint(const char16_t* text, const Grapheme& g) { UChar32 codepoint; size_t start = g.start; size_t end = g.end; U16_NEXT(text, start, end, codepoint); return codepoint; }
// 各文字ごとにスクリプトを特定 std::vector< std::pair<size_t, UScriptCode> > createScriptList(const char16_t* text, const std::vector<Grapheme>& glist) { std::vector< std::pair<size_t, UScriptCode> > scliptslice; UScriptCode latest; UErrorCode err; UChar32 codepoint; UScriptCode script; for (size_t i = 0; i < glist.size(); i++) { codepoint = getCodepoint(text, glist[i]); script = uscript_getScript(codepoint, &err); if (U_SUCCESS(err)) { latest = script; } else { latest = USCRIPT_UNKNOWN; } scliptslice.push_back({ i, latest }); } return scliptslice; }
struct ScriptSlice { size_t grapheme_start; size_t grapheme_end; UScriptCode script; };
// 一文字ごとに設定されているスクリプトを元に、同じスクリプトが連続している部分をひとまとめにする std::vector<ScriptSlice> ScriptListShrink(const std::vector< std::pair<size_t, UScriptCode> >& ss) { std::vector<ScriptSlice> slist; UScriptCode script = ss[0].second; slist.push_back({ ss[0].first, ss[0].first, ss[0].second}); for (size_t i = 1; i < ss.size(); i++) { if (ss[i].second != slist.back().script) { slist.back().grapheme_end = ss[i].first-1; slist.push_back({ ss[i].first,ss[i].first, ss[i].second }); } } slist.back().grapheme_end = ss.back().first; return slist; }
int main() { // 日本語ロケール std::locale::global(std::locale("japanese")); std::u16string u16str = u"あいうイロハホヘト你好😁👩👨👦👧ÄɪʊabcQué"; // 書記素リスト作成 std::vector<Grapheme> glist = createGraphemeList(u16str.data(), u16str.length()); // 書記素リストを元にスクリプトリストを作成 std::vector< std::pair<size_t, UScriptCode> > slist = createScriptList(u16str.data(), glist); // スクリプトリストを元に、同じスクリプトが連続している部分をひとまとめにする std::vector<ScriptSlice> ss = ScriptListShrink(slist); // 同じスクリプト単位で表示 for (size_t i = 0; i < ss.size(); i++) { size_t u16start = glist[ss[i].grapheme_start].start; size_t u16end = glist[ss[i].grapheme_end].end; size_t length = u16end - u16start; std::u16string u16wstr(u16str.data() + u16start, length); std::wcout << L"[" << ss[i].script << L"] "; WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), u16wstr.c_str(), length, nullptr, nullptr); WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), L"\n", 2, nullptr, nullptr); } return 0; }
、や全角英数など文字によってはイメージと違うことがある。
std::u16string u16str = u"あいうイロハホヘト你好😁👩👨👦👧ÄɪʊabcQué,'\"-、ABC”‘’"
vtkPolyData一つだけであればvtp形式で保存できるが、複数のvtkPolyDataを保存する場合はvtm形式にする。vtmは一つの.vtmと、各vtkPolyData毎に.vtpを出力する。
#include <iostream> //VTK_MODULE_INITに必要 #include <vtkAutoInit.h> #include <vtkSmartPointer.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkActor.h> #include <vtkPolyData.h> #include <vtkCellArray.h> #include <vtkPoints.h> #include <vtkLine.h> #include <vtkTriangle.h> #include <vtkPolyDataMapper.h> //properties #include <vtkProperty.h> #include <vtkPolyLine.h> #include <vtkCylinderSource.h> #include <vtkXMLPolyDataWriter.h> #include <vtkXMLMultiBlockDataWriter.h> #include <vtkMultiBlockDataSet.h> #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"psapi.lib") #pragma comment(lib,"dbghelp.lib") #pragma comment(lib,"ws2_32.lib") //必須 VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle);
vtkSmartPointer<vtkPolyData> PolyLine() { // ポイントデータの作成 vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); double rrr = 1.0; points->InsertNextPoint(0.0, 0.0, 0.0); points->InsertNextPoint(-rrr, 0.0, 0.0); points->InsertNextPoint(-rrr, 0.0, rrr); points->InsertNextPoint(0.0, 0.0, rrr); points->InsertNextPoint(0.0, 0.0, 0.0); // 閉じた形状 // PolyLineの作成 vtkSmartPointer<vtkPolyLine> polyLine = vtkSmartPointer<vtkPolyLine>::New(); polyLine->GetPointIds()->SetNumberOfIds(5); // 点の数 for (vtkIdType i = 0; i < 5; ++i) { polyLine->GetPointIds()->SetId(i, i); } // CellArrayに追加 vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New(); cells->InsertNextCell(polyLine); // PolyDataの作成 vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New(); polyData->SetPoints(points); polyData->SetLines(cells); return polyData; }
vtkSmartPointer<vtkPolyData> PolyCylinder() { // vtkCylinderSource の作成 vtkSmartPointer<vtkCylinderSource> cylinderSource = vtkSmartPointer<vtkCylinderSource>::New(); cylinderSource->SetHeight(5.0); cylinderSource->SetRadius(0.2); cylinderSource->SetResolution(50); cylinderSource->Update(); // 出力を確定させる // vtkPolyData に変換 vtkSmartPointer<vtkPolyData> cylinderPolyData = cylinderSource->GetOutput(); return cylinderPolyData; }
void save_as_vtm(std::vector<vtkSmartPointer<vtkPolyData>> polyDataList, const std::string& filename) { vtkSmartPointer<vtkXMLMultiBlockDataWriter> writer = vtkSmartPointer<vtkXMLMultiBlockDataWriter>::New(); writer->SetFileName(filename.c_str()); vtkSmartPointer<vtkMultiBlockDataSet> multiBlock = vtkSmartPointer<vtkMultiBlockDataSet>::New(); for (size_t i = 0; i < polyDataList.size(); ++i) { multiBlock->SetBlock(i, polyDataList[i]); } writer->SetInputData(multiBlock); writer->Write(); }
int main(int /*argc*/, char** /*argv*/) { vtkSmartPointer<vtkPolyData> polyline = PolyLine(); vtkSmartPointer<vtkPolyData> cylinder = PolyCylinder(); save_as_vtm({ polyline, cylinder }, "items.vtm"); vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->SetBackground(0.1, 0.1, 0.1); vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputData(polyline); // Actorの作成 vtkSmartPointer<vtkActor> actorPolyline = vtkSmartPointer<vtkActor>::New(); actorPolyline->SetMapper(mapper); vtkSmartPointer<vtkPolyDataMapper> cylinderMapper = vtkSmartPointer<vtkPolyDataMapper>::New(); cylinderMapper->SetInputData(cylinder); vtkSmartPointer<vtkActor> cylinderActor = vtkSmartPointer<vtkActor>::New(); cylinderActor->SetMapper(cylinderMapper); renderer->AddActor(actorPolyline); renderer->AddActor(cylinderActor); vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); vtkSmartPointer<vtkRenderWindowInteractor> interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); interactor->SetRenderWindow(renderWindow); renderWindow->Render(); interactor->Start(); return 0; }
実行すると、
の3ファイルが出力される。
動作確認はparaviewでできる。
Skiaでフォントを指定して文字列を描画する。Freetype2とHarfbuzzが必要。
Skiaでフォント使用(1)文字列描画
#pragma comment(lib,"skia.dll.lib") #pragma comment(lib,"skshaper.dll.lib") ///////////////////////////////////// #include "skia/include/core/SkCanvas.h" #include "skia/include/core/SkBitmap.h" #include "skia/include/core/SkSurface.h" #include "include/core/SkSurface.h" ///////////////////////////////////// // テキスト描画 #include "include/core/SkTypeface.h" #include "include/core/SkFont.h" #include "include/core/SkFontMgr.h" #include "include/ports/SkFontMgr_directory.h" // フォントマネージャ作成 #include "include/core/SkFontMetrics.h" // 文字のレイアウト #include "modules/skshaper/include/SkShaper.h" ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // Png保存に必要 #include "include/encode/SkPngEncoder.h" #include "include/core/SkStream.h" /////////////////////////////////////
// SkShaperで文字列を描画するためのクラス class CanvasRunHandler : public SkShaper::RunHandler { public: CanvasRunHandler(SkCanvas* canvas, SkPoint origin, const SkFont& font, const SkPaint& paint) : canvas_(canvas), origin_(origin), font_(font), paint_(paint) {} void runInfo(const RunInfo& info) override { glyphCount_ = info.glyphCount; glyphs_.resize(glyphCount_); positions_.resize(glyphCount_); } Buffer runBuffer(const RunInfo& info) override { return { glyphs_.data(), // glyphs positions_.data(), // positions nullptr, // offsets nullptr, // clusters origin_ // point offset to add to all positions }; } void commitRunBuffer(const RunInfo& info) override { canvas_->drawGlyphs( glyphCount_, glyphs_.data(), positions_.data(), runOrigin_, font_, paint_ ); } void beginLine() override { } void commitRunInfo() override { } void commitLine() override { } private: SkCanvas* canvas_; SkPoint origin_; SkPoint runOrigin_; SkFont font_; SkPaint paint_; int glyphCount_ = 0; SkVector runOffset_; std::vector<SkGlyphID> glyphs_; std::vector<SkPoint> positions_; };
// テキスト描画のテスト SkBitmap TextTest() { // フォントマネージャ作成 sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_Custom_Directory("C:\\Windows\\Fonts\\"); // フォントが格納されているディレクトリ指定 //auto typeface = fontMgr->matchFamilyStyle("Segoe UI Emoji", SkFontStyle::Normal()); // カラー絵文字はうまく描画できない auto typeface = fontMgr->matchFamilyStyle("MS Gothic", SkFontStyle::Normal()); // フォントの取得 /////////////////////////////////////////////////////////////////////////////////////////// SkImageInfo imginfo = SkImageInfo::Make(500, 100, kN32_SkColorType, kPremul_SkAlphaType); sk_sp<SkSurface> surface = SkSurfaces::Raster(imginfo); SkCanvas* canvas = surface->getCanvas(); canvas->clear(SK_ColorWHITE); // 背景を白にクリア /////////////////////////////////////////////////////////////////////////////////////////// // テキストの描画 SkPaint paint; paint.setColor(SK_ColorBLACK); // テキストの色 paint.setAntiAlias(false); // アンチエイリアスを有効にする /////////////////////////////////////////////////////////////////////////////////////////// // フォントの設定 SkFont font; font.setEdging(SkFont::Edging::kAntiAlias); // エッジングの設定 font.setSize(50.f * 72 / 96); // テキストのサイズ font.setTypeface(typeface); // フォントの設定 /////////////////////////////////////////////////////////////////////////////////////////// SkScalar x = 30.f; // テキストの位置(横)を設定 SkScalar y = 50.0f; // テキストの位置(縦)を設定 SkFontMetrics metrics; font.getMetrics(&metrics); y -= metrics.fAscent;// テキストの位置(高さ)を設定 /////////////////////////////////////////////////////////////////////////////////////////// // カラー絵文字はうまく描画できない //SkString text((const char*)u8"😊👩👨👦👧"); SkString text((const char*)u8"がぎぐげご"); /////////////////////////////////////////////////////////////////////////////////////////// #if 1 // テキストの描画(結合文字対応) int width = 500; std::unique_ptr<SkShaper> shaper = SkShaper::Make(); SkPoint origin = SkPoint::Make(x, y); CanvasRunHandler runHandler(canvas, origin, font, paint); shaper->shape(text.data(), strlen(text.c_str()), font, true, width, &runHandler); #else // テキストの描画 canvas->drawString(text, x, y, font, paint); #endif ////////////////////////////////////////////////// // // PNG出力 SkPixmap pixmap; if (surface->peekPixels(&pixmap)) { SkFILEWStream stream("output.png"); SkPngEncoder::Options options; options.fZLibLevel = 9; SkPngEncoder::Encode(&stream, pixmap, options); } // Bitmap化 SkBitmap bitmap; bitmap.allocPixels(imginfo); surface->readPixels(bitmap.pixmap(), 0, 0); return bitmap; }
カラー絵文字についてもフォントをSegoe UI Emojiなどにすれば一応動くのだが、デバッグモードでは正しく出力できるのにリリースモードでは透明度かなにかがおかしくなる。意味が分からない。
画像ビューア的なものを作っているのだが、2000件くらいの画像を一括読み込みすると読み込みまでフリーズするような状態になったので、スレッドプールを使って操作中に読み込みをするようにした。
// https://docs.wxwidgets.org/3.0/overview_helloworld.html // プリプロセッサに以下二つを追加 // __WXMSW__ // WXUSINGDLL // サブシステムをWindowsに設定(WinMainで呼び出すので) // Windows (/SUBSYSTEM:WINDOWS) #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 #include <string> #include <wx/statbmp.h> #include <vector> #include <memory> #include <filesystem> #include "ThreadWorker.hpp"
//! @brief データをスレッドで読み込むクラス //! @details データの読み込みをスレッドプールで行う //! @tparam Source データの入手元 std::filesystem::pathなど //! @tparam Reference データの格納先 std::shared_ptr<DATA> など //! @tparam Job データの読み込みを行うスレッドプールのジョブ型 template<typename Source, typename Reference, typename Job> class MultiDataLoader { std::unique_ptr<ThreadPool> _pool; // スレッドプール public: MultiDataLoader(const int _threadcount_) : _pool( new ThreadPool(_threadcount_) ) {} bool isFinished() { return _pool->isAllJobCompleted(); } ~MultiDataLoader() { _pool.reset(); } //! @brief データの読み込みを開始する //! @param _source_ データの入手元一覧 //! @param ploaded データの格納先 //! @param unloadedsign 未読み込みの場合の値 nullptrなど void load(const std::vector<Source>& _source_, std::vector<Reference>* _ploaded_, Reference _unloadedsign_=nullptr) { // データの格納先を用意 _ploaded_->clear(); _ploaded_->resize(_source_.size(), _unloadedsign_); for (size_t index = 0; index < _source_.size(); index++) { _pool->add(std::make_unique<Job>(_ploaded_, _source_[index], index)); } } };
//! @brief データの読み込みを行うジョブ class JobLoadData : public JobBase { using Reference = std::shared_ptr<wxBitmap>; std::filesystem::path path; size_t index; std::vector<Reference>* ref; public: //! @brief コンストラクタ //! @param _pref_ データの格納先 //! @param _path_ データの入手元 //! @param _index_ データの格納先のインデックス JobLoadData(std::vector<Reference>* _pref_, const std::filesystem::path& _path_, size_t _index_) { ref = _pref_; path = _path_; index = _index_; } virtual void run() override { wxString fname = wxString::FromUTF8((const char*)path.u8string().c_str()); wxLogNull logNo; // 警告を抑制 // 拡張子チェック (*ref)[index] = std::make_shared<wxBitmap>(fname, wxBITMAP_TYPE_ANY); } };
//! @brief 指定したディレクトリ以下の、指定した拡張子のファイル一覧を取得する //! @param _dir_path_ 検索するディレクトリのパス //! @param extensions 検索対象の拡張子 //! @return 検索結果のファイルパス一覧 std::vector<std::filesystem::path> get_files_with_extensions(const std::filesystem::path& _dir_path_, const std::vector<std::string>& _extensions_) { std::vector<std::filesystem::path> file_list; if (!std::filesystem::exists(_dir_path_) || !std::filesystem::is_directory(_dir_path_)) { std::cerr << "Error: The provided path is not a valid directory.\n"; return file_list; } for (const auto& entry : std::filesystem::recursive_directory_iterator(_dir_path_)) { if (entry.is_regular_file()) { std::string ext = entry.path().extension().string(); if (std::find(_extensions_.begin(), _extensions_.end(), ext) != _extensions_.end()) { file_list.push_back(entry.path()); } } } return file_list; }
class MyFrame : public wxFrame { using DataLoader = MultiDataLoader<std::filesystem::path, std::shared_ptr<wxBitmap>, JobLoadData>; std::unique_ptr< DataLoader > ploader; public: MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size), currentIndex(0) { // 画像ファイルの読み込み // wxBITMAP_TYPE_JPEG 等 wxInitAllImageHandlers(); // ファイル一覧の取得 auto sourcepath = LR"(C:\test\images)"; std::vector<std::filesystem::path> paths = get_files_with_extensions(sourcepath, { ".png",".jpg"}); // データ読み込みの準備(8スレッド) ploader = std::make_unique< DataLoader >(8); // データのロード開始 ploader->load(paths, &bitmaps); // 初期状態 空の画像を表示 imageCtrl = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap, wxPoint(10, 10), wxSize(400, 300)); // キーボードイベントを処理 Bind(wxEVT_KEY_DOWN, &MyFrame::OnKeyDown, this); } ~MyFrame() { ploader.reset(); } private: std::vector<std::shared_ptr<wxBitmap>> bitmaps; wxStaticBitmap* imageCtrl; size_t currentIndex; void OnKeyDown(wxKeyEvent& event) { if (event.GetKeyCode() == WXK_LEFT) { if (currentIndex > 0) { currentIndex--; UpdateImage(); } } else if (event.GetKeyCode() == WXK_RIGHT) { if (currentIndex < bitmaps.size() - 1) { currentIndex++; UpdateImage(); } } } void UpdateImage() { if(bitmaps[currentIndex] == nullptr){ //imageCtrlに表示されている画像を削除 imageCtrl->SetBitmap(wxNullBitmap); } else { imageCtrl->SetBitmap(*bitmaps[currentIndex]); } Layout(); // レイアウトを更新 } };
class MyApp : public wxApp { public: virtual bool OnInit() { // windowsのコンソールでUTF-8を表示するために必要 // ロケール設定 std::locale::global(std::locale("ja_JP.UTF-8")); std::cerr.imbue(std::locale("ja_JP.UTF-8")); std::cout.imbue(std::locale("ja_JP.UTF-8")); SetConsoleOutputCP(CP_UTF8); MyFrame* frame = new MyFrame("Bitmap Viewer", wxPoint(50, 50), wxSize(450, 340)); frame->Show(true); return true; } }; wxIMPLEMENT_APP(MyApp);
画像フォルダ内のデータが読み込まれるので、左右キーで表示切替ができる。
スレッドプールのコードで、スレッドプールそのものを破棄するときに、未処理のキューをすべて削除して即終了できるように修正。
#pragma once #include <deque> #include <vector> #include <thread> #include <mutex> #include <condition_variable> #include <cassert> //! @brief スレッドに行わせる仕事のインターフェース //! @details run()メソッドを実装し、処理内容を記述する \n //! 使用例: //! @code //! class MyWork : public JobBase { //! public: //! void run() override {} //! }; //! @endcode class JobBase { public: virtual void run() = 0; virtual ~JobBase() {} }; //! @brief ジョブをためるキュー class Queue { public: Queue() {} void push(std::unique_ptr<JobBase>&& data) { _deque.emplace_back(std::move(data)); } std::unique_ptr<JobBase> pop() { if (_deque.empty()) { return nullptr; } auto data = std::move(_deque.front()); _deque.pop_front(); return data; } bool empty() const { return _deque.empty(); } void clear() { _deque.clear(); // 未処理のジョブを全削除 } private: // ジョブがたまっているキュー // ThreadPoolで作成したN個のThreadWorkerスレッドが、このキューからジョブを取り出して実行する std::deque<std::unique_ptr<JobBase>> _deque; }; //! スレッドプールで動いているスレッドの実装 class ThreadWorker { public: ThreadWorker(bool& isTerminationRequested, Queue& queue, std::mutex& mutex, std::condition_variable& cv, int& activeJobCount, std::condition_variable& activeJobCv) : _isTerminationRequested(isTerminationRequested), _queue(queue), _mutex(mutex), _cv(cv), _activeJobCount(activeJobCount), _activeJobCv(activeJobCv) {} void operator()() { while (1) { std::unique_ptr<JobBase> jobinstance; { std::unique_lock<std::mutex> ul(_mutex); while (_queue.empty()) { if (_isTerminationRequested) { return; } _cv.wait(ul); } jobinstance = _queue.pop(); assert(jobinstance != nullptr); _activeJobCount++; } jobinstance->run(); // ジョブを実行 { std::unique_lock<std::mutex> ul(_mutex); _activeJobCount--; // 実行中ジョブ数を減らす if (_activeJobCount == 0 && _queue.empty()) { _activeJobCv.notify_all(); // すべてのジョブが完了したことを通知 } } } } private: bool& _isTerminationRequested; Queue& _queue; std::mutex& _mutex; std::condition_variable& _cv; int& _activeJobCount; // 実行中ジョブ数 std::condition_variable& _activeJobCv; // waitForCompletion() へ通知 }; //! @brief スレッドプールクラス //! @details N個のスレッドで、M個のジョブを実行する。\n //! waitForCompletion()で、現在キューを入れたすべてのジョブが完了するまで待機できる \n //! 使用例: //! @code //! ThreadPool pool(4); //! pool.add(std::make_unique<MyWork>()); //! pool.add(std::make_unique<MyWork>()); //! pool.waitForCompletion(); //! pool.add(std::make_unique<MyWork>()); //! pool.add(std::make_unique<MyWork>()); //! @endcode class ThreadPool { public: ThreadPool(int threadCount) : _isTerminationRequested(false), _activeJobCount(0) { for (int n = 0; n < threadCount; n++) { auto worker = std::make_shared<ThreadWorker>(_isTerminationRequested, _queue, _mutex, _cv, _activeJobCount, _activeJobCv); _workers.push_back(worker); _threads.emplace_back(std::thread(std::ref(*worker))); } } ~ThreadPool() { { std::unique_lock<std::mutex> ul(_mutex); _isTerminationRequested = true; _queue.clear(); // 未処理のジョブを全削除し中断 } _cv.notify_all(); for (auto& thread : _threads) { if (thread.joinable()) { thread.join(); // スレッドを安全に終了 } } } //! @brief すべてのジョブが完了するまで待機する void waitForCompletion() { std::unique_lock<std::mutex> ul(_mutex); _activeJobCv.wait(ul, [this]() { return _activeJobCount == 0 && _queue.empty(); }); } //! @brief すべてのジョブが完了したかを確認する関数 bool isAllJobCompleted() { std::unique_lock<std::mutex> ul(_mutex); return _activeJobCount == 0 && _queue.empty(); } //! @brief ジョブをキューに追加 //! @param jobinstance ジョブ void add(std::unique_ptr<JobBase>&& jobinstance) { { std::unique_lock<std::mutex> ul(_mutex); _queue.push(std::move(jobinstance)); } _cv.notify_all(); } private: bool _isTerminationRequested; Queue _queue; std::mutex _mutex; std::condition_variable _cv; std::vector<std::thread> _threads; std::vector<std::shared_ptr<ThreadWorker>> _workers; int _activeJobCount; // 実行中ジョブ数 std::condition_variable _activeJobCv; // ジョブの完了を待機するための条件変数 };
pythonはデフォルトでsqliteを使えるらしい。
import sqlite3 # データベースファイルに接続(なければ自動的に作成される) conn = sqlite3.connect('test.db') cursor = conn.cursor() # テーブル作成 cursor.execute(''' CREATE TABLE IF NOT EXISTS ingredients ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, quantity INTEGER NOT NULL, unit TEXT NOT NULL, expiration_date TEXT ) ''') # ダミーデータ挿入 sample_data = [ ("たまねぎ", 10, "個", "2025-05-01"), ("にんじん", 5, "本", "2025-04-15"), ("じゃがいも", 20, "個", "2025-04-20"), ("牛乳", 2, "本", "2025-04-11"), ("卵", 12, "個", "2025-04-18"), ("鶏むね肉", 3, "枚", "2025-04-13"), ("小麦粉", 1, "袋", "2025-10-01"), ("砂糖", 1, "袋", "2026-01-01"), ("塩", 1, "袋", None), ] cursor.executemany(''' INSERT INTO ingredients (name, quantity, unit, expiration_date) VALUES (?, ?, ?, ?) ''', sample_data) # コミットして保存 conn.commit() # データ確認 cursor.execute("SELECT * FROM ingredients") for row in cursor.fetchall(): print(row) # 接続を閉じる conn.close()
DB Browser for SQLite
FreeType-rsは内部でビルド済みのFreeTypeを呼び出しているので、ビルド済みFreeTypeを用意する必要がある。加えて、pkg-configが必要。
以下でfreetype2を導入。
これにより、freetype2の関連ライブラリと一緒に、.pcファイル(pkg-configに必要)が生成される。
以下よりpkg-config-liteを導入し、パスを通す。
https://sourceforge.net/projects/pkgconfiglite/
環境変数PKG_CONFIG_PATHを追加。これでcargoが.pcファイルを見つけられるようになる。
PKG_CONFIG_PATH=C:\app\vcpkg\installed\x64-windows\lib\pkgconfig
RustRoverであれば、実行/デバッグ構成から環境変数を設定できる。
use freetype::Library; // image = "0.25.6" を追加 use image::{GrayImage, Luma}; fn main() -> Result<(), Box<dyn std::error::Error>> { // FreeTypeライブラリ初期化 let lib = Library::init()?; // フォント読み込み let face = lib.new_face("C:/Windows/Fonts/arial.ttf", 0)?; face.set_char_size(40 * 64, 0, 300, 0)?; // 40pt, 300dpi // 描画する文字 face.load_char('A' as usize, freetype::face::LoadFlag::RENDER)?; let glyph = face.glyph(); let bitmap = glyph.bitmap(); let width = bitmap.width() as u32; let rows = bitmap.rows() as u32; // グレースケール画像作成 let mut img = GrayImage::new(width, rows); for y in 0..rows { for x in 0..width { let value = bitmap.buffer()[(y * width + x) as usize]; img.put_pixel(x, y, Luma([value])); } } img.save("glyph.png")?;// 出力 Ok(()) }
Wordを使って数式を埋め込んだ結果、以下のように数式の上下が見切れてしまう事案が発生。
これは行の高さが固定になっていることが原因で、行間のオプション→行間で、固定値以外を指定する。
中国産のLLM、Qwen3。
今使っているのが NVIDIA GeForce RTX 3070 Ti 。
パラメータ数1Bあたり約1GBのVRAMが必要らしいので、 Qwen3-8B が使える。
https://www.linkedin.com/pulse/qwen3-self-hosting-guide-vllm-sglang-maksym-huczynski-i4v2f?utm_source=chatgpt.com
from langchain_ollama import OllamaLLM import re llm = OllamaLLM(model="qwen3:8b") response = llm.invoke("こんにちは") response_without_think = re.sub(r"<think>.*?</think>", "", response, flags=re.DOTALL) print(response_without_think)
結構理想的な答えが返ってきたので、ユーザーの指示通りにPCを操作する超簡易的スクリプトを組んでみた。流石に重要なフォルダを削除などされると困るので、スクリプトを確認してから動作させる。
from langchain_ollama import OllamaEmbeddings from langchain_community.vectorstores import FAISS from langchain_community.document_loaders import TextLoader from langchain_ollama import OllamaLLM from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.chains import RetrievalQA import re import subprocess
def GetPythonCodeLLM( llm,shiji ): response = llm.invoke("あなたはWindows上で動作するPythonコード生成器です。次の指示を実行可能なPythonコードを作成せよ。出力にPythonコード以外の内容を一切含めてはならない。コードはMarkdownのコードブロックで囲むこと。以下指示:"+ shiji ) # <think>...</think> を削除 response_without_think = re.sub(r"<think>.*?</think>", "", response, flags=re.DOTALL) return response_without_think
def extract_python_code_blocks(text): """ 与えられた文字列から、 ```python ~ ``` で囲まれた範囲のコードをリストで返す。 """ pattern = r"```python\s*(.*?)```" match = re.search(pattern, text, re.DOTALL) return match.group(1) if match else None
llm = OllamaLLM(model="qwen3:8b") instructions = input("指示:").strip().lower() response = GetPythonCodeLLM(llm,instructions) code = extract_python_code_blocks(response) print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") print(code) print("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") confirmation = input("処理を続行しますか? [Y/n]: ").strip().lower() if confirmation in ['', 'y', 'yes']: with open('job.py', 'w') as file: file.write(code) subprocess.run(["python", "job.py"]) else: print("処理を中止しました。")
指示:C:\test2 内のファイル一覧を表示してください
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
import os
path = r'C:\test2'
for item in os.listdir(path):
item_path = os.path.join(path, item)
if os.path.isfile(item_path):
print(item)
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
処理を続行しますか? [Y/n]: Y
freetypetest.pbm
local.html
meiryob.ttc
s01.gif
時々、リストをそのまま出力することがある。
指示:C:\test2 内のファイル一覧を表示してください
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
import os
print(os.listdir(r'C:\test2'))
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
処理を続行しますか? [Y/n]: Y
['freetypetest.pbm', 'local.html', 'meiryob.ttc', 's01.gif']