Merge branch 'master' into stop_motion_improvements

This commit is contained in:
Jeremy Bullock 2020-04-09 17:08:09 -06:00
commit 6413c50de2
17 changed files with 4349 additions and 576 deletions

View file

@ -1530,6 +1530,9 @@ ProjectPopup QLabel {
#GearButton {
qproperty-icon: url('../Default/imgs/white/gear.svg');
}
#GearButton::menu-indicator {
image: "";
}
#SubfolderButton {
qproperty-icon: url('../Default/imgs/white/subfolder.svg');
padding-left: 6px;

View file

@ -1530,6 +1530,9 @@ ProjectPopup QLabel {
#GearButton {
qproperty-icon: url('../Default/imgs/white/gear.svg');
}
#GearButton::menu-indicator {
image: "";
}
#SubfolderButton {
qproperty-icon: url('../Default/imgs/white/subfolder.svg');
padding-left: 6px;

View file

@ -1530,6 +1530,9 @@ ProjectPopup QLabel {
#GearButton {
qproperty-icon: url('imgs/white/gear.svg');
}
#GearButton::menu-indicator {
image: "";
}
#SubfolderButton {
qproperty-icon: url('imgs/white/subfolder.svg');
padding-left: 6px;

View file

@ -97,6 +97,9 @@ PencilTestPopup {
#GearButton {
qproperty-icon: url('@{img-url}/gear.svg');
&::menu-indicator{
image:"";
}
}
#SubfolderButton {

View file

@ -1531,6 +1531,9 @@ ProjectPopup QLabel {
#GearButton {
qproperty-icon: url('../Default/imgs/black/gear.svg');
}
#GearButton::menu-indicator {
image: "";
}
#SubfolderButton {
qproperty-icon: url('../Default/imgs/black/subfolder.svg');
padding-left: 6px;

View file

@ -41,7 +41,7 @@ inline float valueToExposure(float value, float filmGamma) {
inline float exposureToValue(float exposure, float filmGamma) {
return log10(exposure) * filmGamma + 0.5;
}
};
}; // namespace
//--------------------------------------------
// Threads used for FFT computation for each RGB channel
@ -51,17 +51,17 @@ MyThread::MyThread(Channel channel, TRasterP layerTileRas, TRasterP outTileRas,
TRasterP tmpAlphaRas, kiss_fft_cpx* kissfft_comp_iris,
float filmGamma,
bool doLightenComp) // not used for now
: m_channel(channel),
m_layerTileRas(layerTileRas),
m_outTileRas(outTileRas),
m_tmpAlphaRas(tmpAlphaRas),
m_kissfft_comp_iris(kissfft_comp_iris),
m_filmGamma(filmGamma),
m_finished(false),
m_kissfft_comp_in(0),
m_kissfft_comp_out(0),
m_isTerminated(false),
m_doLightenComp(doLightenComp) // not used for now
: m_channel(channel)
, m_layerTileRas(layerTileRas)
, m_outTileRas(outTileRas)
, m_tmpAlphaRas(tmpAlphaRas)
, m_kissfft_comp_iris(kissfft_comp_iris)
, m_filmGamma(filmGamma)
, m_finished(false)
, m_kissfft_comp_in(0)
, m_kissfft_comp_out(0)
, m_isTerminated(false)
, m_doLightenComp(doLightenComp) // not used for now
{}
bool MyThread::init() {
@ -446,6 +446,11 @@ void Iwa_BokehFx::doCompute(TTile& tile, double frame,
if (dimOut.lx < 10000 && dimOut.ly < 10000) {
int new_x = kiss_fft_next_fast_size(dimOut.lx);
int new_y = kiss_fft_next_fast_size(dimOut.ly);
// margin should be integer
while ((new_x - dimOut.lx) % 2 != 0)
new_x = kiss_fft_next_fast_size(new_x + 1);
while ((new_y - dimOut.ly) % 2 != 0)
new_y = kiss_fft_next_fast_size(new_y + 1);
_rectOut = _rectOut.enlarge(static_cast<double>(new_x - dimOut.lx) / 2.0,
static_cast<double>(new_y - dimOut.ly) / 2.0);
@ -852,22 +857,17 @@ void Iwa_BokehFx::convertIris(const float irisSize,
// Create the raster for resized iris
double2 resizedIrisSize = {std::abs(irisSizeResampleRatio) * irisOrgSize.x,
std::abs(irisSizeResampleRatio) * irisOrgSize.y};
int2 filterSize = {tceil(resizedIrisSize.x), tceil(resizedIrisSize.y)};
// add 1 pixel margins to all sides
int2 filterSize = {int(std::ceil(resizedIrisSize.x)) + 2,
int(std::ceil(resizedIrisSize.y)) + 2};
TPointD resizeOffset((double)filterSize.x - resizedIrisSize.x,
(double)filterSize.y - resizedIrisSize.y);
// Add some adjustment in order to absorb the difference of the cases when the
// iris size is odd and even numbers.
bool isIrisOffset[2] = {false, false};
// Try to set the center of the iris to the center of the screen
if ((dimOut.lx - filterSize.x) % 2 == 1) {
filterSize.x++;
isIrisOffset[0] = true;
}
if ((dimOut.ly - filterSize.y) % 2 == 1) {
filterSize.y++;
isIrisOffset[1] = true;
}
if ((dimOut.lx - filterSize.x) % 2 == 1) filterSize.x++;
if ((dimOut.ly - filterSize.y) % 2 == 1) filterSize.y++;
// Terminate if the filter size becomes bigger than the output size.
if (filterSize.x > dimOut.lx || filterSize.y > dimOut.ly) {
@ -882,10 +882,9 @@ void Iwa_BokehFx::convertIris(const float irisSize,
// Add some adjustment in order to absorb the 0.5 translation to be done in
// resample()
TAffine aff;
TPointD affOffset((isIrisOffset[0]) ? 0.5 : 1.0,
(isIrisOffset[1]) ? 0.5 : 1.0);
if (!isIrisOffset[0]) affOffset.x -= resizeOffset.x / 2;
if (!isIrisOffset[1]) affOffset.y -= resizeOffset.y / 2;
TPointD affOffset(0.5, 0.5);
affOffset += TPointD((dimOut.lx % 2 == 1) ? 0.5 : 0.0,
(dimOut.ly % 2 == 1) ? 0.5 : 0.0);
aff = TTranslation(resizedIris->getCenterD() + affOffset);
aff *= TScale(irisSizeResampleRatio);

View file

@ -463,21 +463,15 @@ void Iwa_BokehRefFx::convertIris(const float irisSize, const TRectD& irisBBox,
// create the raster for resizing
TDimensionD resizedIrisSize(std::abs(irisSizeResampleRatio) * irisOrgSize.lx,
std::abs(irisSizeResampleRatio) * irisOrgSize.ly);
TDimensionI filterSize((int)std::ceil(resizedIrisSize.lx),
(int)std::ceil(resizedIrisSize.ly));
// add 1 pixel margins to all sides
TDimensionI filterSize((int)std::ceil(resizedIrisSize.lx) + 2,
(int)std::ceil(resizedIrisSize.ly) + 2);
TPointD resizeOffset((double)filterSize.lx - resizedIrisSize.lx,
(double)filterSize.ly - resizedIrisSize.ly);
bool isIrisOffset[2] = {false, false};
// iris shape must be exactly at the center of the image
if ((dimOut.lx - filterSize.lx) % 2 == 1) {
filterSize.lx++;
isIrisOffset[0] = true;
}
if ((dimOut.ly - filterSize.ly) % 2 == 1) {
filterSize.ly++;
isIrisOffset[1] = true;
}
if ((dimOut.lx - filterSize.lx) % 2 == 1) filterSize.lx++;
if ((dimOut.ly - filterSize.ly) % 2 == 1) filterSize.ly++;
// if the filter size becomes larger than the output size, return
if (filterSize.lx > dimOut.lx || filterSize.ly > dimOut.ly) {
@ -491,10 +485,9 @@ void Iwa_BokehRefFx::convertIris(const float irisSize, const TRectD& irisBBox,
// offset
TAffine aff;
TPointD affOffset((isIrisOffset[0]) ? 0.5 : 1.0,
(isIrisOffset[1]) ? 0.5 : 1.0);
if (!isIrisOffset[0]) affOffset.x -= resizeOffset.x / 2;
if (!isIrisOffset[1]) affOffset.y -= resizeOffset.y / 2;
TPointD affOffset(0.5, 0.5);
affOffset += TPointD((dimOut.lx % 2 == 1) ? 0.5 : 0.0,
(dimOut.ly % 2 == 1) ? 0.5 : 0.0);
aff = TTranslation(resizedIris->getCenterD() + affOffset);
aff *= TScale(irisSizeResampleRatio);
@ -924,6 +917,11 @@ void Iwa_BokehRefFx::doCompute(TTile& tile, double frame,
if (dimOut.lx < 10000 && dimOut.ly < 10000) {
int new_x = kiss_fft_next_fast_size(dimOut.lx);
int new_y = kiss_fft_next_fast_size(dimOut.ly);
// margin should be integer
while ((new_x - dimOut.lx) % 2 != 0)
new_x = kiss_fft_next_fast_size(new_x + 1);
while ((new_y - dimOut.ly) % 2 != 0)
new_y = kiss_fft_next_fast_size(new_y + 1);
rectOut = rectOut.enlarge(static_cast<double>(new_x - dimOut.lx) / 2.0,
static_cast<double>(new_y - dimOut.ly) / 2.0);

View file

@ -13,7 +13,6 @@ set(MOC_HEADERS
boardsettingspopup.h
brightnessandcontrastpopup.h
cachefxcommand.h
cameracapturelevelcontrol.h
camerasettingspopup.h
canvassizepopup.h
castviewer.h
@ -69,7 +68,6 @@ set(MOC_HEADERS
outputsettingspopup.h
overwritepopup.h
pane.h
penciltestpopup.h
pltgizmopopup.h
preferencespopup.h
previewer.h
@ -124,6 +122,14 @@ set(MOC_HEADERS
xdtsimportpopup.h
../stopmotion/stopmotion.h
../stopmotion/stopmotioncontroller.h
cameracapturelevelcontrol.h
penciltestpopup.h
)
else()
set(MOC_HEADERS
${MOC_HEADERS}
cameracapturelevelcontrol_qt.h
penciltestpopup_qt.h
)
set(HEADERS
@ -334,11 +340,9 @@ set(SOURCES
comboviewerpane.cpp
historypane.cpp
cleanupsettingspane.cpp
penciltestpopup.cpp
audiorecordingpopup.cpp
locatorpopup.cpp
styleshortcutswitchablepanel.cpp
cameracapturelevelcontrol.cpp
reframepopup.cpp
autoinputcellnumberpopup.cpp
colormodelbehaviorpopup.cpp
@ -354,6 +358,14 @@ set(SOURCES
predict3d.cpp
../stopmotion/stopmotion.cpp
../stopmotion/stopmotioncontroller.cpp
cameracapturelevelcontrol.cpp
penciltestpopup.cpp
)
else()
set(SOURCES
${SOURCES}
cameracapturelevelcontrol_qt.cpp
penciltestpopup_qt.cpp
)
add_translation(toonz ${HEADERS} ${SOURCES})

View file

@ -5,7 +5,12 @@
#include "menubarcommandids.h"
#include "cellselection.h"
#include "columnselection.h"
#include "penciltestpopup.h" // for FrameNumberLineEdit
#ifdef WITH_STOPMOTION
#include "penciltestpopup.h"
#else
#include "penciltestpopup_qt.h"
#endif
// TnzQt includes
#include "toonzqt/intfield.h"
@ -31,7 +36,6 @@
#include <iostream>
namespace {
class AutoInputCellNumberUndo final : public TUndo {
int m_increment, m_interval, m_step, m_repeat;
int m_from, m_to;
@ -64,7 +68,7 @@ public:
int rowsCount() { return m_rowsCount; }
};
};
}; // namespace
//-----------------------------------------------------------------------------
// executing this on column selection, set r1 = -1.

View file

@ -50,12 +50,12 @@ void drawSliderHandle(QPoint pos, QPainter& p, QColor color, bool selected) {
p.drawConvexPolygon(points, 5);
p.resetTransform();
}
};
}; // namespace
//-----------------------------------------------------------------------------
CameraCaptureLevelHistogram::CameraCaptureLevelHistogram(QWidget* parent)
: QFrame(parent)
, m_histogramCue(false)
//, m_histogramCue(false)
, m_currentItem(None)
, m_black(0)
, m_white(255)
@ -72,29 +72,27 @@ CameraCaptureLevelHistogram::CameraCaptureLevelHistogram(QWidget* parent)
//-----------------------------------------------------------------------------
void CameraCaptureLevelHistogram::updateHistogram(QImage& image) {
// obtain histogram only when clicked
if (!m_histogramCue) return;
void CameraCaptureLevelHistogram::updateHistogram(cv::Mat& image) {
cv::Mat gray, hist;
cv::cvtColor(image, gray, cv::COLOR_RGB2GRAY);
QVector<int> tmpHisto(256);
tmpHisto.fill(0);
for (int y = 0; y < image.height(); y++) {
QRgb* rgb_p = (QRgb*)(image.scanLine(y));
for (int x = 0; x < image.width(); x++, rgb_p++) {
tmpHisto[qGray(*rgb_p)]++;
}
}
// obtain max value and normalize
int max = 0;
const int hdims[] = {256}; // size of histogram
const float hranges[] = {0, 256};
const float* ranges[] = {hranges}; // min and max values for each bin
double max_val = .0;
cv::calcHist(&gray, 1, 0, cv::Mat(), hist, 1, hdims, ranges);
// obtain the maximum value
cv::minMaxLoc(hist, 0, &max_val);
// scaling
hist = hist * (max_val ? HISTOGRAM_HEIGHT / max_val : 0.);
float* histVal = hist.ptr<float>(0);
for (int c = 0; c < 256; c++) {
if (tmpHisto.at(c) > max) max = tmpHisto.at(c);
}
for (int c = 0; c < 256; c++) {
m_histogramData[c] = tmpHisto.at(c) * HISTOGRAM_HEIGHT / max;
m_histogramData[c] = int(histVal[c]);
}
histogramObtained = true;
update();
m_histogramCue = false;
}
//-----------------------------------------------------------------------------
@ -106,7 +104,7 @@ void CameraCaptureLevelHistogram::paintEvent(QPaintEvent* event) {
// draw histogram
p.setPen(Qt::black);
p.setBrush((m_currentItem == Histogram) ? Qt::darkGray : QColor(32, 32, 32));
p.setBrush(QColor(32, 32, 32));
p.drawRect(histoRect.adjusted(-1, -1, 0, 0));
if (histogramObtained) {
@ -117,10 +115,6 @@ void CameraCaptureLevelHistogram::paintEvent(QPaintEvent* event) {
histoRect.bottomLeft() + QPoint(c, -m_histogramData.at(c)));
}
}
if (!histogramObtained || m_currentItem == Histogram) {
p.setPen(Qt::white);
p.drawText(histoRect, Qt::AlignCenter, tr("Click to Update Histogram"));
}
p.setRenderHint(QPainter::Antialiasing);
// draw slider handles
@ -147,10 +141,6 @@ void CameraCaptureLevelHistogram::paintEvent(QPaintEvent* event) {
void CameraCaptureLevelHistogram::mousePressEvent(QMouseEvent* event) {
if (event->button() != Qt::LeftButton) return;
if (m_currentItem == Histogram) {
m_histogramCue = true;
return;
}
if (m_currentItem == None) return;
QPoint pos = event->pos();
if (m_currentItem == BlackSlider)
@ -168,7 +158,7 @@ void CameraCaptureLevelHistogram::mousePressEvent(QMouseEvent* event) {
void CameraCaptureLevelHistogram::mouseMoveEvent(QMouseEvent* event) {
QPoint pos = event->pos();
if (event->buttons() & Qt::LeftButton) {
if (m_currentItem == None || m_currentItem == Histogram) return;
if (m_currentItem == None) return;
int hPos = pos.x() - SIDE_MARGIN - m_offset;
bool changed = false;
@ -223,8 +213,7 @@ void CameraCaptureLevelHistogram::mouseMoveEvent(QMouseEvent* event) {
setToolTip("");
QRect histoRect(5, 1, 256, HISTOGRAM_HEIGHT);
if (histoRect.contains(pos)) {
setToolTip(tr("Click to Update Histogram"));
m_currentItem = Histogram;
// do nothing
} else {
// slider handles
QPoint sliderBasePos(SIDE_MARGIN, HISTOGRAM_HEIGHT + 2);
@ -286,6 +275,7 @@ CameraCaptureLevelControl::CameraCaptureLevelControl(QWidget* parent)
m_whiteFld = new IntLineEdit(this, m_histogram->white(), 2, 255);
m_thresholdFld = new IntLineEdit(this, m_histogram->threshold(), 0, 255);
m_gammaFld = new DoubleLineEdit(this, m_histogram->gamma());
m_lut = cv::Mat(1, 256, CV_8U);
m_blackFld->setToolTip(tr("Black Point Value"));
m_whiteFld->setToolTip(tr("White Point Value"));
@ -330,6 +320,10 @@ CameraCaptureLevelControl::CameraCaptureLevelControl(QWidget* parent)
//-----------------------------------------------------------------------------
void CameraCaptureLevelControl::onHistogramValueChanged(int itemId) {
if (itemId == CameraCaptureLevelHistogram::ThresholdSlider) {
m_thresholdFld->setValue(m_histogram->threshold());
return;
}
if (itemId == CameraCaptureLevelHistogram::BlackSlider) {
m_blackFld->setValue(m_histogram->black());
m_whiteFld->setRange(m_histogram->black() + 2, 255);
@ -338,9 +332,8 @@ void CameraCaptureLevelControl::onHistogramValueChanged(int itemId) {
m_blackFld->setRange(0, m_histogram->white() - 2);
} else if (itemId == CameraCaptureLevelHistogram::GammaSlider) {
m_gammaFld->setValue(m_histogram->gamma());
} else if (itemId == CameraCaptureLevelHistogram::ThresholdSlider) {
m_thresholdFld->setValue(m_histogram->threshold());
}
computeLut();
}
//-----------------------------------------------------------------------------
@ -367,3 +360,42 @@ void CameraCaptureLevelControl::setMode(bool color_grayscale) {
m_thresholdFld->setVisible(!color_grayscale);
update();
}
//-----------------------------------------------------------------------------
void CameraCaptureLevelControl::adjustLevel(cv::Mat& image) {
int black = m_histogram->black();
int white = m_histogram->white();
float gamma = m_histogram->gamma();
if (black == 0 && white == 255 && gamma == 1.0) return;
cv::LUT(image, m_lut, image);
}
void CameraCaptureLevelControl::binarize(cv::Mat& image) {
cv::threshold(image, image, m_histogram->threshold(), 255, cv::THRESH_BINARY);
}
void CameraCaptureLevelControl::computeLut() {
int black = m_histogram->black();
int white = m_histogram->white();
float gamma = m_histogram->gamma();
const float maxChannelValueF = 255.0f;
float value;
uchar* p = m_lut.data;
for (int i = 0; i < 256; i++) {
if (i <= black)
value = 0.0f;
else if (i >= white)
value = 1.0f;
else {
value = (float)(i - black) / (float)(white - black);
value = std::pow(value, 1.0f / gamma);
}
p[i] = (uchar)std::floor(value * maxChannelValueF);
}
}

View file

@ -5,11 +5,12 @@
#include <QFrame>
#include <QVector>
#include "opencv2/opencv.hpp"
namespace DVGui {
class IntLineEdit;
class DoubleLineEdit;
}
} // namespace DVGui
//=============================================================================
// CameraCaptureLevelHistogram
//-----------------------------------------------------------------------------
@ -22,15 +23,12 @@ public:
WhiteSlider,
GammaSlider,
ThresholdSlider,
Histogram,
NumItems
};
enum LevelControlMode { Color_GrayScale, BlackAndWhite, NumModes };
private:
bool m_histogramCue;
QVector<int> m_histogramData;
LevelControlItem m_currentItem;
int m_black, m_white, m_threshold;
@ -42,7 +40,7 @@ private:
public:
CameraCaptureLevelHistogram(QWidget* parent = 0);
void updateHistogram(QImage& image);
void updateHistogram(cv::Mat& image);
int black() { return m_black; }
int white() { return m_white; }
@ -78,19 +76,18 @@ class CameraCaptureLevelControl : public QFrame {
CameraCaptureLevelHistogram* m_histogram;
DVGui::IntLineEdit *m_blackFld, *m_whiteFld, *m_thresholdFld;
DVGui::DoubleLineEdit* m_gammaFld;
cv::Mat m_lut;
void computeLut();
public:
CameraCaptureLevelControl(QWidget* parent = 0);
void updateHistogram(QImage& image) { m_histogram->updateHistogram(image); }
void updateHistogram(cv::Mat& image) { m_histogram->updateHistogram(image); }
void setMode(bool color_grayscale);
void getValues(int& black, int& white, float& gamma) {
black = m_histogram->black();
white = m_histogram->white();
gamma = m_histogram->gamma();
}
float getThreshold() { return m_histogram->threshold(); }
void adjustLevel(cv::Mat& image);
void binarize(cv::Mat& image);
protected slots:
void onHistogramValueChanged(int itemId);

View file

@ -0,0 +1,369 @@
#include "cameracapturelevelcontrol_qt.h"
#include "toonzqt/intfield.h"
#include "toonzqt/doublefield.h"
#include <QPainter>
#include <QMouseEvent>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <cmath>
#include <iostream>
using namespace DVGui;
namespace {
const int HISTOGRAM_HEIGHT = 50;
const int SIDE_MARGIN = 7;
static bool histogramObtained = false;
// returns the horizontal position of gamma slider (0-255)
int gammaToHPos(float gamma, int black, int white) {
float ratio = std::pow(0.5f, gamma);
return black + (int)std::round((float)(white - black) * ratio);
}
// returns the gamma value from the slider position
float hPosToGamma(int hPos, int black, int white) {
if (hPos <= black + 1)
return 9.99f;
else if (hPos >= white - 1)
return 0.01f;
float ratio = (float)(hPos - black) / (float)(white - black);
float gamma = std::log(ratio) / std::log(0.5);
int decimals = 2;
gamma =
std::round(gamma * std::pow(10.0, decimals)) / std::pow(10.0, decimals);
return gamma;
}
void drawSliderHandle(QPoint pos, QPainter& p, QColor color, bool selected) {
p.setPen((selected) ? Qt::yellow : Qt::black);
p.setBrush(color);
p.translate(pos);
static const QPoint points[5] = {QPoint(0, 0), QPoint(-6, 8), QPoint(-3, 12),
QPoint(3, 12), QPoint(6, 8)};
p.drawConvexPolygon(points, 5);
p.resetTransform();
}
}; // namespace
//-----------------------------------------------------------------------------
CameraCaptureLevelHistogram::CameraCaptureLevelHistogram(QWidget* parent)
: QFrame(parent)
, m_histogramCue(false)
, m_currentItem(None)
, m_black(0)
, m_white(255)
, m_gamma(1.0)
, m_threshold(128)
, m_offset(0)
, m_mode(Color_GrayScale)
, m_histogramData(256) {
setFixedSize(256 + SIDE_MARGIN * 2, HISTOGRAM_HEIGHT + 15);
setMouseTracking(true);
m_histogramData.fill(0);
}
//-----------------------------------------------------------------------------
void CameraCaptureLevelHistogram::updateHistogram(QImage& image) {
// obtain histogram only when clicked
if (!m_histogramCue) return;
QVector<int> tmpHisto(256);
tmpHisto.fill(0);
for (int y = 0; y < image.height(); y++) {
QRgb* rgb_p = (QRgb*)(image.scanLine(y));
for (int x = 0; x < image.width(); x++, rgb_p++) {
tmpHisto[qGray(*rgb_p)]++;
}
}
// obtain max value and normalize
int max = 0;
for (int c = 0; c < 256; c++) {
if (tmpHisto.at(c) > max) max = tmpHisto.at(c);
}
for (int c = 0; c < 256; c++) {
m_histogramData[c] = tmpHisto.at(c) * HISTOGRAM_HEIGHT / max;
}
histogramObtained = true;
update();
m_histogramCue = false;
}
//-----------------------------------------------------------------------------
void CameraCaptureLevelHistogram::paintEvent(QPaintEvent* event) {
QPainter p(this);
QRect histoRect(SIDE_MARGIN, 1, 256, HISTOGRAM_HEIGHT);
// draw histogram
p.setPen(Qt::black);
p.setBrush((m_currentItem == Histogram) ? Qt::darkGray : QColor(32, 32, 32));
p.drawRect(histoRect.adjusted(-1, -1, 0, 0));
if (histogramObtained) {
p.setPen(Qt::white);
for (int c = 0; c < 256; c++) {
if (m_histogramData.at(c) == 0) continue;
p.drawLine(histoRect.bottomLeft() + QPoint(c, 0),
histoRect.bottomLeft() + QPoint(c, -m_histogramData.at(c)));
}
}
if (!histogramObtained || m_currentItem == Histogram) {
p.setPen(Qt::white);
p.drawText(histoRect, Qt::AlignCenter, tr("Click to Update Histogram"));
}
p.setRenderHint(QPainter::Antialiasing);
// draw slider handles
QPoint sliderBasePos(SIDE_MARGIN, HISTOGRAM_HEIGHT + 2);
if (m_mode == Color_GrayScale) {
// black
drawSliderHandle(sliderBasePos + QPoint(m_black, 0), p, QColor(32, 32, 32),
m_currentItem == BlackSlider);
// gamma
drawSliderHandle(
sliderBasePos + QPoint(gammaToHPos(m_gamma, m_black, m_white), 0), p,
Qt::gray, m_currentItem == GammaSlider);
// white
drawSliderHandle(sliderBasePos + QPoint(m_white, 0), p,
QColor(220, 220, 220), m_currentItem == WhiteSlider);
} else if (m_mode == BlackAndWhite)
// threshold
drawSliderHandle(sliderBasePos + QPoint(m_threshold, 0), p, Qt::gray,
m_currentItem == ThresholdSlider);
p.setRenderHint(QPainter::Antialiasing, false);
}
//-----------------------------------------------------------------------------
void CameraCaptureLevelHistogram::mousePressEvent(QMouseEvent* event) {
if (event->button() != Qt::LeftButton) return;
if (m_currentItem == Histogram) {
m_histogramCue = true;
return;
}
if (m_currentItem == None) return;
QPoint pos = event->pos();
if (m_currentItem == BlackSlider)
m_offset = pos.x() - SIDE_MARGIN - m_black;
else if (m_currentItem == GammaSlider)
m_offset = pos.x() - SIDE_MARGIN - gammaToHPos(m_gamma, m_black, m_white);
else if (m_currentItem == BlackSlider)
m_offset = pos.x() - SIDE_MARGIN - m_white;
else if (m_currentItem == ThresholdSlider)
m_offset = pos.x() - SIDE_MARGIN - m_threshold;
}
//-----------------------------------------------------------------------------
void CameraCaptureLevelHistogram::mouseMoveEvent(QMouseEvent* event) {
QPoint pos = event->pos();
if (event->buttons() & Qt::LeftButton) {
if (m_currentItem == None || m_currentItem == Histogram) return;
int hPos = pos.x() - SIDE_MARGIN - m_offset;
bool changed = false;
if (m_currentItem == BlackSlider) {
if (hPos < 0)
hPos = 0;
else if (hPos > m_white - 2)
hPos = m_white - 2;
if (hPos != m_black) {
m_black = hPos;
changed = true;
}
} else if (m_currentItem == GammaSlider) {
if (hPos < m_black + 1)
hPos = m_black + 1;
else if (hPos > m_white - 1)
hPos = m_white - 1;
float gamma = hPosToGamma(hPos, m_black, m_white);
if (gamma != m_gamma) {
m_gamma = gamma;
changed = true;
}
} else if (m_currentItem == WhiteSlider) {
if (hPos < m_black + 2)
hPos = m_black + 2;
else if (hPos > 255)
hPos = 255;
if (hPos != m_white) {
m_white = hPos;
changed = true;
}
} else if (m_currentItem == ThresholdSlider) {
if (hPos < 0)
hPos = 0;
else if (hPos > 255)
hPos = 255;
if (hPos != m_threshold) {
m_threshold = hPos;
changed = true;
}
}
if (changed) {
update();
emit valueChange(m_currentItem);
}
return;
}
// on hover
LevelControlItem oldItem = m_currentItem;
m_currentItem = None;
setToolTip("");
QRect histoRect(5, 1, 256, HISTOGRAM_HEIGHT);
if (histoRect.contains(pos)) {
setToolTip(tr("Click to Update Histogram"));
m_currentItem = Histogram;
} else {
// slider handles
QPoint sliderBasePos(SIDE_MARGIN, HISTOGRAM_HEIGHT + 2);
QRect sliderRect(-6, 0, 12, 12);
if (m_mode == Color_GrayScale) {
// white
if (sliderRect.translated(sliderBasePos + QPoint(m_white, 0))
.contains(pos)) {
m_currentItem = WhiteSlider;
setToolTip(tr("Drag to Move White Point"));
}
// gamma
else if (sliderRect
.translated(
sliderBasePos +
QPoint(gammaToHPos(m_gamma, m_black, m_white), 0))
.contains(pos)) {
m_currentItem = GammaSlider;
setToolTip(tr("Drag to Move Gamma"));
}
// black
else if (sliderRect.translated(sliderBasePos + QPoint(m_black, 0))
.contains(pos)) {
m_currentItem = BlackSlider;
setToolTip(tr("Drag to Move Black Point"));
}
} else if (m_mode == BlackAndWhite) {
// threshold
if (sliderRect.translated(sliderBasePos + QPoint(m_threshold, 0))
.contains(pos)) {
m_currentItem = ThresholdSlider;
setToolTip(tr("Drag to Move Threshold Point"));
}
}
}
if (oldItem != m_currentItem) update();
}
//-----------------------------------------------------------------------------
void CameraCaptureLevelHistogram::mouseReleaseEvent(QMouseEvent* event) {
m_offset = 0;
}
//-----------------------------------------------------------------------------
void CameraCaptureLevelHistogram::leaveEvent(QEvent* event) {
m_currentItem = None;
m_offset = 0;
update();
}
//=============================================================================
CameraCaptureLevelControl::CameraCaptureLevelControl(QWidget* parent)
: QFrame(parent) {
m_histogram = new CameraCaptureLevelHistogram(this);
m_blackFld = new IntLineEdit(this, m_histogram->black(), 0, 253);
m_whiteFld = new IntLineEdit(this, m_histogram->white(), 2, 255);
m_thresholdFld = new IntLineEdit(this, m_histogram->threshold(), 0, 255);
m_gammaFld = new DoubleLineEdit(this, m_histogram->gamma());
m_blackFld->setToolTip(tr("Black Point Value"));
m_whiteFld->setToolTip(tr("White Point Value"));
m_thresholdFld->setToolTip(tr("Threshold Value"));
m_thresholdFld->hide();
m_gammaFld->setRange(0.01, 9.99);
m_gammaFld->setDecimals(2);
m_gammaFld->setFixedWidth(54);
m_gammaFld->setToolTip(tr("Gamma Value"));
QVBoxLayout* mainLay = new QVBoxLayout();
mainLay->setMargin(0);
mainLay->setSpacing(4);
{
mainLay->addWidget(m_histogram, 0, Qt::AlignHCenter);
QHBoxLayout* fieldsLay = new QHBoxLayout();
fieldsLay->setMargin(1);
fieldsLay->setSpacing(0);
{
fieldsLay->addWidget(m_blackFld, 0);
fieldsLay->addStretch(1);
fieldsLay->addWidget(m_gammaFld, 0);
fieldsLay->addWidget(m_thresholdFld, 0);
fieldsLay->addStretch(1);
fieldsLay->addWidget(m_whiteFld, 0);
}
mainLay->addLayout(fieldsLay, 0);
}
setLayout(mainLay);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
connect(m_histogram, SIGNAL(valueChange(int)), this,
SLOT(onHistogramValueChanged(int)));
connect(m_blackFld, SIGNAL(editingFinished()), this, SLOT(onFieldChanged()));
connect(m_whiteFld, SIGNAL(editingFinished()), this, SLOT(onFieldChanged()));
connect(m_gammaFld, SIGNAL(editingFinished()), this, SLOT(onFieldChanged()));
connect(m_thresholdFld, SIGNAL(editingFinished()), this,
SLOT(onFieldChanged()));
}
//-----------------------------------------------------------------------------
void CameraCaptureLevelControl::onHistogramValueChanged(int itemId) {
if (itemId == CameraCaptureLevelHistogram::BlackSlider) {
m_blackFld->setValue(m_histogram->black());
m_whiteFld->setRange(m_histogram->black() + 2, 255);
} else if (itemId == CameraCaptureLevelHistogram::WhiteSlider) {
m_whiteFld->setValue(m_histogram->white());
m_blackFld->setRange(0, m_histogram->white() - 2);
} else if (itemId == CameraCaptureLevelHistogram::GammaSlider) {
m_gammaFld->setValue(m_histogram->gamma());
} else if (itemId == CameraCaptureLevelHistogram::ThresholdSlider) {
m_thresholdFld->setValue(m_histogram->threshold());
}
}
//-----------------------------------------------------------------------------
void CameraCaptureLevelControl::onFieldChanged() {
if (m_histogram->mode() == CameraCaptureLevelHistogram::Color_GrayScale)
m_histogram->setValues(m_blackFld->getValue(), m_whiteFld->getValue(),
m_gammaFld->getValue());
else if (m_histogram->mode() == CameraCaptureLevelHistogram::BlackAndWhite)
m_histogram->setThreshold(m_thresholdFld->getValue());
m_histogram->update();
}
//-----------------------------------------------------------------------------
void CameraCaptureLevelControl::setMode(bool color_grayscale) {
m_histogram->setMode((color_grayscale)
? CameraCaptureLevelHistogram::Color_GrayScale
: CameraCaptureLevelHistogram::BlackAndWhite);
m_blackFld->setVisible(color_grayscale);
m_whiteFld->setVisible(color_grayscale);
m_gammaFld->setVisible(color_grayscale);
m_thresholdFld->setVisible(!color_grayscale);
update();
}

View file

@ -0,0 +1,100 @@
#pragma once
#ifndef CAMERA_CAPTURE_LEVEL_CONTROL_H
#define CAMERA_CAPTURE_LEVEL_CONTROL_H
#include <QFrame>
#include <QVector>
namespace DVGui {
class IntLineEdit;
class DoubleLineEdit;
} // namespace DVGui
//=============================================================================
// CameraCaptureLevelHistogram
//-----------------------------------------------------------------------------
class CameraCaptureLevelHistogram : public QFrame {
Q_OBJECT
public:
enum LevelControlItem {
None = 0,
BlackSlider,
WhiteSlider,
GammaSlider,
ThresholdSlider,
Histogram,
NumItems
};
enum LevelControlMode { Color_GrayScale, BlackAndWhite, NumModes };
private:
bool m_histogramCue;
QVector<int> m_histogramData;
LevelControlItem m_currentItem;
int m_black, m_white, m_threshold;
float m_gamma;
int m_offset; // offset between the handle position and the clicked position
LevelControlMode m_mode;
public:
CameraCaptureLevelHistogram(QWidget* parent = 0);
void updateHistogram(QImage& image);
int black() { return m_black; }
int white() { return m_white; }
float gamma() { return m_gamma; }
int threshold() { return m_threshold; }
LevelControlMode mode() { return m_mode; }
void setValues(int black, int white, float gamma) {
m_black = black;
m_white = white;
m_gamma = gamma;
}
void setThreshold(int thres) { m_threshold = thres; }
void setMode(LevelControlMode mode) { m_mode = mode; }
protected:
void paintEvent(QPaintEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void leaveEvent(QEvent* event) override;
signals:
void valueChange(int itemId);
};
//-----------------------------------------------------------------------------
class CameraCaptureLevelControl : public QFrame {
Q_OBJECT
CameraCaptureLevelHistogram* m_histogram;
DVGui::IntLineEdit *m_blackFld, *m_whiteFld, *m_thresholdFld;
DVGui::DoubleLineEdit* m_gammaFld;
public:
CameraCaptureLevelControl(QWidget* parent = 0);
void updateHistogram(QImage& image) { m_histogram->updateHistogram(image); }
void setMode(bool color_grayscale);
void getValues(int& black, int& white, float& gamma) {
black = m_histogram->black();
white = m_histogram->white();
gamma = m_histogram->gamma();
}
float getThreshold() { return m_histogram->threshold(); }
protected slots:
void onHistogramValueChanged(int itemId);
void onFieldChanged();
};
#endif

View file

@ -1,5 +1,17 @@
#include "penciltestpopup.h"
#ifdef WIN32
#include <Windows.h>
#include <mfobjects.h>
#include <mfapi.h>
#include <mfidl.h>
#pragma comment(lib, "Mfplat.lib")
#pragma comment(lib, "Mf.lib")
#pragma comment(lib, "Mfreadwrite.lib")
#pragma comment(lib, "mfuuid.lib")
#pragma comment(lib, "shlwapi.lib")
#endif
// Tnz6 includes
#include "tapp.h"
#include "menubarcommandids.h"
@ -87,6 +99,10 @@ TEnv::StringVar CamCapCameraName("CamCapCameraName", "");
TEnv::StringVar CamCapCameraResolution("CamCapCameraResolution", "");
// Whether to open save-in popup on launch
TEnv::IntVar CamCapOpenSaveInPopupOnLaunch("CamCapOpenSaveInPopupOnLaunch", 0);
TEnv::IntVar CamCapUseMjpg("CamCapUseMjpg", 1);
#ifdef _WIN32
TEnv::IntVar CamCapUseDirectShow("CamCapUseDirectShow", 1);
#endif
// SaveInFolderPopup settings
TEnv::StringVar CamCapSaveInParentFolder("CamCapSaveInParentFolder", "");
TEnv::IntVar CamCapSaveInPopupSubFolder("CamCapSaveInPopupSubFolder", 0);
@ -118,7 +134,9 @@ void convertImageToRaster(TRaster32P dstRas, const QImage& srcImg) {
dstRas->unlock();
}
void bgReduction(QImage& srcImg, QImage& bgImg, int reduction) {
void bgReduction(cv::Mat& srcImg, cv::Mat& bgImg, int reduction) {
// void bgReduction(QImage& srcImg, QImage& bgImg, int reduction) {
if (srcImg.cols != bgImg.cols || srcImg.rows != bgImg.rows) return;
float reductionRatio = (float)reduction / 100.0f;
// first, make the reduction table
std::vector<int> reductionAmount(256);
@ -126,104 +144,15 @@ void bgReduction(QImage& srcImg, QImage& bgImg, int reduction) {
reductionAmount[i] = (int)(std::floor((float)(255 - i) * reductionRatio));
}
// then, compute for all pixels
int lx = srcImg.width();
int ly = srcImg.height();
for (int j = 0; j < ly; j++) {
// TPixel32 * pix = ras->pixels(j);
QRgb* pix = (QRgb*)srcImg.scanLine(j);
QRgb* bgPix = (QRgb*)bgImg.scanLine(j);
for (int i = 0; i < lx; i++, pix++, bgPix++) {
*pix = qRgb(std::min(255, qRed(*pix) + reductionAmount[qRed(*bgPix)]),
std::min(255, qGreen(*pix) + reductionAmount[qGreen(*bgPix)]),
std::min(255, qBlue(*pix) + reductionAmount[qBlue(*bgPix)]));
}
}
}
void my_compute_lut(int black, int white, float gamma, std::vector<int>& lut) {
const int maxChannelValue = lut.size() - 1;
const float half_maxChannelValueF = 0.5f * maxChannelValue;
const float maxChannelValueF = maxChannelValue;
float value;
int lutSize = lut.size();
for (int i = 0; i < lutSize; i++) {
if (i <= black)
value = 0.0f;
else if (i >= white)
value = 1.0f;
else {
value = (float)(i - black) / (float)(white - black);
value = std::pow(value, 1.0f / gamma);
}
lut[i] = (int)std::floor(value * maxChannelValueF);
}
}
//-----------------------------------------------------------------------------
inline void doPixGray(QRgb* pix, const std::vector<int>& lut) {
int gray = lut[qGray(*pix)];
*pix = qRgb(gray, gray, gray);
}
//-----------------------------------------------------------------------------
inline void doPixBinary(QRgb* pix, int threshold) {
int gray = qGray(*pix);
if (gray >= threshold)
gray = 255;
else
gray = 0;
*pix = qRgb(gray, gray, gray);
}
//-----------------------------------------------------------------------------
inline void doPix(QRgb* pix, const std::vector<int>& lut) {
// The captured image MUST be full opaque!
*pix = qRgb(lut[qRed(*pix)], lut[qGreen(*pix)], lut[qBlue(*pix)]);
}
//-----------------------------------------------------------------------------
void onChange(QImage& img, int black, int white, float gamma, bool doGray) {
std::vector<int> lut(TPixel32::maxChannelValue + 1);
my_compute_lut(black, white, gamma, lut);
int ly = img.height();
// compute in multi thread
int threadCount =
std::max(1, QThreadPool::globalInstance()->maxThreadCount() / 2);
int tmpStart = 0;
for (int t = 0; t < threadCount; t++) {
int tmpEnd = (int)std::round((float)(ly * (t + 1)) / (float)threadCount);
QRunnable* task;
if (doGray)
task = new ApplyGrayLutTask(tmpStart, tmpEnd, img, lut);
else
task = new ApplyLutTask(tmpStart, tmpEnd, img, lut);
QThreadPool::globalInstance()->start(task);
tmpStart = tmpEnd;
}
QThreadPool::globalInstance()->waitForDone();
}
//-----------------------------------------------------------------------------
void onChangeBW(QImage& img, int threshold) {
int lx = img.width(), y, ly = img.height();
for (y = 0; y < ly; ++y) {
QRgb *pix = (QRgb*)img.scanLine(y), *endPix = (QRgb*)(pix + lx);
while (pix < endPix) {
doPixBinary(pix, threshold);
++pix;
int lx = srcImg.cols;
int ly = srcImg.rows;
for (int j = 0; j < srcImg.rows; j++) {
cv::Vec3b* pix = srcImg.ptr<cv::Vec3b>(j);
cv::Vec3b* bgPix = bgImg.ptr<cv::Vec3b>(j);
for (int i = 0; i < srcImg.cols; i++, pix++, bgPix++) {
*pix = cv::Vec3b(std::min(255, (*pix)[0] + reductionAmount[(*bgPix)[0]]),
std::min(255, (*pix)[1] + reductionAmount[(*bgPix)[1]]),
std::min(255, (*pix)[2] + reductionAmount[(*bgPix)[2]]));
}
}
}
@ -473,130 +402,9 @@ bool getRasterLevelSize(TXshLevel* level, TDimension& dim) {
//=============================================================================
void ApplyLutTask::run() {
int lx = m_img.width();
for (int y = m_fromY; y < m_toY; ++y) {
QRgb *pix = (QRgb*)m_img.scanLine(y), *endPix = (QRgb*)(pix + lx);
while (pix < endPix) {
doPix(pix, m_lut);
++pix;
}
}
}
void ApplyGrayLutTask::run() {
int lx = m_img.width();
for (int y = m_fromY; y < m_toY; ++y) {
QRgb *pix = (QRgb*)m_img.scanLine(y), *endPix = (QRgb*)(pix + lx);
while (pix < endPix) {
doPixGray(pix, m_lut);
++pix;
}
}
}
//=============================================================================
MyVideoSurface::MyVideoSurface(QWidget* widget, QObject* parent)
: QAbstractVideoSurface(parent)
, m_widget(widget)
, m_imageFormat(QImage::Format_Invalid) {}
QList<QVideoFrame::PixelFormat> MyVideoSurface::supportedPixelFormats(
QAbstractVideoBuffer::HandleType handleType) const {
if (handleType == QAbstractVideoBuffer::NoHandle) {
return QList<QVideoFrame::PixelFormat>()
<< QVideoFrame::Format_RGB32 << QVideoFrame::Format_ARGB32
<< QVideoFrame::Format_ARGB32_Premultiplied
<< QVideoFrame::Format_RGB565 << QVideoFrame::Format_RGB555;
} else {
return QList<QVideoFrame::PixelFormat>();
}
}
bool MyVideoSurface::isFormatSupported(const QVideoSurfaceFormat& format,
QVideoSurfaceFormat* similar) const {
Q_UNUSED(similar);
const QImage::Format imageFormat =
QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat());
const QSize size = format.frameSize();
return imageFormat != QImage::Format_Invalid && !size.isEmpty() &&
format.handleType() == QAbstractVideoBuffer::NoHandle;
}
bool MyVideoSurface::start(const QVideoSurfaceFormat& format) {
const QImage::Format imageFormat =
QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat());
const QSize size = format.frameSize();
if (imageFormat != QImage::Format_Invalid && !size.isEmpty()) {
m_imageFormat = imageFormat;
m_imageSize = size;
m_sourceRect = format.viewport();
QAbstractVideoSurface::start(format);
m_widget->updateGeometry();
updateVideoRect();
return true;
} else {
return false;
}
}
void MyVideoSurface::updateVideoRect() {
QSize size = surfaceFormat().sizeHint();
size.scale(m_widget->size(), Qt::KeepAspectRatio);
m_targetRect = QRect(QPoint(0, 0), size);
m_targetRect.moveCenter(m_widget->rect().center());
double scale =
(double)m_targetRect.width() / (double)surfaceFormat().sizeHint().width();
m_S2V_Transform =
QTransform::fromTranslate(m_targetRect.left(), m_targetRect.top())
.scale(scale, scale);
}
bool MyVideoSurface::present(const QVideoFrame& frame) {
if (surfaceFormat().pixelFormat() != frame.pixelFormat() ||
surfaceFormat().frameSize() != frame.size()) {
setError(IncorrectFormatError);
stop();
return false;
} else {
m_currentFrame = frame;
if (m_currentFrame.map(QAbstractVideoBuffer::ReadOnly)) {
QImage image = QImage(m_currentFrame.bits(), m_currentFrame.width(),
m_currentFrame.height(),
m_currentFrame.bytesPerLine(), m_imageFormat);
m_currentFrame.unmap();
if (!image.isNull()) emit frameCaptured(image);
}
return true;
}
}
void MyVideoSurface::stop() {
m_currentFrame = QVideoFrame();
m_targetRect = QRect();
QAbstractVideoSurface::stop();
m_widget->update();
}
//=============================================================================
MyVideoWidget::MyVideoWidget(QWidget* parent)
: QWidget(parent)
, m_previousImage(QImage())
, m_surface(0)
, m_showOnionSkin(false)
, m_onionOpacity(128)
, m_upsideDown(false)
@ -604,44 +412,21 @@ MyVideoWidget::MyVideoWidget(QWidget* parent)
, m_subCameraRect(QRect()) {
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
m_surface = new MyVideoSurface(this);
setMouseTracking(true);
}
MyVideoWidget::~MyVideoWidget() { delete m_surface; }
QSize MyVideoWidget::sizeHint() const {
return m_surface->surfaceFormat().sizeHint();
}
void MyVideoWidget::paintEvent(QPaintEvent* event) {
QPainter p(this);
p.fillRect(rect(), Qt::black);
if (m_surface->isActive()) {
const QRect videoRect = m_surface->videoRect();
const QTransform oldTransform = p.transform();
if (m_upsideDown) {
p.translate(videoRect.center());
p.rotate(180);
p.translate(-videoRect.center());
}
if (m_surface->surfaceFormat().scanLineDirection() ==
QVideoSurfaceFormat::BottomToTop) {
p.scale(1, -1);
p.translate(0, -height());
}
p.drawImage(videoRect, m_image, m_surface->sourceRect());
p.setTransform(oldTransform);
if (!m_image.isNull()) {
p.drawImage(m_targetRect, m_image);
if (m_showOnionSkin && m_onionOpacity > 0.0f && !m_previousImage.isNull() &&
m_previousImage.size() == m_image.size()) {
p.setOpacity((qreal)m_onionOpacity / 255.0);
p.drawImage(videoRect, m_previousImage, m_surface->sourceRect());
p.drawImage(m_targetRect, m_previousImage);
p.setOpacity(1.0);
}
@ -669,12 +454,23 @@ void MyVideoWidget::paintEvent(QPaintEvent* event) {
void MyVideoWidget::resizeEvent(QResizeEvent* event) {
QWidget::resizeEvent(event);
computeTransform(m_image.size());
}
m_surface->updateVideoRect();
void MyVideoWidget::computeTransform(QSize imgSize) {
QSize adjustedSize = imgSize;
adjustedSize.scale(size(), Qt::KeepAspectRatio);
m_targetRect = QRect(QPoint(), adjustedSize);
m_targetRect.moveCenter(rect().center());
double scale = (double)m_targetRect.width() / (double)imgSize.width();
m_S2V_Transform =
QTransform::fromTranslate(m_targetRect.left(), m_targetRect.top())
.scale(scale, scale);
}
void MyVideoWidget::setSubCameraSize(QSize size) {
QSize frameSize = m_surface->surfaceFormat().frameSize();
QSize frameSize = m_image.size();
assert(frameSize == size.expandedTo(frameSize));
m_subCameraRect.setSize(size);
@ -698,7 +494,7 @@ void MyVideoWidget::drawSubCamera(QPainter& p) {
p.drawRect(handleRect);
};
QRect vidSubRect = m_surface->transform().mapRect(m_subCameraRect);
QRect vidSubRect = m_S2V_Transform.mapRect(m_subCameraRect);
p.setBrush(Qt::NoBrush);
drawSubFrameLine(HandleLeft, vidSubRect.topLeft(), vidSubRect.bottomLeft());
drawSubFrameLine(HandleTop, vidSubRect.topLeft(), vidSubRect.topRight());
@ -726,11 +522,11 @@ void MyVideoWidget::mouseMoveEvent(QMouseEvent* event) {
};
// if the sub camera is not active, do nothing and return
if (!m_surface->isActive() || m_subCameraRect.isNull()) return;
if (m_image.isNull() || m_subCameraRect.isNull()) return;
// with no mouse button, update the active handles
if (event->buttons() == Qt::NoButton) {
QRect vidSubRect = m_surface->transform().mapRect(m_subCameraRect);
QRect vidSubRect = m_S2V_Transform.mapRect(m_subCameraRect);
SUBHANDLE preHandle = m_activeSubHandle;
if (!vidSubRect.adjusted(-d, -d, d, d).contains(event->pos()))
m_activeSubHandle = HandleNone;
@ -795,7 +591,7 @@ void MyVideoWidget::mouseMoveEvent(QMouseEvent* event) {
int minimumSize = 100;
QPoint offset =
m_surface->transform().inverted().map(event->pos()) - m_dragStartPos;
m_S2V_Transform.inverted().map(event->pos()) - m_dragStartPos;
if (m_activeSubHandle >= HandleTopLeft &&
m_activeSubHandle <= HandleBottomRight) {
QSize offsetSize = m_preSubCameraRect.size();
@ -808,7 +604,7 @@ void MyVideoWidget::mouseMoveEvent(QMouseEvent* event) {
m_activeSubHandle == HandleTopRight)
offset.rx() *= -1;
}
QSize camSize = m_surface->surfaceFormat().sizeHint();
QSize camSize = m_image.size();
if (m_activeSubHandle == HandleFrame) {
clampPoint(offset, -m_preSubCameraRect.left(),
@ -855,13 +651,13 @@ void MyVideoWidget::mouseMoveEvent(QMouseEvent* event) {
void MyVideoWidget::mousePressEvent(QMouseEvent* event) {
// if the sub camera is not active, do nothing and return
// use left button only and some handle must be active
if (!m_surface->isActive() || m_subCameraRect.isNull() ||
if (m_image.isNull() || m_subCameraRect.isNull() ||
event->button() != Qt::LeftButton || m_activeSubHandle == HandleNone)
return;
// record the original sub camera size
m_preSubCameraRect = m_subCameraRect;
m_dragStartPos = m_surface->transform().inverted().map(event->pos());
m_dragStartPos = m_S2V_Transform.inverted().map(event->pos());
// temporary stop the camera
emit stopCamera();
@ -870,7 +666,7 @@ void MyVideoWidget::mousePressEvent(QMouseEvent* event) {
void MyVideoWidget::mouseReleaseEvent(QMouseEvent* event) {
// if the sub camera is not active, do nothing and return
// use left button only and some handle must be active
if (!m_surface->isActive() || m_subCameraRect.isNull() ||
if (m_image.isNull() || m_subCameraRect.isNull() ||
event->button() != Qt::LeftButton || m_activeSubHandle == HandleNone)
return;
@ -1421,7 +1217,12 @@ PencilTestPopup::PencilTestPopup()
: Dialog(0, false, false, "PencilTest")
, m_currentCamera(NULL)
, m_captureWhiteBGCue(false)
, m_captureCue(false) {
, m_captureCue(false)
, m_useMjpg(CamCapUseMjpg != 0)
#ifdef _WIN32
, m_useDirectShow(CamCapUseDirectShow != 0)
#endif
{
setWindowTitle(tr("Camera Capture"));
// add maximize button to the dialog
@ -1476,15 +1277,12 @@ PencilTestPopup::PencilTestPopup()
m_timerIntervalFld = new IntField(this);
m_captureTimer = new QTimer(this);
m_countdownTimer = new QTimer(this);
m_timer = new QTimer(this);
m_captureButton = new QPushButton(tr("Capture\n[Return key]"), this);
QPushButton* closeButton = new QPushButton(tr("Close"), this);
#ifdef _WIN32
m_captureFilterSettingsBtn = new QPushButton(this);
#else
m_captureFilterSettingsBtn = 0;
#endif
QPushButton* subfolderButton = new QPushButton(tr("Subfolder"), this);
@ -1543,13 +1341,11 @@ PencilTestPopup::PencilTestPopup()
m_captureButton->setIcon(style.standardIcon(QStyle::SP_DialogOkButton));
m_captureButton->setIconSize(QSize(30, 30));
if (m_captureFilterSettingsBtn) {
m_captureFilterSettingsBtn->setObjectName("GearButton");
m_captureFilterSettingsBtn->setFixedSize(23, 23);
m_captureFilterSettingsBtn->setIconSize(QSize(15, 15));
m_captureFilterSettingsBtn->setToolTip(
tr("Video Capture Filter Settings..."));
}
m_captureFilterSettingsBtn->setToolTip(tr("Options"));
m_captureFilterSettingsBtn->setMenu(createOptionsMenu());
subfolderButton->setObjectName("SubfolderButton");
subfolderButton->setIconSize(QSize(15, 15));
@ -1746,8 +1542,8 @@ PencilTestPopup::PencilTestPopup()
SLOT(refreshCameraList()));
ret = ret && connect(m_cameraListCombo, SIGNAL(activated(int)), this,
SLOT(onCameraListComboActivated(int)));
ret = ret && connect(m_resolutionCombo, SIGNAL(activated(const QString&)),
this, SLOT(onResolutionComboActivated(const QString&)));
ret = ret && connect(m_resolutionCombo, SIGNAL(activated(int)), this,
SLOT(onResolutionComboActivated()));
ret = ret && connect(m_fileFormatOptionButton, SIGNAL(pressed()), this,
SLOT(onFileFormatOptionButtonPressed()));
ret = ret && connect(m_levelNameEdit, SIGNAL(levelNameEdited()), this,
@ -1778,9 +1574,6 @@ PencilTestPopup::PencilTestPopup()
ret = ret && connect(closeButton, SIGNAL(clicked()), this, SLOT(reject()));
ret = ret && connect(m_captureButton, SIGNAL(clicked(bool)), this,
SLOT(onCaptureButtonClicked(bool)));
if (m_captureFilterSettingsBtn)
ret = ret && connect(m_captureFilterSettingsBtn, SIGNAL(pressed()), this,
SLOT(onCaptureFilterSettingsBtnPressed()));
ret = ret && connect(subfolderButton, SIGNAL(clicked(bool)), this,
SLOT(openSaveInFolderPopup()));
ret = ret && connect(m_saveInFileFld, SIGNAL(pathChanged()), this,
@ -1799,15 +1592,10 @@ PencilTestPopup::PencilTestPopup()
SLOT(onSubCameraSizeEdited()));
ret = ret && connect(m_subHeightFld, SIGNAL(editingFinished()), this,
SLOT(onSubCameraSizeEdited()));
ret = ret && connect(m_videoWidget, &MyVideoWidget::startCamera, [&]() {
if (m_currentCamera) m_currentCamera->start();
});
ret = ret && connect(m_videoWidget, &MyVideoWidget::stopCamera, [&]() {
if (m_currentCamera) m_currentCamera->stop();
});
ret = ret && connect(m_videoWidget, SIGNAL(subCameraResized(bool)), this,
SLOT(onSubCameraResized(bool)));
ret = ret && connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
assert(ret);
refreshCameraList();
@ -1824,7 +1612,7 @@ PencilTestPopup::PencilTestPopup()
int startupResolutionIndex = m_resolutionCombo->findText(resStr);
if (startupResolutionIndex >= 0) {
m_resolutionCombo->setCurrentIndex(startupResolutionIndex);
onResolutionComboActivated(resStr);
onResolutionComboActivated();
}
}
@ -1833,14 +1621,45 @@ PencilTestPopup::PencilTestPopup()
//-----------------------------------------------------------------------------
PencilTestPopup::~PencilTestPopup() {
if (m_currentCamera) {
if (m_currentCamera->state() == QCamera::ActiveState)
m_currentCamera->stop();
if (m_currentCamera->state() == QCamera::LoadedState)
m_currentCamera->unload();
delete m_currentCamera;
}
PencilTestPopup::~PencilTestPopup() { m_cvWebcam.release(); }
//-----------------------------------------------------------------------------
QMenu* PencilTestPopup::createOptionsMenu() {
QMenu* menu = new QMenu();
bool ret = true;
#ifdef _WIN32
QAction* settingsAct =
menu->addAction(tr("Video Capture Filter Settings..."));
ret = ret && connect(settingsAct, SIGNAL(triggered()), this,
SLOT(onCaptureFilterSettingsBtnPressed()));
settingsAct->setIcon(QIcon(":Resources/preferences.svg"));
menu->addSeparator();
QAction* useDShowAct = menu->addAction(tr("Use Direct Show Webcam Drivers"));
useDShowAct->setCheckable(true);
useDShowAct->setChecked(m_useDirectShow);
ret = ret && connect(useDShowAct, &QAction::toggled, [&](bool checked) {
m_cvWebcam.release();
if (m_timer->isActive()) m_timer->stop();
m_useDirectShow = checked;
CamCapUseDirectShow = checked;
m_timer->start(40);
});
#endif
QAction* useMjpgAct = menu->addAction(tr("Use MJPG with Webcam"));
useMjpgAct->setCheckable(true);
useMjpgAct->setChecked(m_useMjpg);
ret = ret && connect(useMjpgAct, &QAction::toggled, [&](bool checked) {
m_cvWebcam.release();
if (m_timer->isActive()) m_timer->stop();
m_useMjpg = checked;
CamCapUseMjpg = checked;
m_timer->start(40);
});
return menu;
}
//-----------------------------------------------------------------------------
@ -1874,15 +1693,11 @@ void PencilTestPopup::onCameraListComboActivated(int comboIndex) {
QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
if (cameras.size() != m_cameraListCombo->count() - 1) return;
m_cvWebcam.release();
if (m_timer->isActive()) m_timer->stop();
// if selected the non-connected state, then disconnect the current camera
if (comboIndex == 0) {
m_videoWidget->videoSurface()->stop();
if (m_currentCamera) {
if (m_currentCamera->state() == QCamera::ActiveState)
m_currentCamera->stop();
if (m_currentCamera->state() == QCamera::LoadedState)
m_currentCamera->unload();
}
m_deviceName = QString();
m_videoWidget->setImage(QImage());
// update env
@ -1894,9 +1709,6 @@ void PencilTestPopup::onCameraListComboActivated(int comboIndex) {
// in case the camera is not changed (just click the combobox)
if (cameras.at(index).deviceName() == m_deviceName) return;
QCamera* oldCamera = m_currentCamera;
if (oldCamera) m_videoWidget->videoSurface()->stop();
m_currentCamera = new QCamera(cameras.at(index), this);
m_deviceName = cameras.at(index).deviceName();
@ -1907,63 +1719,46 @@ void PencilTestPopup::onCameraListComboActivated(int comboIndex) {
m_resolutionCombo->clear();
QList<QSize> sizes = m_currentCamera->supportedViewfinderResolutions();
for (int s = 0; s < sizes.size(); s++) {
m_currentCamera->unload();
for (const QSize size : sizes) {
m_resolutionCombo->addItem(
QString("%1 x %2").arg(sizes.at(s).width()).arg(sizes.at(s).height()));
QString("%1 x %2").arg(size.width()).arg(size.height()), size);
}
if (!sizes.isEmpty()) {
// select the largest available resolution
m_resolutionCombo->setCurrentIndex(m_resolutionCombo->count() - 1);
QCameraViewfinderSettings settings = m_currentCamera->viewfinderSettings();
settings.setResolution(sizes.last());
m_currentCamera->setViewfinderSettings(settings);
}
m_currentCamera->setViewfinder(m_videoWidget->videoSurface());
m_videoWidget->videoSurface()->start(
m_videoWidget->videoSurface()->surfaceFormat());
// deleting old camera
if (oldCamera) {
if (oldCamera->state() == QCamera::ActiveState) oldCamera->stop();
delete oldCamera;
}
// start new camera
m_currentCamera->start();
m_videoWidget->setImage(QImage());
m_timer->start(40);
// update env
CamCapCameraName = m_cameraListCombo->itemText(comboIndex).toStdString();
}
//-----------------------------------------------------------------------------
void PencilTestPopup::onResolutionComboActivated(const QString& itemText) {
// resolution is written in the itemText with the format "<width> x <height>"
// (e.g. "800 x 600")
QStringList texts = itemText.split(' ');
// the splited text must be "<width>" "x" and "<height>"
if (texts.size() != 3) return;
void PencilTestPopup::onResolutionComboActivated() {
m_cvWebcam.release();
if (m_timer->isActive()) m_timer->stop();
#ifndef MACOSX
m_currentCamera->stop();
m_currentCamera->unload();
#endif
QSize newResolution = m_resolutionCombo->currentData().toSize();
QCameraViewfinderSettings settings = m_currentCamera->viewfinderSettings();
QSize newResolution(texts[0].toInt(), texts[2].toInt());
settings.setResolution(newResolution);
m_currentCamera->setViewfinderSettings(settings);
if (!newResolution.isValid()) return;
if (newResolution == m_resolution) {
m_timer->start(40);
return;
}
m_resolution = newResolution;
// reset white bg
m_whiteBGImg = QImage();
m_whiteBGImg = cv::Mat();
m_bgReductionFld->setDisabled(true);
#ifndef MACOSX
m_currentCamera->start();
#endif
m_videoWidget->setImage(QImage());
m_videoWidget->computeTransform(m_resolution);
// update env
CamCapCameraResolution = itemText.toStdString();
CamCapCameraResolution = m_resolutionCombo->currentText().toStdString();
refreshFrameInfo();
@ -1982,13 +1777,14 @@ void PencilTestPopup::onResolutionComboActivated(const QString& itemText) {
TDimension camres = camera->getRes();
newResolution =
QSize(camres.lx, camres.ly).scaled(newResolution, Qt::KeepAspectRatio);
// newResolution.scale(QSize(res.lx, res.ly), Qt::KeepAspectRatio);
m_subWidthFld->setValue(newResolution.width());
m_subHeightFld->setValue(newResolution.height());
} else {
m_subWidthFld->setValue(m_allowedCameraSize.width());
m_subHeightFld->setValue(m_allowedCameraSize.height());
}
m_timer->start(40);
}
//-----------------------------------------------------------------------------
@ -2018,7 +1814,6 @@ void PencilTestPopup::onNextName() {
}
std::wstring levelName = nameCreator->getNext();
updateLevelNameAndFrame(levelName);
}
@ -2125,33 +1920,158 @@ void PencilTestPopup::onColorTypeComboChanged(int index) {
//-----------------------------------------------------------------------------
void PencilTestPopup::onFrameCaptured(QImage& image) {
if (!m_videoWidget || image.isNull()) return;
void PencilTestPopup::onTimeout() { getWebcamImage(); }
//-----------------------------------------------------------------------------
int PencilTestPopup::translateIndex(int camIndex) {
// We are using Qt to get the camera info and supported resolutions, but
// we are using OpenCV to actually get the images.
// The camera index from OpenCV and from Qt don't always agree,
// So this checks the name against the correct index.
// Thanks to:
// https://elcharolin.wordpress.com/2017/08/28/webcam-capture-with-the-media-foundation-sdk/
// for the webcam enumeration here
std::wstring desc = m_cameraListCombo->currentText().toStdWString();
#define CLEAN_ATTRIBUTES() \
if (attributes) { \
attributes->Release(); \
attributes = NULL; \
} \
for (DWORD i = 0; i < count; i++) { \
if (&devices[i]) { \
devices[i]->Release(); \
devices[i] = NULL; \
} \
} \
CoTaskMemFree(devices); \
return camIndex;
HRESULT hr = S_OK;
// this is important!!
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
UINT32 count = 0;
IMFAttributes* attributes = NULL;
IMFActivate** devices = NULL;
if (FAILED(hr)) {
CLEAN_ATTRIBUTES()
}
// Create an attribute store to specify enumeration parameters.
hr = MFCreateAttributes(&attributes, 1);
if (FAILED(hr)) {
CLEAN_ATTRIBUTES()
}
// The attribute to be requested is devices that can capture video
hr = attributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
if (FAILED(hr)) {
CLEAN_ATTRIBUTES()
}
// Enummerate the video capture devices
hr = MFEnumDeviceSources(attributes, &devices, &count);
if (FAILED(hr)) {
CLEAN_ATTRIBUTES()
}
// if there are any available devices
if (count > 0) {
WCHAR* nameString = NULL;
// Get the human-friendly name of the device
UINT32 cchName;
for (int i = 0; i < count; i++) {
hr = devices[i]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
&nameString, &cchName);
if (nameString == desc) {
return i;
}
// devices[0]->ShutdownObject();
}
CoTaskMemFree(nameString);
}
// clean
CLEAN_ATTRIBUTES()
}
//-----------------------------------------------------------------------------
void PencilTestPopup::getWebcamImage() {
bool error = false;
cv::Mat imgOriginal;
cv::Mat imgCorrected;
if (m_cvWebcam.isOpened() == false) {
if (m_cameraListCombo->currentIndex() <= 0) return;
int camIndex = m_cameraListCombo->currentIndex() - 1;
if (!m_useDirectShow) {
// the webcam order obtained from Qt isn't always the same order as
// the one obtained from OpenCV without DirectShow
m_cvWebcam.open(translateIndex(camIndex));
} else {
m_cvWebcam.open(camIndex, cv::CAP_DSHOW);
}
// mjpg is used by many webcams
// opencv runs very slow on some webcams without it.
if (m_useMjpg) {
m_cvWebcam.set(cv::CAP_PROP_FOURCC,
cv::VideoWriter::fourcc('m', 'j', 'p', 'g'));
m_cvWebcam.set(cv::CAP_PROP_FOURCC,
cv::VideoWriter::fourcc('M', 'J', 'P', 'G'));
}
m_cvWebcam.set(cv::CAP_PROP_FRAME_WIDTH, m_resolution.width());
m_cvWebcam.set(cv::CAP_PROP_FRAME_HEIGHT, m_resolution.height());
if (!m_cvWebcam.isOpened()) error = true;
}
bool blnFrameReadSuccessfully =
m_cvWebcam.read(imgOriginal); // get next frame
if (!blnFrameReadSuccessfully ||
imgOriginal.empty()) { // if frame not read successfully
std::cout << "error: frame not read from webcam\n";
error = true; // print error message to std out
}
if (!error) {
cv::cvtColor(imgOriginal, imgCorrected, cv::COLOR_BGR2RGB);
onFrameCaptured(imgCorrected);
}
}
//-----------------------------------------------------------------------------
void PencilTestPopup::onFrameCaptured(cv::Mat& image) {
if (!m_videoWidget) return;
// capture the white BG
if (m_captureWhiteBGCue) {
m_whiteBGImg = image.copy();
;
m_whiteBGImg = image.clone();
m_captureWhiteBGCue = false;
m_bgReductionFld->setEnabled(true);
}
processImage(image);
m_videoWidget->setImage(image.copy());
QImage::Format format = (m_colorTypeCombo->currentIndex() == 0)
? QImage::Format_RGB888
: QImage::Format_Grayscale8;
QImage qimg(image.data, image.cols, image.rows, format);
m_videoWidget->setImage(qimg.copy());
if (m_captureCue) {
#ifndef MACOSX
m_currentCamera->stop();
#endif
m_captureCue = false;
bool scanBtoT =
m_videoWidget->videoSurface()->surfaceFormat().scanLineDirection() ==
QVideoSurfaceFormat::BottomToTop;
bool upsideDown = m_upsideDownCB->isChecked();
image = image.mirrored(upsideDown, upsideDown != scanBtoT);
if (importImage(image)) {
m_videoWidget->setPreviousImage(image.copy());
if (importImage(qimg)) {
m_videoWidget->setPreviousImage(qimg.copy());
if (Preferences::instance()->isShowFrameNumberWithLettersEnabled()) {
int f = m_frameNumberEdit->getValue();
if (f % 10 == 0) // next number
@ -2179,34 +2099,18 @@ void PencilTestPopup::onFrameCaptured(QImage& image) {
m_captureButton->setChecked(false);
onCaptureButtonClicked(false);
}
#ifndef MACOSX
m_currentCamera->start();
#endif
}
}
//-----------------------------------------------------------------------------
void PencilTestPopup::showEvent(QShowEvent* event) {
// m_timerId = startTimer(10);
// if there is another action of which "return" key is assigned as short cut
// key,
// then release the shortcut key temporary while the popup opens
QAction* action = CommandManager::instance()->getActionFromShortcut("Return");
if (action) action->setShortcut(QKeySequence(""));
connect(m_videoWidget->videoSurface(), SIGNAL(frameCaptured(QImage&)), this,
SLOT(onFrameCaptured(QImage&)));
// reload camera
if (m_currentCamera) {
if (m_currentCamera->state() == QCamera::UnloadedState)
m_currentCamera->load();
if (m_currentCamera->state() == QCamera::LoadedState)
m_currentCamera->start();
}
TSceneHandle* sceneHandle = TApp::instance()->getCurrentScene();
connect(sceneHandle, SIGNAL(sceneSwitched()), this, SLOT(onSceneSwitched()));
connect(sceneHandle, SIGNAL(castChanged()), this, SLOT(refreshFrameInfo()));
@ -2214,6 +2118,9 @@ void PencilTestPopup::showEvent(QShowEvent* event) {
bool tmp_alwaysOverwrite = m_alwaysOverwrite;
onSceneSwitched();
m_alwaysOverwrite = tmp_alwaysOverwrite;
onResolutionComboActivated();
m_videoWidget->computeTransform(m_resolution);
}
//-----------------------------------------------------------------------------
@ -2223,22 +2130,15 @@ void PencilTestPopup::hideEvent(QHideEvent* event) {
QAction* action = CommandManager::instance()->getActionFromShortcut("Return");
if (action) action->setShortcut(QKeySequence("Return"));
disconnect(m_videoWidget->videoSurface(), SIGNAL(frameCaptured(QImage&)),
this, SLOT(onFrameCaptured(QImage&)));
// stop interval timer if it is active
if (m_timerCB->isChecked() && m_captureButton->isChecked()) {
m_captureButton->setChecked(false);
onCaptureButtonClicked(false);
}
// release camera
if (m_currentCamera) {
if (m_currentCamera->state() == QCamera::ActiveState)
m_currentCamera->stop();
if (m_currentCamera->state() == QCamera::LoadedState)
m_currentCamera->unload();
}
m_cvWebcam.release();
if (m_timer->isActive()) m_timer->stop();
Dialog::hideEvent(event);
TSceneHandle* sceneHandle = TApp::instance()->getCurrentScene();
@ -2281,27 +2181,26 @@ bool PencilTestPopup::event(QEvent* event) {
}
//-----------------------------------------------------------------------------
void PencilTestPopup::processImage(QImage& image) {
/* "upside down" is not executed here. It will be done when capturing the
* image */
void PencilTestPopup::processImage(cv::Mat& image) {
// void PencilTestPopup::processImage(QImage& image) {
if (m_upsideDownCB->isChecked())
cv::flip(image, image, -1); // flip in both directions
// white bg reduction
if (!m_whiteBGImg.isNull() && m_bgReductionFld->getValue() != 0) {
if (!m_whiteBGImg.empty() && m_bgReductionFld->getValue() != 0)
bgReduction(image, m_whiteBGImg, m_bgReductionFld->getValue());
}
// obtain histogram AFTER bg reduction
m_camCapLevelControl->updateHistogram(image);
// change channel
if (m_colorTypeCombo->currentIndex() != 0)
cv::cvtColor(image, image, cv::COLOR_RGB2GRAY);
// color and grayscale mode
if (m_colorTypeCombo->currentIndex() != 2) {
int black, white;
float gamma;
m_camCapLevelControl->getValues(black, white, gamma);
if (black != 0 || white != 255 || gamma != 1.0)
onChange(image, black, white, gamma,
m_colorTypeCombo->currentIndex() != 0);
} else {
onChangeBW(image, m_camCapLevelControl->getThreshold());
}
if (m_colorTypeCombo->currentIndex() != 2)
m_camCapLevelControl->adjustLevel(image);
else
m_camCapLevelControl->binarize(image);
}
//-----------------------------------------------------------------------------

View file

@ -6,16 +6,17 @@
#include "toonzqt/dvdialog.h"
#include "toonzqt/lineedit.h"
#include "toonz/namebuilder.h"
#include "opencv2/opencv.hpp"
#include <QAbstractVideoSurface>
#include <QRunnable>
#include <QLineEdit>
// forward decl.
class QCamera;
class QCameraImageCapture;
class QComboBox;
class QLineEdit;
class QSlider;
class QCheckBox;
class QPushButton;
@ -36,70 +37,6 @@ class IntLineEdit;
class CameraCaptureLevelControl;
class ApplyLutTask : public QRunnable {
protected:
int m_fromY, m_toY;
QImage& m_img;
std::vector<int>& m_lut;
public:
ApplyLutTask(int from, int to, QImage& img, std::vector<int>& lut)
: m_fromY(from), m_toY(to), m_img(img), m_lut(lut) {}
private:
virtual void run() override;
};
class ApplyGrayLutTask : public ApplyLutTask {
public:
ApplyGrayLutTask(int from, int to, QImage& img, std::vector<int>& lut)
: ApplyLutTask(from, to, img, lut) {}
private:
void run() override;
};
//=============================================================================
// MyVideoSurface
//-----------------------------------------------------------------------------
class QVideoSurfaceFormat;
class MyVideoSurface : public QAbstractVideoSurface {
Q_OBJECT
public:
MyVideoSurface(QWidget* widget, QObject* parent = 0);
QList<QVideoFrame::PixelFormat> supportedPixelFormats(
QAbstractVideoBuffer::HandleType handleType =
QAbstractVideoBuffer::NoHandle) const;
bool isFormatSupported(const QVideoSurfaceFormat& format,
QVideoSurfaceFormat* similar) const;
bool start(const QVideoSurfaceFormat& format);
void stop();
bool present(const QVideoFrame& frame);
QRect videoRect() const { return m_targetRect; }
QRect sourceRect() const { return m_sourceRect; }
void updateVideoRect();
QTransform transform() { return m_S2V_Transform; }
private:
QWidget* m_widget;
QImage::Format m_imageFormat;
QRect m_targetRect;
QSize m_imageSize;
QRect m_sourceRect;
QVideoFrame m_currentFrame;
QTransform m_S2V_Transform; // surface to video transform
signals:
void frameCaptured(QImage& image);
};
//=============================================================================
// MyVideoWidget
//-----------------------------------------------------------------------------
@ -118,6 +55,8 @@ class MyVideoWidget : public QWidget {
QRect m_preSubCameraRect;
QPoint m_dragStartPos;
QRect m_targetRect;
QTransform m_S2V_Transform; // surface to video transform
enum SUBHANDLE {
HandleNone,
HandleFrame,
@ -134,15 +73,11 @@ class MyVideoWidget : public QWidget {
public:
MyVideoWidget(QWidget* parent = 0);
~MyVideoWidget();
void setImage(const QImage& image) {
m_image = image;
update();
}
QAbstractVideoSurface* videoSurface() const { return m_surface; }
QSize sizeHint() const;
void setShowOnionSkin(bool on) { m_showOnionSkin = on; }
void setOnionOpacity(int value) { m_onionOpacity = value; }
@ -156,6 +91,8 @@ public:
void setSubCameraSize(QSize size);
QRect subCameraRect() { return m_subCameraRect; }
void computeTransform(QSize imgSize);
protected:
void paintEvent(QPaintEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
@ -164,9 +101,6 @@ protected:
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
private:
MyVideoSurface* m_surface;
protected slots:
void onUpsideDownChecked(bool on) { m_upsideDown = on; }
@ -281,6 +215,12 @@ protected slots:
class PencilTestPopup : public DVGui::Dialog {
Q_OBJECT
QTimer* m_timer;
cv::VideoCapture m_cvWebcam;
QSize m_resolution;
//--------
QCamera* m_currentCamera;
QString m_deviceName;
MyVideoWidget* m_videoWidget;
@ -297,7 +237,7 @@ class PencilTestPopup : public DVGui::Dialog {
QTimer *m_captureTimer, *m_countdownTimer;
QImage m_whiteBGImg;
cv::Mat m_whiteBGImg;
// used only for Windows
QPushButton* m_captureFilterSettingsBtn;
@ -317,13 +257,23 @@ class PencilTestPopup : public DVGui::Dialog {
bool m_captureWhiteBGCue;
bool m_captureCue;
bool m_alwaysOverwrite = false;
bool m_useMjpg;
#ifdef _WIN32
bool m_useDirectShow;
#endif
void processImage(QImage& procImage);
void processImage(cv::Mat& procImage);
bool importImage(QImage image);
void setToNextNewLevel();
void updateLevelNameAndFrame(std::wstring levelName);
void getWebcamImage();
QMenu* createOptionsMenu();
int translateIndex(int camIndex);
public:
PencilTestPopup();
~PencilTestPopup();
@ -338,13 +288,13 @@ protected:
protected slots:
void refreshCameraList();
void onCameraListComboActivated(int index);
void onResolutionComboActivated(const QString&);
void onResolutionComboActivated();
void onFileFormatOptionButtonPressed();
void onLevelNameEdited();
void onNextName();
void onPreviousName();
void onColorTypeComboChanged(int index);
void onFrameCaptured(QImage& image);
void onFrameCaptured(cv::Mat& image);
void onCaptureWhiteBGButtonPressed();
void onOnionCBToggled(bool);
void onLoadImageButtonPressed();
@ -365,6 +315,7 @@ protected slots:
void onSubCameraResized(bool isDragging);
void onSubCameraSizeEdited();
void onTimeout();
public slots:
void openSaveInFolderPopup();
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,372 @@
#pragma once
#ifndef PENCILTESTPOPUP_H
#define PENCILTESTPOPUP_H
#include "toonzqt/dvdialog.h"
#include "toonzqt/lineedit.h"
#include "toonz/namebuilder.h"
#include <QAbstractVideoSurface>
#include <QRunnable>
// forward decl.
class QCamera;
class QCameraImageCapture;
class QComboBox;
class QLineEdit;
class QSlider;
class QCheckBox;
class QPushButton;
class QVideoFrame;
class QTimer;
class QIntValidator;
class QRegExpValidator;
class QPushButton;
#ifdef MACOSX
class QCameraViewfinder;
#endif
namespace DVGui {
class FileField;
class IntField;
class IntLineEdit;
} // namespace DVGui
class CameraCaptureLevelControl;
class ApplyLutTask : public QRunnable {
protected:
int m_fromY, m_toY;
QImage& m_img;
std::vector<int>& m_lut;
public:
ApplyLutTask(int from, int to, QImage& img, std::vector<int>& lut)
: m_fromY(from), m_toY(to), m_img(img), m_lut(lut) {}
private:
virtual void run() override;
};
class ApplyGrayLutTask : public ApplyLutTask {
public:
ApplyGrayLutTask(int from, int to, QImage& img, std::vector<int>& lut)
: ApplyLutTask(from, to, img, lut) {}
private:
void run() override;
};
//=============================================================================
// MyVideoSurface
//-----------------------------------------------------------------------------
class QVideoSurfaceFormat;
class MyVideoSurface : public QAbstractVideoSurface {
Q_OBJECT
public:
MyVideoSurface(QWidget* widget, QObject* parent = 0);
QList<QVideoFrame::PixelFormat> supportedPixelFormats(
QAbstractVideoBuffer::HandleType handleType =
QAbstractVideoBuffer::NoHandle) const;
bool isFormatSupported(const QVideoSurfaceFormat& format,
QVideoSurfaceFormat* similar) const;
bool start(const QVideoSurfaceFormat& format);
void stop();
bool present(const QVideoFrame& frame);
QRect videoRect() const { return m_targetRect; }
QRect sourceRect() const { return m_sourceRect; }
void updateVideoRect();
QTransform transform() { return m_S2V_Transform; }
private:
QWidget* m_widget;
QImage::Format m_imageFormat;
QRect m_targetRect;
QSize m_imageSize;
QRect m_sourceRect;
QVideoFrame m_currentFrame;
QTransform m_S2V_Transform; // surface to video transform
signals:
void frameCaptured(QImage& image);
};
//=============================================================================
// MyVideoWidget
//-----------------------------------------------------------------------------
class MyVideoWidget : public QWidget {
Q_OBJECT
QImage m_image;
QImage m_previousImage;
int m_countDownTime;
bool m_showOnionSkin;
int m_onionOpacity;
bool m_upsideDown;
QRect m_subCameraRect;
QRect m_preSubCameraRect;
QPoint m_dragStartPos;
enum SUBHANDLE {
HandleNone,
HandleFrame,
HandleTopLeft,
HandleTopRight,
HandleBottomLeft,
HandleBottomRight,
HandleLeft,
HandleTop,
HandleRight,
HandleBottom
} m_activeSubHandle = HandleNone;
void drawSubCamera(QPainter&);
public:
MyVideoWidget(QWidget* parent = 0);
~MyVideoWidget();
void setImage(const QImage& image) {
m_image = image;
update();
}
QAbstractVideoSurface* videoSurface() const { return m_surface; }
QSize sizeHint() const;
void setShowOnionSkin(bool on) { m_showOnionSkin = on; }
void setOnionOpacity(int value) { m_onionOpacity = value; }
void setPreviousImage(QImage prevImage) { m_previousImage = prevImage; }
void showCountDownTime(int time) {
m_countDownTime = time;
repaint();
}
void setSubCameraSize(QSize size);
QRect subCameraRect() { return m_subCameraRect; }
protected:
void paintEvent(QPaintEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
private:
MyVideoSurface* m_surface;
protected slots:
void onUpsideDownChecked(bool on) { m_upsideDown = on; }
signals:
void startCamera();
void stopCamera();
void subCameraResized(bool isDragging);
};
//=============================================================================
// FrameNumberLineEdit
// a special Line Edit which accepts imputting alphabets if the preference
// option
// "Show ABC Appendix to the Frame Number in Xsheet Cell" is active.
//-----------------------------------------------------------------------------
class FrameNumberLineEdit : public DVGui::LineEdit {
Q_OBJECT
/* having two validators and switch them according to the preferences*/
QIntValidator* m_intValidator;
QRegExpValidator* m_regexpValidator;
void updateValidator();
QString m_textOnFocusIn;
public:
FrameNumberLineEdit(QWidget* parent = 0, int value = 1);
~FrameNumberLineEdit() {}
/*! Set text in field to \b value. */
void setValue(int value);
/*! Return an integer with text field value. */
int getValue();
protected:
/*! If focus is lost and current text value is out of range emit signal
\b editingFinished.*/
void focusInEvent(QFocusEvent*) override;
void focusOutEvent(QFocusEvent*) override;
void showEvent(QShowEvent* event) override { updateValidator(); }
};
//=============================================================================
class LevelNameLineEdit : public QLineEdit {
Q_OBJECT
QString m_textOnFocusIn;
public:
LevelNameLineEdit(QWidget* parent = 0);
protected:
void focusInEvent(QFocusEvent* e);
protected slots:
void onEditingFinished();
signals:
void levelNameEdited();
};
//=============================================================================
// FlexibleNameCreator
// Inherits NameCreator, added function for obtaining the previous name and
// setting the current name.
class FlexibleNameCreator final : public NameCreator {
public:
FlexibleNameCreator() {}
std::wstring getPrevious();
bool setCurrent(std::wstring name);
};
//=============================================================================
// PencilTestSaveInFolderPopup
//-----------------------------------------------------------------------------
class PencilTestSaveInFolderPopup : public DVGui::Dialog {
Q_OBJECT
DVGui::FileField* m_parentFolderField;
QLineEdit *m_projectField, *m_episodeField, *m_sequenceField, *m_sceneField,
*m_subFolderNameField;
QCheckBox *m_subFolderCB, *m_autoSubNameCB, *m_createSceneInFolderCB;
QComboBox* m_subNameFormatCombo;
void createSceneInFolder();
public:
PencilTestSaveInFolderPopup(QWidget* parent = 0);
QString getPath();
QString getParentPath();
void updateParentFolder();
protected:
void showEvent(QShowEvent* event);
protected slots:
void updateSubFolderName();
void onAutoSubNameCBClicked(bool);
void onShowPopupOnLaunchCBClicked(bool);
void onCreateSceneInFolderCBClicked(bool);
void onSetAsDefaultBtnPressed();
void onOkPressed();
};
//=============================================================================
// PencilTestPopup
//-----------------------------------------------------------------------------
class PencilTestPopup : public DVGui::Dialog {
Q_OBJECT
QCamera* m_currentCamera;
QString m_deviceName;
MyVideoWidget* m_videoWidget;
QComboBox *m_cameraListCombo, *m_resolutionCombo, *m_fileTypeCombo,
*m_colorTypeCombo;
LevelNameLineEdit* m_levelNameEdit;
QCheckBox *m_upsideDownCB, *m_onionSkinCB, *m_saveOnCaptureCB, *m_timerCB;
QPushButton *m_fileFormatOptionButton, *m_captureWhiteBGButton,
*m_captureButton, *m_loadImageButton;
DVGui::FileField* m_saveInFileFld;
FrameNumberLineEdit* m_frameNumberEdit;
DVGui::IntField *m_bgReductionFld, *m_onionOpacityFld, *m_timerIntervalFld;
QTimer *m_captureTimer, *m_countdownTimer;
QImage m_whiteBGImg;
// used only for Windows
QPushButton* m_captureFilterSettingsBtn;
PencilTestSaveInFolderPopup* m_saveInFolderPopup;
CameraCaptureLevelControl* m_camCapLevelControl;
QLabel* m_frameInfoLabel;
QToolButton* m_previousLevelButton;
QPushButton* m_subcameraButton;
DVGui::IntLineEdit *m_subWidthFld, *m_subHeightFld;
QSize m_allowedCameraSize;
bool m_captureWhiteBGCue;
bool m_captureCue;
bool m_alwaysOverwrite = false;
void processImage(QImage& procImage);
bool importImage(QImage image);
void setToNextNewLevel();
void updateLevelNameAndFrame(std::wstring levelName);
public:
PencilTestPopup();
~PencilTestPopup();
protected:
void showEvent(QShowEvent* event);
void hideEvent(QHideEvent* event);
void keyPressEvent(QKeyEvent* event);
bool event(QEvent* e) override;
protected slots:
void refreshCameraList();
void onCameraListComboActivated(int index);
void onResolutionComboActivated(const QString&);
void onFileFormatOptionButtonPressed();
void onLevelNameEdited();
void onNextName();
void onPreviousName();
void onColorTypeComboChanged(int index);
void onFrameCaptured(QImage& image);
void onCaptureWhiteBGButtonPressed();
void onOnionCBToggled(bool);
void onLoadImageButtonPressed();
void onOnionOpacityFldEdited();
void onTimerCBToggled(bool);
void onCaptureTimerTimeout();
void onCountDown();
void onCaptureButtonClicked(bool);
void onCaptureFilterSettingsBtnPressed();
void refreshFrameInfo();
void onSaveInPathEdited();
void onSceneSwitched();
void onSubCameraToggled(bool);
void onSubCameraResized(bool isDragging);
void onSubCameraSizeEdited();
public slots:
void openSaveInFolderPopup();
};
#endif