📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)
| (同じ利用者による、間の11版が非表示) | |||
| 1行目: | 1行目: | ||
== 概要 == | == 概要 == | ||
Qtにおける画像処理は、主に<code>QImage</code>クラスを中心に行われる。<br> | |||
このクラスは、画像データの読み込み、保存、操作などの基本的な機能を提供している。<br> | |||
<br> | <br> | ||
その他、Qtでは画像を取り扱うクラスとして、<code>QBitmap</code>クラス、<code>QPixmap</code>クラスもあり、それぞれ使い分けが必要である。<br> | |||
<br> | |||
画像の読み込みと保存は、<code>QImage</code>クラスの<code>load</code>メソッドと<code>save</code>メソッドを使用する。<br> | |||
これにより、JPEG、PNG、GIF等の様々な形式の画像ファイルを扱うことができる。<br> | |||
<br> | |||
ピクセル単位の操作も可能であり、個々のピクセルの色情報を取得・変更することができる。<br> | |||
これは、画像の細かい編集や分析に役立つ。<br> | |||
<br> | |||
基本的な画像変換機能も充実しており、<br> | |||
例えば、画像のサイズ変更、回転、反転等を簡単に行うことができる。<br> | |||
これらの操作は、QImageクラスのメソッドを使用するだけで実現できる。<br> | |||
<br> | |||
より高度な処理が必要な場合、<code>QPainter</code>クラスを使用してQImageクラス上に直接描画することができる。<br> | |||
これにより、画像に図形やテキストを追加、あるいは、複雑な視覚効果を適用したりすることが可能である。<br> | |||
<br> | |||
また、<code>QImageReader</code>クラスを使用することにより、画像の読み込み時にフィルタを適用することもできる。<br> | |||
これは、画像の前処理や特定の効果を適用する場合に便利である。<br> | |||
<br> | |||
しかし、非常に複雑な画像処理や高度な機能が必要な場合は、OpenCV等の専門的な外部ライブラリとQtを組み合わせて使用することも一般的である。<br> | |||
これにより、Qtの使いやすいGUI機能とOpenCVの強力な画像処理アルゴリズムを組み合わせることができる。<br> | |||
<br> | |||
Qtでの画像処理は、これらの機能を組み合わせることにより、基本的な画像編集から複雑な画像分析まで幅広いタスクに対応できる。<br> | |||
<br><br> | |||
== QImageクラス / QPixmapクラス / QBitmapクラス == | |||
QImageクラス、QBitmapクラス、QPixmapクラスの3つのクラスは、Qtで画像を扱う時に使用される重要なクラスである。<br> | |||
それぞれのクラスは異なる用途と特徴を持つ。<br> | |||
<br> | |||
==== QImageクラス ==== | |||
QImageクラスは、ピクセルデータの直接操作に適している。<br> | |||
主な特徴として、デバイスに依存しない画像表現を提供しており、個々のピクセルへのアクセスや操作が容易である。<br> | |||
<br> | |||
画像処理、フィルタリング、色変換等の操作に最適である。<br> | |||
また、様々な色深度と形式 (1-bit、8-bit、32-bit等) をサポートしている。<br> | |||
<br> | |||
QImageクラスは、画像ファイルの読み込み、保存、画像データの詳細な操作が必要な場合に使用する。<br> | |||
<br> | |||
==== QPixmapクラス ==== | |||
QPixmapクラスは、画面表示に最適化された画像表現を提供している。<br> | |||
このクラスは、ハードウェアアクセラレーションを利用できるため、GUIアプリケーションでの画像表示に非常に効率的である。<br> | |||
<br> | |||
QPixmapクラスは、デバイスに依存する形式で画像を保持するため、画面への描画が高速である。<br> | |||
ただし、個々のピクセルへのアクセスはQImageクラスほど容易ではない。<br> | |||
<br> | |||
QPixmapクラスは、主にボタンのアイコンやウインドウの背景等、UIコンポーネントでの画像表示に使用する。<br> | |||
<br> | |||
==== QBitmapクラス ==== | |||
QBitmapクラスは、QPixmapクラスの特殊なケースであり、1ビット深度 (モノクロ) の画像を表現している。<br> | |||
各ピクセルは、オン / オフの2つの状態のみを持つ。<br> | |||
<br> | |||
QBitmapクラスは、主にマスク、カーソル、アイコン等の単純な画像要素の作成に使用する。<br> | |||
<br> | |||
メモリ使用量が少なく、単純な形状やパターンの表現に適している。<br> | |||
<br> | |||
==== クラスの使い分け ==== | |||
* QImageクラス | |||
*: 画像処理や詳細な編集が必要な場合に使用する。 | |||
* QPixmapクラス | |||
*: GUIでの高速な画像表示が必要な場合に使用する。 | |||
* QBitmapクラス | |||
*: モノクロの単純な画像やマスクが必要な場合に使用する。 | |||
<br> | |||
また、これらのクラス間では相互変換が可能である。<br> | |||
例えば、QImageクラスで画像を処理した後、QPixmapクラスに変換して画面に表示するといった使い方ができる。<br> | |||
<br> | |||
これらのクラスを使い分けることにより、効率的で柔軟な画像処理と表示が可能になる。<br> | |||
<br><br> | |||
== QImageクラスの使用例 == | |||
==== 画像の拡大 ==== | |||
以下の例では、QImageクラスを使用して、画像を拡大している。<br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
#include <QImage> | |||
#include <QSize> | |||
#include <QFileInfo> | |||
int main() | |||
{ | |||
// 入力ファイルの存在確認 | |||
if (!QFileInfo::exists("input.jpg")) return -1; | |||
// 画像の読み込み | |||
QImage image("input.jpg"); | |||
if (image.isNull()) return -1; | |||
// 画像の拡大 | |||
QImage resizedImage = image.scaled(QSize(800, 600), Qt::KeepAspectRatio, Qt::SmoothTransformation); | |||
// 拡大した画像の保存 | |||
if (!resizedImage.save("output.jpg")) return -1; | |||
return 0; | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
==== 画像の縮小 ==== | |||
以下の例では、QImageクラスを使用して、指定された最大サイズ (例: 400x300ピクセル) に収まるように縮小する。<br> | |||
元の画像が既このサイズ以下の場合は、縮小処理は行わない。<br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
#include <QImage> | |||
#include <QSize> | |||
#include <QFileInfo> | |||
QImage shrinkImage(QImage &image, int maxWidth, int maxHeight) | |||
{ | |||
QSize originalSize = image.size(); | |||
QSize newSize = originalSize; | |||
// アスペクト比を維持しながら、指定された最大サイズに収まるように縮小 | |||
if (originalSize.width() > maxWidth || originalSize.height() > maxHeight) { | |||
newSize.scale(maxWidth, maxHeight, Qt::KeepAspectRatio); | |||
} | |||
// 元のサイズより小さい場合のみ縮小を行う | |||
// 縮小の必要がない場合は元の画像をそのまま | |||
if (newSize != originalSize) { | |||
return image.scaled(newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); | |||
} | |||
else { | |||
return image; | |||
} | |||
} | |||
int main() | |||
{ | |||
// 入力ファイルの存在確認 | |||
if (!QFileInfo::exists("input.jpg")) return -1; | |||
// 画像の読み込み | |||
QImage image("input.jpg"); | |||
if (image.isNull()) return -1; | |||
// 画像の縮小 | |||
QImage resizedImage = shrinkImage(image, 400, 300) | |||
// 縮小した画像の保存 | |||
if (!resizedImage.save("output.jpg")) return -1; | |||
return 0; | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
==== 画像の回転 ==== | |||
以下の例では、QImageクラスを使用して、画像を回転している。<br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
#include <QApplication> | |||
#include <QImage> | |||
#include <QTransform> | |||
#include <QFileDialog> | |||
int main(int argc, char *argv[]) | |||
{ | |||
QApplication app(argc, argv); | |||
// 画像を読み込む | |||
QImage image("input.jpg"); | |||
if (image.isNull()) return -1; | |||
// 回転角度を指定 (例: 90度) | |||
double angle = 90.0; | |||
// QTransformクラスを使用して画像を回転 | |||
QTransform transform; | |||
transform.rotate(angle); | |||
QImage rotatedImage = image.transformed(transform, Qt::SmoothTransformation); | |||
if (rotatedImage.isNull()) return -1; | |||
// 回転した画像を保存 | |||
if (!rotatedImage.save("output.jpg")) return -1; | |||
return 0; | |||
} | |||
</syntaxhighlight> | |||
<br><br> | |||
== QPixmapクラスの使用例 == | |||
==== 画像ビューアウィジェット ==== | |||
以下の例では、画像ビューアウィジェットのクラスを定義している。<br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// ImageViewer.hファイル | |||
#include <QWidget> | |||
#include <QPixmap> | |||
#include <QLabel> | |||
#include <QVBoxLayout> | |||
#include <QPushButton> | |||
#include <QFileDialog> | |||
#include <QMessageBox> | |||
class ImageViewer : public QWidget | |||
{ | |||
Q_OBJECT | |||
private: | |||
QLabel *imageLabel; // 画像表示用ラベル | |||
QPushButton *loadButton; // 画像読み込みボタン | |||
public: | |||
// コンストラクタ: ウィジェットの初期化 | |||
ImageViewer(QWidget *parent = nullptr) : QWidget(parent) | |||
{ | |||
// 画像を表示するためのQLabel | |||
imageLabel = new QLabel(this); | |||
imageLabel->setAlignment(Qt::AlignCenter); | |||
// 画像読み込みボタン | |||
loadButton = new QPushButton("画像を読み込む", this); | |||
// ボタンクリック時にloadImage()スロットを呼び出す | |||
connect(loadButton, &QPushButton::clicked, this, &ImageViewer::loadImage); | |||
// レイアウトの設定 | |||
QVBoxLayout *layout = new QVBoxLayout(this); | |||
layout->addWidget(imageLabel); | |||
layout->addWidget(loadButton); | |||
setLayout(layout); | |||
} | |||
private slots: | |||
// 画像読み込み処理を行うスロット | |||
void loadImage() | |||
{ | |||
// ファイル選択ダイアログを表示 | |||
QString fileName = QFileDialog::getOpenFileName(this, "画像を開く", "", "画像ファイル (*.png *.jpg *.bmp)"); | |||
if (!fileName.isEmpty()) { | |||
// 選択されたファイルからQPixmapを作成 | |||
QPixmap pixmap(fileName); | |||
if (pixmap.isNull()) { | |||
// 画像読み込みに失敗した場合はエラーメッセージを表示 | |||
QMessageBox::critical(this, "エラー", "画像の読み込みに失敗"); | |||
return; | |||
} | |||
// 読み込んだ画像をラベルに表示 (アスペクト比を保持してリサイズ) | |||
imageLabel->setPixmap(pixmap.scaled(imageLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); | |||
} | |||
} | |||
}; | |||
</syntaxhighlight> | |||
<br> | |||
==== アニメーションスプライトシート ==== | |||
以下の例では、スプライトシートを使用したアニメーションクラスを定義している。<br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// AnimatedSprite.hファイル | |||
#include <QWidget> | |||
#include <QPixmap> | |||
#include <QTimer> | |||
#include <QPainter> | |||
class AnimatedSprite : public QWidget | |||
{ | |||
Q_OBJECT | |||
private: | |||
QPixmap spriteSheet; // スプライトシート | |||
QTimer *timer; // アニメーション用タイマ | |||
int currentFrame; // 現在のフレーム番号 | |||
int frameWidth; // 1フレームの幅 | |||
int frameHeight; // 1フレームの高さ | |||
protected: | |||
// ペイントイベント: 現在のフレームを描画 | |||
void paintEvent(QPaintEvent *event) override | |||
{ | |||
Q_UNUSED(event); | |||
QPainter painter(this); | |||
// スプライトシートから現在のフレームを切り出して描画 | |||
painter.drawPixmap(0, 0, spriteSheet, currentFrame * frameWidth, 0, frameWidth, frameHeight); | |||
} | |||
public: | |||
// コンストラクタ: スプライトシートの読み込みとタイマの設定 | |||
AnimatedSprite(QWidget *parent = nullptr) : QWidget(parent), currentFrame(0) | |||
{ | |||
// スプライトシートの読み込み | |||
spriteSheet.load(":/sprites/character.png"); // スプライトシートのリソースパス | |||
if (spriteSheet.isNull()) { | |||
qWarning() << "スプライトシートの読み込みに失敗"; | |||
return; | |||
} | |||
// スプライトシートの各フレームのサイズを計算 | |||
frameWidth = spriteSheet.width() / 4; // 4フレームあると仮定 | |||
frameHeight = spriteSheet.height(); | |||
// アニメーション用タイマの設定 | |||
timer = new QTimer(this); | |||
connect(timer, &QTimer::timeout, this, &AnimatedSprite::nextFrame); | |||
timer->start(100); // 100[mS]ごとにフレーム更新 | |||
// ウィジェットのサイズを1フレームのサイズに固定 | |||
setFixedSize(frameWidth, frameHeight); | |||
} | |||
private slots: | |||
// 次のフレームに進むスロット | |||
void nextFrame() | |||
{ | |||
currentFrame = (currentFrame + 1) % 4; // 4フレームを循環 | |||
update(); // ウィジェットの再描画をリクエスト | |||
} | |||
}; | |||
</syntaxhighlight> | |||
<br> | |||
==== 非同期で画像を読み込む ==== | |||
以下の例では、大きなサイズの画像を非同期で読み込むウィジェットを定義している。<br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// AsyncImageLoader.hファイル | |||
#include <QWidget> | |||
#include <QPixmap> | |||
#include <QLabel> | |||
#include <QPushButton> | |||
#include <QVBoxLayout> | |||
#include <QFuture> | |||
#include <QtConcurrent> | |||
#include <QFileDialog> | |||
#include <QMessageBox> | |||
class AsyncImageLoader : public QWidget | |||
{ | |||
Q_OBJECT | |||
private: | |||
QLabel *imageLabel; // 画像表示用ラベル | |||
QPushButton *loadButton; // 画像読み込みボタン | |||
public: | |||
// コンストラクタ: ウィジェットの初期化 | |||
AsyncImageLoader(QWidget *parent = nullptr) : QWidget(parent) | |||
{ | |||
// 画像表示用ラベル | |||
imageLabel = new QLabel(this); | |||
imageLabel->setAlignment(Qt::AlignCenter); | |||
// 画像読み込みボタン | |||
loadButton = new QPushButton("大きな画像を読み込む", this); | |||
connect(loadButton, &QPushButton::clicked, this, &AsyncImageLoader::loadImageAsync); | |||
// レイアウトの設定 | |||
QVBoxLayout *layout = new QVBoxLayout(this); | |||
layout->addWidget(imageLabel); | |||
layout->addWidget(loadButton); | |||
setLayout(layout); | |||
} | |||
private slots: | |||
// 非同期で画像を読み込むスロット | |||
void loadImageAsync() | |||
{ | |||
// ファイル選択ダイアログを表示 | |||
QString fileName = QFileDialog::getOpenFileName(this, "大きな画像を開く", "", "画像ファイル (*.png *.jpg *.bmp)"); | |||
if (!fileName.isEmpty()) { | |||
// UIの更新(ボタンを無効化し、テキストを変更) | |||
loadButton->setEnabled(false); | |||
loadButton->setText("読み込み中..."); | |||
// 非同期で画像を読み込む | |||
QFuture<QPixmap> future = QtConcurrent::run([fileName]() { | |||
return QPixmap(fileName); | |||
}); | |||
// 非同期処理の完了を監視するためのQFutureWatcherを設定 | |||
QFutureWatcher<QPixmap> *watcher = new QFutureWatcher<QPixmap>(this); | |||
connect(watcher, &QFutureWatcher<QPixmap>::finished, this, [this, watcher]() { | |||
QPixmap pixmap = watcher->result(); | |||
if (pixmap.isNull()) { | |||
// 画像読み込みに失敗した場合はエラーメッセージを表示 | |||
QMessageBox::critical(this, "エラー", "画像の読み込みに失敗"); | |||
} | |||
else { | |||
// 読み込んだ画像をラベルに表示 (アスペクト比を保持してリサイズ) | |||
imageLabel->setPixmap(pixmap.scaled(imageLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); | |||
} | |||
// UIの更新 (ボタンを再度有効化して、元のテキストに戻す) | |||
loadButton->setEnabled(true); | |||
loadButton->setText("大きな画像を読み込む"); | |||
// watcherのメモリを解放 | |||
watcher->deleteLater(); | |||
}); | |||
// 非同期処理の監視を開始 | |||
watcher->setFuture(future); | |||
} | |||
} | |||
}; | |||
</syntaxhighlight> | |||
<br><br> | <br><br> | ||
== | == QBitmapクラスの使用例 == | ||
==== 描画ツール ==== | |||
以下の例では、1ビット深度の描画ツールクラスを定義している。<br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// SimpleDrawingTool.hファイル | |||
#include <QWidget> | |||
#include <QBitmap> | |||
#include <QPainter> | |||
#include <QMouseEvent> | |||
#include <QPushButton> | |||
#include <QVBoxLayout> | |||
#include <QLabel> | |||
#include <QMessageBox> | |||
class SimpleDraingTool : public QWidget | |||
{ | |||
Q_OBJECT | |||
private: | |||
QLabel *drawingLabel; // 描画エリア用ラベル | |||
QPushButton *clearButton; // クリアボタン | |||
QBitmap bitmap; // 描画用ビットマップ | |||
bool drawing; // 描画中フラグ | |||
// 描画用ビットマップを初期化する関数 | |||
void initializeBitmap() | |||
{ | |||
bitmap = QBitmap(300, 300); | |||
bitmap.fill(Qt::color0); // 背景を黒で初期化 | |||
updateLabel(); | |||
} | |||
// 指定された位置に点を描画する関数 | |||
void drawPoint(const QPoint &pos) | |||
{ | |||
QPainter painter(&bitmap); | |||
painter.setPen(Qt::color1); // 白で描画 | |||
painter.drawPoint(pos); | |||
updateLabel(); | |||
} | |||
// ラベルの表示を更新する関数 | |||
void updateLabel() | |||
{ | |||
drawingLabel->setPixmap(QPixmap::fromImage(bitmap.toImage())); | |||
} | |||
public: | |||
// コンストラクタ: ウィジェットの初期化 | |||
SimpleDraingTool(QWidget *parent = nullptr) : QWidget(parent), drawing(false) | |||
{ | |||
// 描画エリア用ラベル | |||
drawingLabel = new QLabel(this); | |||
drawingLabel->setAlignment(Qt::AlignCenter); | |||
// クリアボタン | |||
clearButton = new QPushButton("クリア", this); | |||
connect(clearButton, &QPushButton::clicked, this, &SimpleDraingTool::clearDrawing); | |||
// レイアウトの設定 | |||
QVBoxLayout *layout = new QVBoxLayout(this); | |||
layout->addWidget(drawingLabel); | |||
layout->addWidget(clearButton); | |||
setLayout(layout); | |||
// 描画用ビットマップの初期化 | |||
initializeBitmap(); | |||
} | |||
protected: | |||
// マウスボタンが押下された時のイベントハンドラ | |||
void mousePressEvent(QMouseEvent *event) override | |||
{ | |||
if (event->button() == Qt::LeftButton) { | |||
drawing = true; | |||
drawPoint(event->pos()); | |||
} | |||
} | |||
// マウスが移動した時のイベントハンドラ | |||
void mouseMoveEvent(QMouseEvent *event) override | |||
{ | |||
if ((event->buttons() & Qt::LeftButton) && drawing) { | |||
drawPoint(event->pos()); | |||
} | |||
} | |||
// マウスボタンが離された時のイベントハンドラ | |||
void mouseReleaseEvent(QMouseEvent *event) override | |||
{ | |||
if (event->button() == Qt::LeftButton && drawing) { | |||
drawing = false; | |||
} | |||
} | |||
private slots: | |||
// 描画をクリアするスロット | |||
void clearDrawing() | |||
{ | |||
try { | |||
initializeBitmap(); | |||
updateLabel(); | |||
} | |||
catch (const std::exception& e) { | |||
QMessageBox::critical(this, "エラー", QString("描画のクリアに失敗: %1").arg(e.what())); | |||
} | |||
} | |||
}; | |||
</syntaxhighlight> | |||
<br> | |||
==== カスタムカーソル作成器 ==== | |||
以下の例では、カスタムカーソルを作成および適用するウィジェットクラスを定義している。<br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// CustomCursorCreator.hファイル | |||
#include <QWidget> | |||
#include <QBitmap> | |||
#include <QPainter> | |||
#include <QPushButton> | |||
#include <QVBoxLayout> | |||
#include <QCursor> | |||
#include <QMessageBox> | |||
class CustomCursorCreator : public QWidget | |||
{ | |||
Q_OBJECT | |||
private: | |||
QPushButton *createButton; // カーソル作成ボタン | |||
public: | |||
// コンストラクタ: ウィジェットの初期化 | |||
CustomCursorCreator(QWidget *parent = nullptr) : QWidget(parent) | |||
{ | |||
// カーソル作成ボタン | |||
createButton = new QPushButton("カスタムカーソルを作成", this); | |||
connect(createButton, &QPushButton::clicked, this, &CustomCursorCreator::createAndApplyCursor); | |||
// レイアウトの設定 | |||
QVBoxLayout *layout = new QVBoxLayout(this); | |||
layout->addWidget(createButton); | |||
setLayout(layout); | |||
} | |||
private slots: | |||
// カスタムカーソルを作成・適用するスロット | |||
void createAndApplyCursor() | |||
{ | |||
try { | |||
// 32x32ピクセルのビットマップを作成 | |||
QBitmap bitmap(32, 32); | |||
bitmap.fill(Qt::color0); // 透明で初期化 | |||
// ビットマップ上に描画 | |||
QPainter painter(&bitmap); | |||
painter.setPen(Qt::color1); // 不透明な色で描画 | |||
// 簡単な十字型のカーソルを描画 | |||
painter.drawLine(16, 0, 16, 31); // 縦線 | |||
painter.drawLine(0, 16, 31, 16); // 横線 | |||
// マスクを作成 (カーソルの形状を定義) | |||
QBitmap mask = bitmap; | |||
// カーソルを作成・適用 | |||
QCursor cursor(bitmap, mask, 16, 16); // ホットスポットを中心に設定 | |||
this->setCursor(cursor); | |||
QMessageBox::information(this, "成功", "カスタムカーソルを作成・適用"); | |||
} | |||
catch (const std::exception& e) { | |||
QMessageBox::critical(this, "エラー", QString("カーソルの作成に失敗: %1").arg(e.what())); | |||
} | |||
} | |||
}; | |||
</syntaxhighlight> | |||
<br> | |||
==== モノクロパターン生成器 ==== | |||
以下の例では、ランダムなモノクロパターンを生成し表示するウィジェットクラスを定義している。<br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// MonochromePatternGenerator.hファイル | |||
#include <QWidget> | |||
#include <QBitmap> | |||
#include <QPainter> | |||
#include <QPushButton> | |||
#include <QVBoxLayout> | |||
#include <QLabel> | |||
#include <QRandomGenerator> | |||
#include <QMessageBox> | |||
class MonochromePatternGenerator : public QWidget | |||
{ | |||
Q_OBJECT | |||
private: | |||
QLabel *patternLabel; // パターン表示用ラベル | |||
QPushButton *generateButton; // パターン生成ボタン | |||
public: | |||
// コンストラクタ: ウィジェットの初期化を行います | |||
MonochromePatternGenerator(QWidget *parent = nullptr) : QWidget(parent) | |||
{ | |||
// パターン表示用ラベル | |||
patternLabel = new QLabel(this); | |||
patternLabel->setAlignment(Qt::AlignCenter); | |||
// パターン生成ボタン | |||
generateButton = new QPushButton("新しいパターンを生成", this); | |||
connect(generateButton, &QPushButton::clicked, this, &MonochromePatternGenerator::generatePattern); | |||
// レイアウトの設定 | |||
QVBoxLayout *layout = new QVBoxLayout(this); | |||
layout->addWidget(patternLabel); | |||
layout->addWidget(generateButton); | |||
setLayout(layout); | |||
// 初期パターンを生成 | |||
generatePattern(); | |||
} | |||
private slots: | |||
// ランダムなモノクロパターンを生成するスロット | |||
void generatePattern() | |||
{ | |||
try { | |||
// 100x100ピクセルのビットマップを作成 | |||
QBitmap bitmap(100, 100); | |||
bitmap.fill(Qt::color0); // 黒で初期化 | |||
QPainter painter(&bitmap); | |||
painter.setPen(Qt::color1); // 白で描画 | |||
// ランダムなパターンを生成 | |||
for (int y = 0; y < 100; y++) { | |||
for (int x = 0; x < 100; x++) { | |||
if (QRandomGenerator::global()->generateDouble() > 0.5) { | |||
painter.drawPoint(x, y); | |||
} | |||
} | |||
} | |||
// パターンをラベルに表示 | |||
patternLabel->setPixmap(QPixmap::fromImage(bitmap.toImage().scaled(200, 200, Qt::KeepAspectRatio, Qt::FastTransformation))); | |||
} | |||
catch (const std::exception& e) { | |||
QMessageBox::critical(this, "エラー", QString("パターンの生成に失敗: %1").arg(e.what())); | |||
} | |||
} | |||
}; | |||
</syntaxhighlight> | |||
<br><br> | |||
== QGraphicsViewクラスの使用 == | |||
画像を表示する機能を持っているウィジェットは無いため、ビュークラスを継承して派生クラスを作成する。<br> | 画像を表示する機能を持っているウィジェットは無いため、ビュークラスを継承して派生クラスを作成する。<br> | ||
( | (元々、<code>QGraphicsView</code>クラスは、<code>QGraphicsScene</code>クラスを表示するためのものである)<br> | ||
<br> | <br> | ||
ビュークラスを継承した派生クラスには、元の画像のデータを保持するメンバ変数を定義する。<br> | ビュークラスを継承した派生クラスには、元の画像のデータを保持するメンバ変数を定義する。<br> | ||
| 31行目: | 686行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
<code>QGraphicsView</code>クラスの<code>paintEvent</code>メソッドは、再描画時に呼び出される仮想関数である。<br> | |||
<code>paintEvent</code>メソッドをオーバーライドして描画部分を記述する。<br> | |||
<br> | <br> | ||
paintEventメソッドとsetImgメソッドの定義は、以下の通りである。<br> | paintEventメソッドとsetImgメソッドの定義は、以下の通りである。<br> | ||
<br> | <br> | ||
以下の例では、paintEventメソッドを使用して<code>QImage</code>クラスのインスタンス(描画用)を生成して、<code>QPainter::drawImage</code>メソッドで描画している。<br> | |||
描画用の画像は、ウィジェットのサイズに合わせてリサイズしている。<br> | 描画用の画像は、ウィジェットのサイズに合わせてリサイズしている。<br> | ||
<br> | <br> | ||
| 57行目: | 712行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
OpenCVライブラリの<code>cv::Mat</code>メソッドを使用する場合、OpenCVで画像を処理してその画像を表示するには、以下のように記述する。<br> | |||
カラー画像の場合は、色配列をRGBからBGRに変換する必要がある。<br> | <u>カラー画像の場合は、色配列をRGBからBGRに変換する必要がある。</u><br> | ||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
void MyGraphicsView::paintEvent(QPaintEvent *event) | void MyGraphicsView::paintEvent(QPaintEvent *event) | ||
| 65行目: | 720行目: | ||
widgetpainter.setWorldTransform(m_matrix); | widgetpainter.setWorldTransform(m_matrix); | ||
// | // メンバ変数m_imgはcv::Matクラスのインスタンス | ||
QImage qimg = QImage(m_img.ptr(), m_img.cols, m_img.rows, m_img.step, QImage::Format_RGB888); | QImage qimg = QImage(m_img.ptr(), m_img.cols, m_img.rows, m_img.step, QImage::Format_RGB888); | ||
qimg = qimg.scaled(viewport()->width(), viewport()->height(), Qt::KeepAspectRatio,Qt::FastTransformation); | qimg = qimg.scaled(viewport()->width(), viewport()->height(), Qt::KeepAspectRatio,Qt::FastTransformation); | ||
| 89行目: | 744行目: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
==== 画像の拡大・縮小・移動 ==== | |||
画像を表示する機能には、拡大・縮小、移動等の制御が必要になる場合が多い。<br> | |||
これらの機能を実装するには、<code>QTransform</code>クラスを使用する。<br> | |||
<br> | |||
まず、<code>QTransform</code>クラスのメンバ変数を2つ宣言する。<br> | |||
<code>QTransform</code>クラスのデフォルトコンストラクタを呼び出す場合、単位行列で初期化される。<br> | |||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
QTransform m_matrix; | QTransform m_matrix; | ||
| 102行目: | 756行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
次に、<code>QGraphicsView</code>クラスの<code>paintEvent</code>メソッドをオーバーライドする。<br> | |||
以下のように記述することで、<code>QTransform</code>クラスの情報が反映されるようになる。<br> | |||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
void MyGraphicsView::paintEvent(QPaintEvent *event) | void MyGraphicsView::paintEvent(QPaintEvent *event) | ||
| 116行目: | 770行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
拡大・縮小の機能は、以下の通りである。<br> | |||
<br> | |||
以下の例では、拡大・縮小の限界値を設定して、変換行列に拡大率を設定する。<br> | |||
既に拡大・縮小が適用されている場合のために、予め、ズームの中心を移動している。<br> | |||
<br> | <br> | ||
第1引数には、拡大する場合は正の値、縮小する場合は負の値を指定する。<br> | |||
第2引数には、拡大・縮小の中心となる点を指定する。<br> | |||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
void MyGraphicsView::scaleView(qreal factor, QPointF center) | void MyGraphicsView::scaleView(qreal factor, QPointF center) | ||
| 159行目: | 813行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
controlImagePositionメソッドは、拡大する時、余白が表示されないように画像位置を調整する。<br> | |||
controlImagePositionメソッドの最後で、viewport()->update();を実行して画面を更新する。<br> | controlImagePositionメソッドの最後で、viewport()->update();を実行して画面を更新する。<br> | ||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
| 166行目: | 820行目: | ||
qreal left, top, right, bottom; | qreal left, top, right, bottom; | ||
//after check top-left, bottom right corner to avoid getting "out" during zoom/panning | // after check top-left, bottom right corner to avoid getting "out" during zoom/panning | ||
m_matrix.map(0, 0, &left, &top); | |||
if(left > 0) | |||
{ | |||
m_matrix.translate(-left, 0); | |||
left = 0; | |||
} | |||
if(top > 0) | |||
{ | |||
m_matrix.translate(0, -top); | |||
top = 0; | |||
} | |||
QSize sizeImage = size(); | |||
m_matrix.map(sizeImage.width(), sizeImage.height(), &right, &bottom); | |||
if(right < sizeImage.width()) | |||
{ | |||
m_matrix.translate(sizeImage.width() - right, 0); | |||
right = sizeImage.width(); | |||
} | |||
if(bottom < sizeImage.height()) | |||
{ | |||
m_matrix.translate(0, sizeImage.height() - bottom); | |||
bottom = sizeImage.height(); | |||
} | |||
m_matrix_inv = m_matrix.inverted(); | |||
viewport()->update(); | viewport()->update(); | ||
| 203行目: | 857行目: | ||
<br> | <br> | ||
以下の例では、移動はマウスの左ボタンを押してドラッグする場合に適用している。<br> | 以下の例では、移動はマウスの左ボタンを押してドラッグする場合に適用している。<br> | ||
また、拡大している場合にのみ適用している。<br> | |||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
void MyGraphicsView::mouseMoveEvent(QMouseEvent *event) | void MyGraphicsView::mouseMoveEvent(QMouseEvent *event) | ||
| 230行目: | 884行目: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | |||
==== 画像のクリック位置の座標を逆算する ==== | |||
== 画像のクリック位置の座標を逆算する == | |||
画像上において、クリックする位置の座標を取得して、ピクセル値を取得する操作がある。<br> | 画像上において、クリックする位置の座標を取得して、ピクセル値を取得する操作がある。<br> | ||
<br> | <br> | ||
| 273行目: | 926行目: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | |||
==== 拡大・縮小時の画像の解像度を上げる ==== | |||
== 拡大・縮小時の画像の解像度を上げる == | |||
上記のセクション群では、画像を拡大・縮小する場合、画像の解像度はビューポートのサイズのままである。<br> | 上記のセクション群では、画像を拡大・縮小する場合、画像の解像度はビューポートのサイズのままである。<br> | ||
また、描画および再描画ごとに表示用の画像を作成しているため非効率である。<br> | また、描画および再描画ごとに表示用の画像を作成しているため非効率である。<br> | ||
| 335行目: | 987行目: | ||
if(left > 0) | if(left > 0) | ||
{ | { | ||
m_matrix.translate(-left,0); | m_matrix.translate(-left, 0); | ||
left = 0; | left = 0; | ||
} | } | ||
| 341行目: | 993行目: | ||
if(top > 0) | if(top > 0) | ||
{ | { | ||
m_matrix.translate(0,-top); | m_matrix.translate(0, -top); | ||
top = 0; | top = 0; | ||
} | } | ||
| 359行目: | 1,011行目: | ||
if(bottom < limBottom) | if(bottom < limBottom) | ||
{ | { | ||
m_matrix.translate(0, limBottom-bottom); | m_matrix.translate(0, limBottom - bottom); | ||
bottom = limBottom; | bottom = limBottom; | ||
} | } | ||
| 389行目: | 1,041行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br><br> | <br><br> | ||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:Qt]] | [[カテゴリ:Qt]] | ||