Created
April 18, 2013 19:13
-
-
Save inplacesapp/5415458 to your computer and use it in GitHub Desktop.
Sample code for overlay image blend mode, FilmThick image filter written at InPlaces Inc using OpemCV
https://inplac.es
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** Sample code for overlay, FilmThick image filter | |
written at InPlaces Inc | |
https://inplac.es | |
**/ | |
#include "stdafx.h" | |
#include <cv.h> | |
#include <cxcore.h> | |
#include <highgui.h> | |
using namespace cv; | |
using namespace std; | |
cv::Mat overlay(cv::Mat src, cv::Mat over); | |
cv::Mat film_thick (cv::Mat src); | |
double calcOverlayBlendValue (double sourceVal, double overlayVal, double minVal, double range); | |
//Main function of the console application | |
int _tmain(int argc, _TCHAR* argv[]) | |
{ | |
// Open the original image as src and | |
//save the filter version as dst | |
cv::Mat src, dst; | |
src = cv::imread("cafe3.jpg"); | |
//applying the image filter | |
dst = film_thick(src); | |
cv::imshow( "FilmThick filter", dst); | |
cv::waitKey(); | |
return 0; | |
} | |
/** | |
The film thick filter uses two blend | |
modes: Add and Overlay | |
**/ | |
cv::Mat film_thick (cv::Mat src) | |
{ | |
cv::Mat colorTone; | |
colorTone = cv::imread("film_thick_body.jpg"); | |
src = overlay(src, src); | |
addWeighted(src, 1.0, colorTone, 0.22, 0, src); | |
return (src); | |
} | |
/** | |
Overlay makes darker pixels darker and lighter pixels lighter. | |
src is the original image, over is the layer to be overlayed on src | |
Technically the algorithm happens to be | |
return (v2 < 128) ? (2 * v1 * v2 / 255):(255 - 2 * (255 - v1) * (255 - v2) / 255); | |
V1 being the intensity of any given pixel from the source | |
V2 being the intensity of any given pixel from the overlaying image | |
But, you will find that the math I have used is quite different in | |
order to achieve smooth results just like you would using Adobe Photoshop or Corel PhotoPaint | |
**/ | |
cv::Mat overlay(cv::Mat src, cv::Mat over) | |
{ | |
int x = src.cols; | |
int y = src.rows; | |
double minValB, maxValB, rangeB; | |
double minValG, maxValG, rangeG; | |
double minValR, maxValR, rangeR; | |
//split your source BGR image into the 3 channels it's made of | |
std::vector<cv::Mat> bgrChannels(3); | |
cv::split(src, bgrChannels); | |
//This is not a part of the overlay algorithm | |
//But I found out that the images look much better | |
//to our eye if you equalize the histogram on all the 3 channels | |
//NOTE 0 is the Blue channel, 1 is Green & 2 is Red | |
equalizeHist(bgrChannels[0], bgrChannels[0]); | |
equalizeHist(bgrChannels[1], bgrChannels[1]); | |
equalizeHist(bgrChannels[2], bgrChannels[2]); | |
//Find the maximum and minimum intensity on each channel | |
//will be used as part of the overlay algorithm | |
minMaxLoc(bgrChannels[0], &minValB, &maxValB); | |
rangeB = maxValB - minValB; | |
minMaxLoc(bgrChannels[1], &minValG, &maxValG); | |
rangeG = maxValG - minValG; | |
minMaxLoc(bgrChannels[2], &minValR, &maxValR); | |
rangeR = maxValR - minValR; | |
//Reset the values if things go beyond | |
//the allowed range 0 to 255 | |
if (rangeB <= 0) | |
{ | |
rangeB = 255; | |
minValB = 0; | |
} | |
if (rangeG <= 0) | |
{ | |
rangeG = 255; | |
minValG = 0; | |
} | |
if (rangeR <= 0) | |
{ | |
rangeR = 255; | |
minValR = 0; | |
} | |
//We iterate over each (pixel) column and row | |
//find the new overlay blend value for each pixel and replace it | |
for (int j = 0; j < y; j++) | |
{ | |
for (int i = 0; i < x; i ++) | |
{ | |
//Get the overlay blend value for all 3 channels BGR | |
src.at<cv::Vec3b>(j,i)[0] = calcOverlayBlendValue (double(src.at<cv::Vec3b>(j,i)[0]), double(over.at<cv::Vec3b>(j,i)[0]), minValB, rangeB); | |
src.at<cv::Vec3b>(j,i)[1] = calcOverlayBlendValue (double(src.at<cv::Vec3b>(j,i)[1]), double(over.at<cv::Vec3b>(j,i)[1]), minValG, rangeG); | |
src.at<cv::Vec3b>(j,i)[2] = calcOverlayBlendValue (double(src.at<cv::Vec3b>(j,i)[2]), double(over.at<cv::Vec3b>(j,i)[2]), minValR, rangeR); | |
} | |
} | |
return (src); | |
} | |
/** | |
The math for overlay blend mode. | |
sourceVal is the intensity at a given point on your source image | |
overlayVal is the intensity at the same point on the image to be overlayed | |
**/ | |
double calcOverlayBlendValue (double sourceVal, double overlayVal, double minVal, double range) | |
{ | |
if (sourceVal < 128) | |
{ | |
return 2.0 * ((sourceVal - minVal) * (overlayVal - minVal) / range) + minVal; | |
} | |
else if (sourceVal == 128) | |
{ | |
return 255 - 2.0 * ((255 - sourceVal) * (255 - overlayVal) / 255); | |
} | |
else | |
{ | |
return range - 2.0 * ((range - (sourceVal - minVal)) * (range - (overlayVal - minVal)) / range) + minVal; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment