Einfacher grafischer Editor mit OpenCV

In diesem Artikel erkläre ich Ihnen, wie Sie mit der opencv Computer Vision-Bibliothek schnell und einfach einen Bildeditor in C ++ schreiben können. Implementierte Effekte wie Sättigung, Belichtung, Schärfe, Kontrast und andere. Keine Magie!

Bild

Achtung! Unter einer Katze gibt es viele Grafiken und Code.

Also lasst uns anfangen ...

Sättigung


Zutaten:
- HSV-Farbsystem,
-
Funktion „Teilen“ , - Funktion „Zusammenführen“ von Ebenen.

Um die Sättigung zu ändern, wird das Bild in das HSV-Farbsystem konvertiert und in Ebenen unterteilt. Zu den Werten der Ebene "Sature" wird ein Schritt hinzugefügt. Ebenen werden zusammengeführt. Alles ist einfach:

Sättigung
void CImageEditor::Sature(int step)
{
	try
	{
		std::vector hsv;
		cv::cvtColor(*m_imgEdit, *m_imgEdit, cv::ColorConversionCodes::COLOR_RGB2HSV_FULL);
		cv::split(*m_imgEdit, hsv);
		hsv[1] += step * 5;
		cv::merge(hsv, *m_imgEdit);
		cv::cvtColor(*m_imgEdit, *m_imgEdit, cv::ColorConversionCodes::COLOR_HSV2RGB_FULL);
	}
	catch (Exception ex)
	{
	}
}


ZuNachher

Ausstellung


Inhaltsstoffe:
- HSV-Farbsystem,
- Split-, Merge-Funktion sowie LUT-
Histogramm-Konvertierungsfunktion , - Histogramm transformiert mit x + sin (x * 0,01255) * Step * 10-Funktion,
- Schutz gegen Überlaufen der Histogramm-Bytewerte .
Wie bei der Sättigung wird das Bild in HSV konvertiert und in Ebenen unterteilt. Für die Ebene „Wert“ führen wir die Konvertierung mit dem Histogramm durch, das durch die Funktion i + sin (i * 0,01255) * step * 10 definiert ist. Vergessen Sie dabei nicht, uns vor einem Überlauf der Bytenummer zu schützen.
Ausstellung
void CImageEditor::Expo(int step)
{
	try
	{
		std::vector hsv;
		cv::cvtColor(*m_imgEdit, *m_imgEdit, cv::ColorConversionCodes::COLOR_RGB2HSV_FULL);
		Mat lut = GetGammaExpo(step);
		cv::split(*m_imgEdit, hsv);
		cv::LUT(hsv[2], lut, hsv[2]);
		cv::merge(hsv, *m_imgEdit);
		cv::cvtColor(*m_imgEdit, *m_imgEdit, cv::ColorConversionCodes::COLOR_HSV2RGB_FULL);
	}
	catch (Exception ex)
	{
	}
}
cv::Mat CImageEditor::GetGammaExpo(int step)
{
	Mat result(1, 256, CV_8UC1);
	uchar* p = result.data;
	for (int i = 0; i < 256; i++)
	{
		p[i] = AddDoubleToByte(i, std::sin(i * 0.01255) * step * 10);
	}
	return result;
}
byte CImageEditor::AddDoubleToByte(byte bt, double d)
{
	byte result = bt;
	if (double(result) + d > 255)
		result = 255;
	else if (double(result) + d < 0)
		result = 0;
	else
	{
		result += d;
	}
	return result;
}


ZuNachher
Funktionsgraph x + sin (x * 0,01255) * step * 10
Bild
Die Funktion wirkt sich hauptsächlich auf die Mitte des Bereichs aus.

Hue


Zutaten:
- das RGB - Farbsystem,
- die Funktion «Split», «merge» und «die LUT»,
- Histogramm, transformiert Funktion der Exposition gegenüber den roten, grünen und blauen Kanälen
- Überfüllsicherung Histogrammwert.

Der Farbtonparameter kennzeichnet das Vorhandensein von Grün und Lila im Bild. Im RGB-Farbsystem können Sie die grüne Ebene steuern. Beachten Sie jedoch, dass Sie den Helligkeitsabfall der beiden anderen Ebenen ausgleichen müssen. Um die roten und blauen Schichten umzuwandeln, wird die positive Belichtungs-Gammafunktion für das Grün-Negativ verwendet.

Hue
void CImageEditor::Hue(int step)
{
	try
	{
		std::vector rgb;
		Mat lut0 = GetGammaExpo(step), lut1 = GetGammaExpo(-step), lut2 = GetGammaExpo(step);
		cv::split(*m_imgEdit, rgb);
		LUT(rgb[0], lut0, rgb[0]);
		LUT(rgb[1], lut1, rgb[1]);
		LUT(rgb[2], lut2, rgb[2]);
		cv::merge(rgb, *m_imgEdit);
	}
	catch (Exception ex)
	{
	}
}


ZuNachher

Farbtemperatur


Zutaten: wie im Schatten, jedoch sind die Histogramme für Rot und Grün positiv und für die blaue Schicht doppelt negativ.

Die Farbtemperatur kennzeichnet das Vorhandensein von gelben und blauen Farben im Bild. Also werden wir das Blau "verdrehen".

Farbtemperatur
void CImageEditor::Temperature(int step)
{
	try
	{
		std::vector rgb;
		Mat lut0 = GetGammaExpo(-step*2), lut1 = GetGammaExpo(step), lut2 = GetGammaExpo(step);
		cv::split(*m_imgEdit, rgb);
		LUT(rgb[0], lut0, rgb[0]);
		LUT(rgb[1], lut1, rgb[1]);
		LUT(rgb[2], lut2, rgb[2]);
		cv::merge(rgb, *m_imgEdit);
	}
	catch (Exception ex)
	{
	}
}


ZuNachher

Licht und Schatten


Zutaten:
- das HSV - Farbsystem,
- die Funktion «die Spaltung», «Die Zusammenführung», «die LUT»,
- Histogramm Schatten, transformierte Funktion (0.36811145 * ein e) ^ (- (x ^ 1,7)) * * 0,2x Schritt,
- Histogramm Lichter konvertiert durch die Funktion (0.36811145 * e) ^ (- (256 - x) ^ 1.7) * 0.2 (256-x) * step,
- Schutz vor Überlauf der Histogrammwerte.

Der Parameter "Licht" kennzeichnet die Helligkeit der hellen Bereiche des Bildes und der Parameter "Schatten" kennzeichnet die Helligkeit der dunklen Bereiche. Wir werden den Helligkeitskanal transformieren.

"alt =" image "/>

In der Grafik wird die Schattenkonvertierungsfunktion durch die rote Linie und die Lichtfunktion durch die grüne Linie angezeigt.

Licht und Schatten
void CImageEditor::White(int step)
{
	try
	{
		std::vector hsv;
		cv::cvtColor(*m_imgEdit, *m_imgEdit, cv::ColorConversionCodes::COLOR_RGB2HSV_FULL);
		cv::split(*m_imgEdit, hsv);
		Mat lut = GetGammaLightShadow(step, true);
		LUT(hsv[2], lut, hsv[2]);
		cv::merge(hsv, *m_imgEdit);
		cv::cvtColor(*m_imgEdit, *m_imgEdit, cv::ColorConversionCodes::COLOR_HSV2RGB_FULL);
	}
	catch (Exception ex)
	{
		AfxMessageBox(CString(CStringA(ex.msg.begin())));
		throw;
	}
}
void CImageEditor::Shadow(int step)
{
	try
	{
		std::vector hsv;
		cv::cvtColor(*m_imgEdit, *m_imgEdit, cv::ColorConversionCodes::COLOR_RGB2HSV_FULL);
		cv::split(*m_imgEdit, hsv);
		Mat lut = GetGammaLightShadow(step, false);
		LUT(hsv[2], lut, hsv[2]);
		cv::merge(hsv, *m_imgEdit);
		cv::cvtColor(*m_imgEdit, *m_imgEdit, cv::ColorConversionCodes::COLOR_HSV2RGB_FULL);
	}
	catch (Exception ex)
	{
		AfxMessageBox(CString(CStringA(ex.msg.begin())));
		throw;
	}
}
Mat CImageEditor::GetGammaLightShadow(int step, bool reverse)
{
	Mat result(1, 256, CV_8UC1);
	for (int i = 0; i < 256; i++)
	{
		*(result.data + i) = AddDoubleToByte(i, std::pow(0.36811145*M_E, 
			-std::pow(abs((reverse ? 256 : 0) - i), 1.7))*0.2*step*abs((reverse ? 256 : 0) - i));
	}
	return result;
}


Das lichtSchatten

Kontrast


Zutaten:
- RGB-Farbsystem,
- Funktion "Teilen", "Zusammenführen", "LUT",
- Kontraststufe "(100 + Schritt) / 100",
- Kontrasthistogramm nach Formel ((x / 255 - 0,5) * constrastLevel + 0.5) * 255.

Der Kontrast wird durch den Helligkeitsunterschied bestimmt. Das heißt Um den Kontrast zu erhöhen, müssen wir den Helligkeitsbereich von der Mitte bis zu den Rändern erweitern. Die Konvertierung wird für alle Ebenen durchgeführt.

Kontrast
void CImageEditor::Contrast(int step)
{
	try
	{
		std::vector rgb;
		cv::split(*m_imgEdit, rgb);
		Mat lut(1, 256, CV_8UC1);
		double contrastLevel = double(100 + step) / 100;
		uchar* p = lut.data;
		double d;
		for (int i = 0; i < 256; i++)
		{
			d = ((double(i) / 255 - 0.5)*contrastLevel + 0.5) * 255;
			if (d > 255)
				d = 255;
			if (d < 0)
				d = 0;
			p[i] = d;
		}
		LUT(rgb[0], lut, rgb[0]);
		LUT(rgb[1], lut, rgb[1]);
		LUT(rgb[2], lut, rgb[2]);
		cv::merge(rgb, *m_imgEdit);
	}
	catch (Exception ex)
	{
		AfxMessageBox(CString(CStringA(ex.msg.begin())));
		throw;
	}
}


Bild

Die rote Linie ist kontrastreich, die grüne Linie ist schwach.

ZuNachher

Schärfe


Zutaten:
- Unschärfefunktion "Unschärfe",
- Faltungsmatrix mit berechneten Koeffizienten,
- Umrechnungsfunktion der Faltungsmatrix "filter2D",
- Kopie des Bildes.

Die Schärfe (sharpness) wird durch die Auswahl der einzelnen Elemente und deren Konturen bestimmt. Die Umkehrung der Schärfe ist die Unschärfe.
In opencv verwenden wir die Unschärfefunktion, um das Bild zu verwischen. Dabei werden das Quellbild, das Ausgabebild und die Größe der Unschärfematrix als Parameter verwendet. Die Unschärfestärke hängt von der Größe der Unschärfematrix ab. Diese Größe muss gerade sein, damit die Mitte der Matrix nicht manuell angezeigt wird.

Die Klarheit in opencv lässt sich mit einer Faltungsmatrix am einfachsten erhöhen, wobei hierfür eine spezielle Matrix verwendet wird. Die filter2D-Funktion, die das Originalbild, das resultierende Bild, die Anzahl der Bits pro Faltungsmatrixwert und die Faltungsmatrix empfängt, führt die Konvertierung direkt durch. Also, wie die Up / Down-Methode aussehen wird.

Schärfe
void CImageEditor::Clarity(int step)
{
	try
	{
		if (step < 0)
		{
			cv::blur(*m_imgEdit, *m_imgEdit, cv::Size(-step * 2 + 1, -step * 2 + 1));
		}
		else
		{
			Mat dst = m_imgEdit->clone();
			float matr[9] {
				-0.0375 - 0.05*step, -0.0375 - 0.05*step, -0.0375 - 0.05*step,
				-0.0375 - 0.05*step, 1.3 + 0.4*step, -0.0375 - 0.05*step,
				-0.0375 - 0.05*step, -0.0375 - 0.05*step, -0.0375 - 0.05*step
			};
			Mat kernel_matrix = Mat(3, 3, CV_32FC1, &matr);
			cv::filter2D(*m_imgEdit, dst, 32, kernel_matrix);
			m_imgEdit = make_shared(dst);
		}
	}
	catch (Exception ex)
	{
		AfxMessageBox(CString(CStringA(ex.msg.begin())));
		throw;
	}
}


ZuNachher

Zusammenfassung


Fast keine Magie. Nun, magische Zahlen werden empirisch gefunden, sodass Sie stattdessen Ihre eigenen verwenden können, die am besten geeignet sind.
Link zur Demo-Anwendung.

Jetzt auch beliebt: