package org.jcodec;
import static org.jcodec.common.tools.MathUtil.abs;
import static org.jcodec.common.tools.MathUtil.clipMax;
import static org.junit.Assert.assertTrue;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.Arrays;
import javax.imageio.ImageIO;
import org.jcodec.api.FrameGrab8Bit;
import org.jcodec.api.JCodecException;
import org.jcodec.common.ArrayUtil;
import org.jcodec.common.io.FileChannelWrapper;
import org.jcodec.common.io.IOUtils;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture8Bit;
import org.jcodec.common.tools.MathUtil;
import org.jcodec.scale.AWTUtil;
import org.junit.Assert;
public class Utils {
public static File tildeExpand(String path) {
if (path.startsWith("~")) {
path = path.replaceFirst("~", System.getProperty("user.home"));
}
return new File(path);
}
public static void printArray(int[] array, String[] title) {
if (title.length > 0)
System.out.println(title[0]);
int max = ArrayUtil.max(array);
// Approx digits needed, 2^10 ~ 10^3
int digits = Math.max(1, (3 * MathUtil.log2(max)) / 10);
for (int i = 0; i < array.length; i++) {
System.out.print(String.format("%0" + digits + "d", array[i]));
if (i < array.length - 1)
System.out.print(",");
}
System.out.println();
}
public static void printArrayHex(int[] array, String[] title) {
if (title.length > 0)
System.out.println(title[0]);
int max = ArrayUtil.max(array);
// One hex digit = 2^4
int digits = Math.max(2, MathUtil.log2(max) >> 2);
for (int i = 0; i < array.length; i++) {
System.out.print(String.format("%0" + digits + "x", array[i]));
if (i < array.length - 1)
System.out.print(",");
}
System.out.println();
}
public static void printArray2d(int[][] array, String[] title) {
if (title.length > 0)
System.out.println(title[0]);
for (int i = 0; i < array.length; i++) {
printArray(array[i], new String[0]);
}
System.out.println();
}
public static void assertArrayEquals(int[][] is, int[][] v) {
Assert.assertEquals(is.length, v.length);
for (int i = 0; i < is.length; i++) {
Assert.assertArrayEquals(is[i], v[i]);
}
}
public static void compareMP4H264Files(File file, File refFile) throws IOException, JCodecException {
FileChannelWrapper ch1 = null, ch2 = null;
try {
ch1 = NIOUtils.readableChannel(file);
ch2 = NIOUtils.readableChannel(refFile);
FrameGrab8Bit frameGrab1 = FrameGrab8Bit.createFrameGrab8Bit(ch1);
FrameGrab8Bit frameGrab2 = FrameGrab8Bit.createFrameGrab8Bit(ch2);
Picture8Bit fr1, fr2;
do {
fr1 = frameGrab1.getNativeFrame();
fr2 = frameGrab2.getNativeFrame();
if (fr1 == null || fr2 == null)
break;
fr1 = fr1.cropped();
fr2 = fr2.cropped();
assertTrue(picturesRoughlyEqual(fr1, fr2, 20));
} while (fr1 != null && fr2 != null);
Assert.assertNull(fr1);
Assert.assertNull(fr2);
} finally {
IOUtils.closeQuietly(ch1);
IOUtils.closeQuietly(ch2);
}
}
public static int maxDiff(Picture8Bit fr1, Picture8Bit fr2) {
if (fr2.getWidth() != fr1.getWidth() || fr2.getHeight() != fr1.getHeight())
throw new IllegalArgumentException("Diffing pictures of different sizes.");
int maxDiff = 0;
for (int i = 0; i < fr2.getData().length; i++) {
int diff = findMaxDiff(fr2.getPlaneData(i), fr1.getPlaneData(i));
if (diff > maxDiff) {
maxDiff = diff;
}
}
return maxDiff;
}
public static boolean picturesRoughlyEqual(Picture8Bit fr1, Picture8Bit fr2, int threshold) {
if (fr2.getWidth() != fr1.getWidth() || fr2.getHeight() != fr1.getHeight())
return false;
for (int i = 0; i < fr2.getData().length; i++) {
int avgDiff = findAvgDiff(fr2.getPlaneData(i), fr1.getPlaneData(i));
if (avgDiff > 0) {
Logger.warn("Avg diff: " + avgDiff);
}
if (avgDiff > threshold) {
return false;
}
}
return true;
}
public static int findMaxDiff(byte[] rand, byte[] newRand) {
int maxDiff = 0;
for (int i = 0; i < rand.length; i++) {
int diff = Math.abs(rand[i] - newRand[i]);
if (diff > maxDiff)
maxDiff = diff;
}
return maxDiff;
}
public static int findAvgDiff(byte[] one, byte[] two) {
int totalDiff = 0;
for (int i = 0; i < one.length; i++) {
totalDiff += Math.abs(one[i] - two[i]);
}
return totalDiff / one.length;
}
public static Picture8Bit readYuvFrame(ReadableByteChannel ch, int width, int height) throws IOException {
Picture8Bit result = Picture8Bit.create(width, height, ColorSpace.YUV420J);
for (int i = 0; i < result.getData().length; i++) {
byte[] planeData = result.getPlaneData(i);
if (ch.read(ByteBuffer.wrap(planeData)) != result.getPlaneData(i).length) {
throw new EOFException();
}
for (int j = 0; j < planeData.length; j++) {
planeData[j] = (byte) ((planeData[j] & 0xff) - 128);
}
}
return result;
}
public static void saveImage(Picture8Bit fr2, String formatName, String name) throws IOException {
ImageIO.write(AWTUtil.toBufferedImage8Bit(fr2), formatName, new File(name));
}
public static Picture8Bit diff(Picture8Bit one, Picture8Bit two, int mul) {
Picture8Bit result = Picture8Bit.create(one.getWidth(), one.getHeight(), one.getColor());
byte[][] dataO = one.getData();
byte[][] dataT = two.getData();
byte[][] dataR = result.getData();
Arrays.fill(dataR[1], (byte) 64);
for (int i = 0; i < dataO[0].length; i++) {
dataR[0][i] = (byte) (clipMax(abs(dataO[0][i] - dataT[0][i]) * mul, 255) - 128);
}
return result;
}
public static BufferedImage scale(BufferedImage source, int width, int height) {
AffineTransform at = AffineTransform.getScaleInstance(((double) width) / source.getWidth(), ((double) height)
/ source.getHeight());
AffineTransformOp bilinearScaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC);
return bilinearScaleOp.filter(
source,
new BufferedImage(width, height, source.getType()));
}
private static double clampVector(double d) {
return d < 0 ? 0 : (d > 1 ? 1 : d);
}
public static Picture8Bit buildSmoothRandomPic(int width, int height, int smooth, double vectorSmooth) {
Picture8Bit pic = Picture8Bit.create(width, height, ColorSpace.YUV420);
int[] prevRow = new int[width];
double vector = Math.random();
for (int p = 0; p < pic.getColor().nComp; p++) {
int val = (int) (Math.random() * 255);
pic.getPlaneData(p)[0] = (byte) (val - 128);
prevRow[0] = val;
for (int i = 1; i < pic.getPlaneData(p).length; i++) {
int predInd = i % pic.getPlaneWidth(p);
int pred;
if (i < pic.getPlaneWidth(p)) {
pred = prevRow[predInd - 1];
} else if (predInd == 0) {
pred = prevRow[predInd];
} else {
vector = clampVector(vector + Math.random() * vectorSmooth - vectorSmooth / 2);
pred = (int) (prevRow[predInd] * vector + prevRow[predInd - 1] * (1 - vector));
}
int delta = (int) (Math.random() * smooth) - (pred * smooth) / 256;
val = MathUtil.clip(pred + delta, 0, 255);
pic.getPlaneData(p)[i] = (byte) (val - 128);
prevRow[predInd] = val;
}
}
return pic;
}
}