Java tutorial
/******************************************************************************* * Copyright (c) 2015 Dominik Brmer. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html ******************************************************************************/ package com.blogspot.thedsweb.engine; import java.util.concurrent.Semaphore; import java.util.logging.Level; import org.opencv.core.Core; import org.opencv.core.Mat; import org.opencv.core.Scalar; import org.opencv.highgui.VideoCapture; import org.opencv.imgproc.Imgproc; import com.blogspot.thedsweb.main.Debug; import com.blogspot.thedsweb.util.Config; import com.blogspot.thedsweb.util.Files; import com.blogspot.thedsweb.util.Initialization; public class Brightness extends Files { private int min; private int max; private int last; private int current; private final int maxLevel; private double[] referenceValue; private boolean face; private final Threshold threshold; private final Face probability; private final Thread fade; private final Fade transition; private boolean runDaemon; private boolean firstRun; private boolean backlit; private final BacklightDevice blDev; private final Semaphore semaphoreFade = new Semaphore(0, false); private final Semaphore semaphoreSet = new Semaphore(1, false); static { // Load native library of OpenCV System.loadLibrary(Core.NATIVE_LIBRARY_NAME); } public Brightness(Config config) { // Create threshold object to calculate threshold threshold = new Threshold(config); // Create face object to track faces probability = new Face(config); // Create fade object for brightness changes transition = new Fade(); // Daemon to fade between screen brightness values fade = new Thread(new FadeDaemon(), "fadeDaemon"); fade.setDaemon(true); // Initialize Files if they dosen't exist Initialization.Files(); final int[] arr = new int[4]; readStatus(arr); min = arr[0]; max = arr[1]; last = arr[2]; if (arr[3] > 0) { maxLevel = arr[3]; } else { maxLevel = 15; } blDev = new BacklightDevice(); blDev.setRef(min, max); referenceValue = blDev.getRef(); firstRun = true; backlit = false; current = captureAndCalculate(); } // Start Daemon public void start() { runDaemon = true; fade.start(); } // Cancel Daemon public void interrupt() { runDaemon = false; fade.interrupt(); try { fade.join(); } catch (final InterruptedException e) { } } // Return the current Brightness public int getCurrent() { return current; } // Get the current Brightness public void setCurrent() { current = captureAndCalculate(); } // Return the last set Brightness public int getLast() { return last; } // Return if Faces are detected public boolean getFace() { return face; } // Control the difference between last and // current Brightness value return true when // it is too high private boolean control(int current) { return current > last << 1 || current << 1 < last; } // Return the ideal brightness value // Method is based on Java's build in binarySearch private int brightnessValue(double[] arr, int key) { int low = 0; int high = arr.length - 1; final int LENGTH = arr.length - 1; while (low <= high) { final int mid = (low + high) >>> 1; final double midVal = arr[mid]; double aMidVal = 255; if (mid != LENGTH) { aMidVal = arr[mid + 1]; } if (key > aMidVal) { low = mid + 1; } else if (key < midVal) { high = mid - 1; } else { Debug.LOG.log(Level.CONFIG, "Set " + mid + " as brightness value."); return mid; } } Debug.LOG.log(Level.CONFIG, "Set " + LENGTH + " as brightness value."); return LENGTH; } private Mat capture10thFrame(VideoCapture cap, Mat frame) { // Grab and decode the 10th frame of the camera for (int i = 0; i <= 10; i++) { cap.grab(); } cap.retrieve(frame); return frame; } private int captureAndCalculate() { final Mat frame = new Mat(); // Initialize video capturing and set a small image size final VideoCapture cap = new VideoCapture(0); cap.set(3, 160); cap.set(4, 120); int meanValue = current; face = true; // Return current as meanValue if camera start fail if (!cap.isOpened()) { return meanValue; } capture10thFrame(cap, frame); // Calculate mean value of frame meanValue = meanCalculation(frame); // If the current value change in a extreme way // set it again if (control(meanValue)) { capture10thFrame(cap, frame); // Re-Calculate mean value of frame meanValue = meanCalculation(frame); } // Set true if someone's Face is detected face = probability.detectFace(frame, meanValue); // Release the camera for other programs cap.release(); return meanValue; } private int meanCalculation(Mat rgb) { // Convert RGB to YcrCb for easier luminance calculation final Mat yCrCb = new Mat(); Imgproc.cvtColor(rgb, yCrCb, Imgproc.COLOR_RGB2YCrCb); // Calculate luminance final Scalar mainMean = Core.mean(yCrCb); int meanLumaValue = (int) mainMean.val[0]; // Test if backlit conditions are true if (!firstRun && backlitDetection(yCrCb, mainMean, meanLumaValue)) { Debug.LOG.log(Level.CONFIG, "Backlit detected."); backlit = true; if (meanLumaValue < current) { meanLumaValue = current; } } else { backlit = false; } // Set the first run parameter to false so that the next time the mean // calculation method is called the backlit detection is also running if (firstRun) { firstRun = false; } // If the frame is completely black or white the camera must already in // use by another program if (meanLumaValue == 0) { meanLumaValue = current; } return meanLumaValue; } private boolean checkChroma(int[] values, int mean) { // Check how similar the chroma values of all frame tiles are. By // calculate the maximum number of similar tiles. int max = 0; for (final int value : values) { int count = 0; for (final int valB : values) { if (valB + 5 > value && valB - 5 < value) { count++; } } if (max < count) { max = count; } } // Return true if more than the half of the tiles have a similar chroma // value. return max > 4; } private boolean backlitDetection(Mat yCrCb, Scalar mainMean, int mainMeanLumaValue) { // Save the mean brightness and chroma value of the whole frame. Plus // the width and height of the frame. final int mainMeanChromaValue = (int) mainMean.val[2]; final int PARTS = 8; final int w = yCrCb.width(); final int h = yCrCb.height(); final int wStep = w >> 2; final int hStep = h >> 1; // Separate the image into 8 equal parts. final Mat[] tiles = new Mat[PARTS]; tiles[0] = yCrCb.submat(0, hStep, 0, wStep); tiles[1] = yCrCb.submat(0, hStep, wStep, wStep * 2); tiles[2] = yCrCb.submat(hStep, h, 0, wStep); tiles[3] = yCrCb.submat(hStep, h, wStep, wStep * 2); tiles[4] = yCrCb.submat(0, hStep, wStep * 2, wStep * 3); tiles[5] = yCrCb.submat(0, hStep, wStep * 2, w); tiles[6] = yCrCb.submat(hStep, h, wStep * 2, wStep * 3); tiles[7] = yCrCb.submat(hStep, h, wStep * 2, w); // Calculate the mean value of all parts. final Scalar[] tileMean = new Scalar[PARTS]; for (int i = 0; i < tileMean.length; i++) { tileMean[i] = Core.mean(tiles[i]); } // Save the mean brightness and chroma of all parts. final int[] tileMeanLuma = new int[PARTS]; final int[] tileMeanChroma = new int[PARTS]; // Save min and max value in container MinMaxContainer con = null; for (int j = 0; j < tileMean.length; j++) { tileMeanLuma[j] = (int) tileMean[j].val[0]; if (j == 0) { con = new MinMaxContainer(tileMeanLuma[j]); } else { con.add(tileMeanLuma[j]); } tileMeanChroma[j] = (int) tileMean[j].val[2]; } // Get the highest and lowest brightness value of all frame tiles. final int min = con.getMin(); final int max = con.getMax() >> 1; // Check if their is a consistent chroma distribution and a brightness // spike to detect backlit conditions. if (checkChroma(tileMeanChroma, mainMeanChromaValue) && min < max) { return true; } return false; } public void setBrightness() { // Threshold values to prevent flickering final int darkeningThreshold = threshold.getDarkeningThreshold(last); final int brighteningThreshold = threshold.getBrighteningThreshold(last); // Set the display brightness value relative to reference if (current <= darkeningThreshold || current > brighteningThreshold) { // Check if the new current value is also the new // minimum or maximum value if (current < min) { min = current; blDev.setRef(min, max); referenceValue = blDev.getRef(); } if (current > max) { max = current; blDev.setRef(min, max); referenceValue = blDev.getRef(); } // Save last current value last = current; // Set Brightness try { semaphoreSet.acquire(); } catch (final InterruptedException e) { } transition.setValue(brightnessValue(referenceValue, current)); semaphoreFade.release(); // Save all values saveValue(current, min, max); } } public void resetBrightnessAfterReboot() { // Set Brightness blDev.set(brightnessValue(referenceValue, last)); } public int calibrateBrightness() { firstRun = false; // Get new current value setCurrent(); // Return failure value on backlit if (backlit) { return -1; } // Check if the new current value is also the new // minimum or maximum value if (current < min) { min = current; } if (current > max) { max = current; } // Save all values saveValue(current, min, max); return current; } public void darkenBrightness() { last = 0; try { semaphoreSet.acquire(); } catch (final InterruptedException e) { } transition.setValue(0); semaphoreFade.release(); } class FadeDaemon implements Runnable { @Override public void run() { int step = 500 / maxLevel; // Ensure that there is a min. laziness of 1 millisecond if (step == 0) { step = 1; } while (runDaemon) { try { semaphoreFade.acquire(); } catch (final InterruptedException e) { return; } int count = 0; // Set dynamically the brightness level while (blDev.get() != transition.getValue() && count < maxLevel) { int i = blDev.get(); if (i > transition.getValue()) { i--; } else { i++; } blDev.set(i); count++; try { Thread.sleep(step); } catch (final InterruptedException e) { return; } } semaphoreSet.release(); } } } }