Skip to content

Instantly share code, notes, and snippets.

@inplacesapp
Created April 18, 2013 19:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save inplacesapp/5415458 to your computer and use it in GitHub Desktop.
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
/** 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