TJBench.java 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021
  1. /*
  2. * Copyright (C)2009-2014, 2016-2019 D. R. Commander. All Rights Reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are met:
  6. *
  7. * - Redistributions of source code must retain the above copyright notice,
  8. * this list of conditions and the following disclaimer.
  9. * - Redistributions in binary form must reproduce the above copyright notice,
  10. * this list of conditions and the following disclaimer in the documentation
  11. * and/or other materials provided with the distribution.
  12. * - Neither the name of the libjpeg-turbo Project nor the names of its
  13. * contributors may be used to endorse or promote products derived from this
  14. * software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
  17. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
  20. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  21. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  22. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  23. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  24. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  25. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  26. * POSSIBILITY OF SUCH DAMAGE.
  27. */
  28. import java.io.*;
  29. import java.awt.image.*;
  30. import javax.imageio.*;
  31. import java.util.*;
  32. import org.libjpegturbo.turbojpeg.*;
  33. final class TJBench {
  34. private TJBench() {}
  35. private static int flags = 0, quiet = 0, pf = TJ.PF_BGR, yuvPad = 1;
  36. private static boolean compOnly, decompOnly, doTile, doYUV, write = true;
  37. static final String[] PIXFORMATSTR = {
  38. "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "GRAY"
  39. };
  40. static final String[] SUBNAME_LONG = {
  41. "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1"
  42. };
  43. static final String[] SUBNAME = {
  44. "444", "422", "420", "GRAY", "440", "411"
  45. };
  46. static final String[] CSNAME = {
  47. "RGB", "YCbCr", "GRAY", "CMYK", "YCCK"
  48. };
  49. private static TJScalingFactor sf;
  50. private static int xformOp = TJTransform.OP_NONE, xformOpt = 0;
  51. private static double benchTime = 5.0, warmup = 1.0;
  52. static double getTime() {
  53. return (double)System.nanoTime() / 1.0e9;
  54. }
  55. private static String tjErrorMsg;
  56. private static int tjErrorCode = -1;
  57. static void handleTJException(TJException e) throws TJException {
  58. String errorMsg = e.getMessage();
  59. int errorCode = e.getErrorCode();
  60. if ((flags & TJ.FLAG_STOPONWARNING) == 0 &&
  61. errorCode == TJ.ERR_WARNING) {
  62. if (tjErrorMsg == null || !tjErrorMsg.equals(errorMsg) ||
  63. tjErrorCode != errorCode) {
  64. tjErrorMsg = errorMsg;
  65. tjErrorCode = errorCode;
  66. System.out.println("WARNING: " + errorMsg);
  67. }
  68. } else
  69. throw e;
  70. }
  71. static String formatName(int subsamp, int cs) {
  72. if (cs == TJ.CS_YCbCr)
  73. return SUBNAME_LONG[subsamp];
  74. else if (cs == TJ.CS_YCCK)
  75. return CSNAME[cs] + " " + SUBNAME_LONG[subsamp];
  76. else
  77. return CSNAME[cs];
  78. }
  79. static String sigFig(double val, int figs) {
  80. String format;
  81. int digitsAfterDecimal = figs - (int)Math.ceil(Math.log10(Math.abs(val)));
  82. if (digitsAfterDecimal < 1)
  83. format = new String("%.0f");
  84. else
  85. format = new String("%." + digitsAfterDecimal + "f");
  86. return String.format(format, val);
  87. }
  88. static byte[] loadImage(String fileName, int[] w, int[] h, int pixelFormat)
  89. throws Exception {
  90. BufferedImage img = ImageIO.read(new File(fileName));
  91. if (img == null)
  92. throw new Exception("Could not read " + fileName);
  93. w[0] = img.getWidth();
  94. h[0] = img.getHeight();
  95. int[] rgb = img.getRGB(0, 0, w[0], h[0], null, 0, w[0]);
  96. int ps = TJ.getPixelSize(pixelFormat);
  97. int rindex = TJ.getRedOffset(pixelFormat);
  98. int gindex = TJ.getGreenOffset(pixelFormat);
  99. int bindex = TJ.getBlueOffset(pixelFormat);
  100. if ((long)w[0] * (long)h[0] * (long)ps > (long)Integer.MAX_VALUE)
  101. throw new Exception("Image is too large");
  102. byte[] dstBuf = new byte[w[0] * h[0] * ps];
  103. int pixels = w[0] * h[0], dstPtr = 0, rgbPtr = 0;
  104. while (pixels-- > 0) {
  105. dstBuf[dstPtr + rindex] = (byte)((rgb[rgbPtr] >> 16) & 0xff);
  106. dstBuf[dstPtr + gindex] = (byte)((rgb[rgbPtr] >> 8) & 0xff);
  107. dstBuf[dstPtr + bindex] = (byte)(rgb[rgbPtr] & 0xff);
  108. dstPtr += ps;
  109. rgbPtr++;
  110. }
  111. return dstBuf;
  112. }
  113. static void saveImage(String fileName, byte[] srcBuf, int w, int h,
  114. int pixelFormat) throws Exception {
  115. BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
  116. int pixels = w * h, srcPtr = 0;
  117. int ps = TJ.getPixelSize(pixelFormat);
  118. int rindex = TJ.getRedOffset(pixelFormat);
  119. int gindex = TJ.getGreenOffset(pixelFormat);
  120. int bindex = TJ.getBlueOffset(pixelFormat);
  121. for (int y = 0; y < h; y++) {
  122. for (int x = 0; x < w; x++, srcPtr += ps) {
  123. int pixel = (srcBuf[srcPtr + rindex] & 0xff) << 16 |
  124. (srcBuf[srcPtr + gindex] & 0xff) << 8 |
  125. (srcBuf[srcPtr + bindex] & 0xff);
  126. img.setRGB(x, y, pixel);
  127. }
  128. }
  129. ImageIO.write(img, "bmp", new File(fileName));
  130. }
  131. /* Decompression test */
  132. static void decomp(byte[] srcBuf, byte[][] jpegBuf, int[] jpegSize,
  133. byte[] dstBuf, int w, int h, int subsamp, int jpegQual,
  134. String fileName, int tilew, int tileh) throws Exception {
  135. String qualStr = new String(""), sizeStr, tempStr;
  136. TJDecompressor tjd;
  137. double elapsed, elapsedDecode;
  138. int ps = TJ.getPixelSize(pf), i, iter = 0;
  139. int scaledw = sf.getScaled(w);
  140. int scaledh = sf.getScaled(h);
  141. int pitch = scaledw * ps;
  142. YUVImage yuvImage = null;
  143. if (jpegQual > 0)
  144. qualStr = new String("_Q" + jpegQual);
  145. tjd = new TJDecompressor();
  146. if (dstBuf == null) {
  147. if ((long)pitch * (long)scaledh > (long)Integer.MAX_VALUE)
  148. throw new Exception("Image is too large");
  149. dstBuf = new byte[pitch * scaledh];
  150. }
  151. /* Set the destination buffer to gray so we know whether the decompressor
  152. attempted to write to it */
  153. Arrays.fill(dstBuf, (byte)127);
  154. if (doYUV) {
  155. int width = doTile ? tilew : scaledw;
  156. int height = doTile ? tileh : scaledh;
  157. yuvImage = new YUVImage(width, yuvPad, height, subsamp);
  158. Arrays.fill(yuvImage.getBuf(), (byte)127);
  159. }
  160. /* Benchmark */
  161. iter = -1;
  162. elapsed = elapsedDecode = 0.0;
  163. while (true) {
  164. int tile = 0;
  165. double start = getTime();
  166. for (int y = 0; y < h; y += tileh) {
  167. for (int x = 0; x < w; x += tilew, tile++) {
  168. int width = doTile ? Math.min(tilew, w - x) : scaledw;
  169. int height = doTile ? Math.min(tileh, h - y) : scaledh;
  170. try {
  171. tjd.setSourceImage(jpegBuf[tile], jpegSize[tile]);
  172. } catch (TJException e) { handleTJException(e); }
  173. if (doYUV) {
  174. yuvImage.setBuf(yuvImage.getBuf(), width, yuvPad, height, subsamp);
  175. try {
  176. tjd.decompressToYUV(yuvImage, flags);
  177. } catch (TJException e) { handleTJException(e); }
  178. double startDecode = getTime();
  179. tjd.setSourceImage(yuvImage);
  180. try {
  181. tjd.decompress(dstBuf, x, y, width, pitch, height, pf, flags);
  182. } catch (TJException e) { handleTJException(e); }
  183. if (iter >= 0)
  184. elapsedDecode += getTime() - startDecode;
  185. } else {
  186. try {
  187. tjd.decompress(dstBuf, x, y, width, pitch, height, pf, flags);
  188. } catch (TJException e) { handleTJException(e); }
  189. }
  190. }
  191. }
  192. elapsed += getTime() - start;
  193. if (iter >= 0) {
  194. iter++;
  195. if (elapsed >= benchTime)
  196. break;
  197. } else if (elapsed >= warmup) {
  198. iter = 0;
  199. elapsed = elapsedDecode = 0.0;
  200. }
  201. }
  202. if (doYUV)
  203. elapsed -= elapsedDecode;
  204. tjd = null;
  205. for (i = 0; i < jpegBuf.length; i++)
  206. jpegBuf[i] = null;
  207. jpegBuf = null; jpegSize = null;
  208. System.gc();
  209. if (quiet != 0) {
  210. System.out.format("%-6s%s",
  211. sigFig((double)(w * h) / 1000000. *
  212. (double)iter / elapsed, 4),
  213. quiet == 2 ? "\n" : " ");
  214. if (doYUV)
  215. System.out.format("%s\n",
  216. sigFig((double)(w * h) / 1000000. *
  217. (double)iter / elapsedDecode, 4));
  218. else if (quiet != 2)
  219. System.out.print("\n");
  220. } else {
  221. System.out.format("%s --> Frame rate: %f fps\n",
  222. (doYUV ? "Decomp to YUV" : "Decompress "),
  223. (double)iter / elapsed);
  224. System.out.format(" Throughput: %f Megapixels/sec\n",
  225. (double)(w * h) / 1000000. * (double)iter / elapsed);
  226. if (doYUV) {
  227. System.out.format("YUV Decode --> Frame rate: %f fps\n",
  228. (double)iter / elapsedDecode);
  229. System.out.format(" Throughput: %f Megapixels/sec\n",
  230. (double)(w * h) / 1000000. *
  231. (double)iter / elapsedDecode);
  232. }
  233. }
  234. if (!write) return;
  235. if (sf.getNum() != 1 || sf.getDenom() != 1)
  236. sizeStr = new String(sf.getNum() + "_" + sf.getDenom());
  237. else if (tilew != w || tileh != h)
  238. sizeStr = new String(tilew + "x" + tileh);
  239. else
  240. sizeStr = new String("full");
  241. if (decompOnly)
  242. tempStr = new String(fileName + "_" + sizeStr + ".bmp");
  243. else
  244. tempStr = new String(fileName + "_" + SUBNAME[subsamp] + qualStr +
  245. "_" + sizeStr + ".bmp");
  246. saveImage(tempStr, dstBuf, scaledw, scaledh, pf);
  247. int ndx = tempStr.lastIndexOf('.');
  248. tempStr = new String(tempStr.substring(0, ndx) + "-err.bmp");
  249. if (srcBuf != null && sf.getNum() == 1 && sf.getDenom() == 1) {
  250. if (quiet == 0)
  251. System.out.println("Compression error written to " + tempStr + ".");
  252. if (subsamp == TJ.SAMP_GRAY) {
  253. for (int y = 0, index = 0; y < h; y++, index += pitch) {
  254. for (int x = 0, index2 = index; x < w; x++, index2 += ps) {
  255. int rindex = index2 + TJ.getRedOffset(pf);
  256. int gindex = index2 + TJ.getGreenOffset(pf);
  257. int bindex = index2 + TJ.getBlueOffset(pf);
  258. int lum = (int)((double)(srcBuf[rindex] & 0xff) * 0.299 +
  259. (double)(srcBuf[gindex] & 0xff) * 0.587 +
  260. (double)(srcBuf[bindex] & 0xff) * 0.114 + 0.5);
  261. if (lum > 255) lum = 255;
  262. if (lum < 0) lum = 0;
  263. dstBuf[rindex] = (byte)Math.abs((dstBuf[rindex] & 0xff) - lum);
  264. dstBuf[gindex] = (byte)Math.abs((dstBuf[gindex] & 0xff) - lum);
  265. dstBuf[bindex] = (byte)Math.abs((dstBuf[bindex] & 0xff) - lum);
  266. }
  267. }
  268. } else {
  269. for (int y = 0; y < h; y++)
  270. for (int x = 0; x < w * ps; x++)
  271. dstBuf[pitch * y + x] =
  272. (byte)Math.abs((dstBuf[pitch * y + x] & 0xff) -
  273. (srcBuf[pitch * y + x] & 0xff));
  274. }
  275. saveImage(tempStr, dstBuf, w, h, pf);
  276. }
  277. }
  278. static void fullTest(byte[] srcBuf, int w, int h, int subsamp, int jpegQual,
  279. String fileName) throws Exception {
  280. TJCompressor tjc;
  281. byte[] tmpBuf;
  282. byte[][] jpegBuf;
  283. int[] jpegSize;
  284. double start, elapsed, elapsedEncode;
  285. int totalJpegSize = 0, tilew, tileh, i, iter;
  286. int ps = TJ.getPixelSize(pf);
  287. int ntilesw = 1, ntilesh = 1, pitch = w * ps;
  288. String pfStr = PIXFORMATSTR[pf];
  289. YUVImage yuvImage = null;
  290. if ((long)pitch * (long)h > (long)Integer.MAX_VALUE)
  291. throw new Exception("Image is too large");
  292. tmpBuf = new byte[pitch * h];
  293. if (quiet == 0)
  294. System.out.format(">>>>> %s (%s) <--> JPEG %s Q%d <<<<<\n", pfStr,
  295. (flags & TJ.FLAG_BOTTOMUP) != 0 ?
  296. "Bottom-up" : "Top-down",
  297. SUBNAME_LONG[subsamp], jpegQual);
  298. tjc = new TJCompressor();
  299. for (tilew = doTile ? 8 : w, tileh = doTile ? 8 : h; ;
  300. tilew *= 2, tileh *= 2) {
  301. if (tilew > w)
  302. tilew = w;
  303. if (tileh > h)
  304. tileh = h;
  305. ntilesw = (w + tilew - 1) / tilew;
  306. ntilesh = (h + tileh - 1) / tileh;
  307. jpegBuf = new byte[ntilesw * ntilesh][TJ.bufSize(tilew, tileh, subsamp)];
  308. jpegSize = new int[ntilesw * ntilesh];
  309. /* Compression test */
  310. if (quiet == 1)
  311. System.out.format("%-4s (%s) %-5s %-3d ", pfStr,
  312. (flags & TJ.FLAG_BOTTOMUP) != 0 ? "BU" : "TD",
  313. SUBNAME_LONG[subsamp], jpegQual);
  314. for (i = 0; i < h; i++)
  315. System.arraycopy(srcBuf, w * ps * i, tmpBuf, pitch * i, w * ps);
  316. tjc.setJPEGQuality(jpegQual);
  317. tjc.setSubsamp(subsamp);
  318. if (doYUV) {
  319. yuvImage = new YUVImage(tilew, yuvPad, tileh, subsamp);
  320. Arrays.fill(yuvImage.getBuf(), (byte)127);
  321. }
  322. /* Benchmark */
  323. iter = -1;
  324. elapsed = elapsedEncode = 0.0;
  325. while (true) {
  326. int tile = 0;
  327. totalJpegSize = 0;
  328. start = getTime();
  329. for (int y = 0; y < h; y += tileh) {
  330. for (int x = 0; x < w; x += tilew, tile++) {
  331. int width = Math.min(tilew, w - x);
  332. int height = Math.min(tileh, h - y);
  333. tjc.setSourceImage(srcBuf, x, y, width, pitch, height, pf);
  334. if (doYUV) {
  335. double startEncode = getTime();
  336. yuvImage.setBuf(yuvImage.getBuf(), width, yuvPad, height,
  337. subsamp);
  338. tjc.encodeYUV(yuvImage, flags);
  339. if (iter >= 0)
  340. elapsedEncode += getTime() - startEncode;
  341. tjc.setSourceImage(yuvImage);
  342. }
  343. tjc.compress(jpegBuf[tile], flags);
  344. jpegSize[tile] = tjc.getCompressedSize();
  345. totalJpegSize += jpegSize[tile];
  346. }
  347. }
  348. elapsed += getTime() - start;
  349. if (iter >= 0) {
  350. iter++;
  351. if (elapsed >= benchTime)
  352. break;
  353. } else if (elapsed >= warmup) {
  354. iter = 0;
  355. elapsed = elapsedEncode = 0.0;
  356. }
  357. }
  358. if (doYUV)
  359. elapsed -= elapsedEncode;
  360. if (quiet == 1)
  361. System.out.format("%-5d %-5d ", tilew, tileh);
  362. if (quiet != 0) {
  363. if (doYUV)
  364. System.out.format("%-6s%s",
  365. sigFig((double)(w * h) / 1000000. *
  366. (double)iter / elapsedEncode, 4),
  367. quiet == 2 ? "\n" : " ");
  368. System.out.format("%-6s%s",
  369. sigFig((double)(w * h) / 1000000. *
  370. (double)iter / elapsed, 4),
  371. quiet == 2 ? "\n" : " ");
  372. System.out.format("%-6s%s",
  373. sigFig((double)(w * h * ps) / (double)totalJpegSize,
  374. 4),
  375. quiet == 2 ? "\n" : " ");
  376. } else {
  377. System.out.format("\n%s size: %d x %d\n", doTile ? "Tile" : "Image",
  378. tilew, tileh);
  379. if (doYUV) {
  380. System.out.format("Encode YUV --> Frame rate: %f fps\n",
  381. (double)iter / elapsedEncode);
  382. System.out.format(" Output image size: %d bytes\n",
  383. yuvImage.getSize());
  384. System.out.format(" Compression ratio: %f:1\n",
  385. (double)(w * h * ps) / (double)yuvImage.getSize());
  386. System.out.format(" Throughput: %f Megapixels/sec\n",
  387. (double)(w * h) / 1000000. *
  388. (double)iter / elapsedEncode);
  389. System.out.format(" Output bit stream: %f Megabits/sec\n",
  390. (double)yuvImage.getSize() * 8. / 1000000. *
  391. (double)iter / elapsedEncode);
  392. }
  393. System.out.format("%s --> Frame rate: %f fps\n",
  394. doYUV ? "Comp from YUV" : "Compress ",
  395. (double)iter / elapsed);
  396. System.out.format(" Output image size: %d bytes\n",
  397. totalJpegSize);
  398. System.out.format(" Compression ratio: %f:1\n",
  399. (double)(w * h * ps) / (double)totalJpegSize);
  400. System.out.format(" Throughput: %f Megapixels/sec\n",
  401. (double)(w * h) / 1000000. * (double)iter / elapsed);
  402. System.out.format(" Output bit stream: %f Megabits/sec\n",
  403. (double)totalJpegSize * 8. / 1000000. *
  404. (double)iter / elapsed);
  405. }
  406. if (tilew == w && tileh == h && write) {
  407. String tempStr = fileName + "_" + SUBNAME[subsamp] + "_" + "Q" +
  408. jpegQual + ".jpg";
  409. FileOutputStream fos = new FileOutputStream(tempStr);
  410. fos.write(jpegBuf[0], 0, jpegSize[0]);
  411. fos.close();
  412. if (quiet == 0)
  413. System.out.println("Reference image written to " + tempStr);
  414. }
  415. /* Decompression test */
  416. if (!compOnly)
  417. decomp(srcBuf, jpegBuf, jpegSize, tmpBuf, w, h, subsamp, jpegQual,
  418. fileName, tilew, tileh);
  419. else if (quiet == 1)
  420. System.out.println("N/A");
  421. if (tilew == w && tileh == h) break;
  422. }
  423. }
  424. static void decompTest(String fileName) throws Exception {
  425. TJTransformer tjt;
  426. byte[][] jpegBuf = null;
  427. byte[] srcBuf;
  428. int[] jpegSize = null;
  429. int totalJpegSize;
  430. double start, elapsed;
  431. int ps = TJ.getPixelSize(pf), tile, x, y, iter;
  432. // Original image
  433. int w = 0, h = 0, ntilesw = 1, ntilesh = 1, subsamp = -1, cs = -1;
  434. // Transformed image
  435. int tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp;
  436. FileInputStream fis = new FileInputStream(fileName);
  437. if (fis.getChannel().size() > (long)Integer.MAX_VALUE)
  438. throw new Exception("Image is too large");
  439. int srcSize = (int)fis.getChannel().size();
  440. srcBuf = new byte[srcSize];
  441. fis.read(srcBuf, 0, srcSize);
  442. fis.close();
  443. int index = fileName.lastIndexOf('.');
  444. if (index >= 0)
  445. fileName = new String(fileName.substring(0, index));
  446. tjt = new TJTransformer();
  447. try {
  448. tjt.setSourceImage(srcBuf, srcSize);
  449. } catch (TJException e) { handleTJException(e); }
  450. w = tjt.getWidth();
  451. h = tjt.getHeight();
  452. subsamp = tjt.getSubsamp();
  453. cs = tjt.getColorspace();
  454. if (quiet == 1) {
  455. System.out.println("All performance values in Mpixels/sec\n");
  456. System.out.format("Bitmap JPEG JPEG %s %s Xform Comp Decomp ",
  457. (doTile ? "Tile " : "Image"),
  458. (doTile ? "Tile " : "Image"));
  459. if (doYUV)
  460. System.out.print("Decode");
  461. System.out.print("\n");
  462. System.out.print("Format CS Subsamp Width Height Perf Ratio Perf ");
  463. if (doYUV)
  464. System.out.print("Perf");
  465. System.out.println("\n");
  466. } else if (quiet == 0)
  467. System.out.format(">>>>> JPEG %s --> %s (%s) <<<<<\n",
  468. formatName(subsamp, cs), PIXFORMATSTR[pf],
  469. (flags & TJ.FLAG_BOTTOMUP) != 0 ?
  470. "Bottom-up" : "Top-down");
  471. for (int tilew = doTile ? 16 : w, tileh = doTile ? 16 : h; ;
  472. tilew *= 2, tileh *= 2) {
  473. if (tilew > w)
  474. tilew = w;
  475. if (tileh > h)
  476. tileh = h;
  477. ntilesw = (w + tilew - 1) / tilew;
  478. ntilesh = (h + tileh - 1) / tileh;
  479. tw = w; th = h; ttilew = tilew; ttileh = tileh;
  480. if (quiet == 0) {
  481. System.out.format("\n%s size: %d x %d", (doTile ? "Tile" : "Image"),
  482. ttilew, ttileh);
  483. if (sf.getNum() != 1 || sf.getDenom() != 1)
  484. System.out.format(" --> %d x %d", sf.getScaled(tw),
  485. sf.getScaled(th));
  486. System.out.println("");
  487. } else if (quiet == 1) {
  488. System.out.format("%-4s (%s) %-5s %-5s ", PIXFORMATSTR[pf],
  489. (flags & TJ.FLAG_BOTTOMUP) != 0 ? "BU" : "TD",
  490. CSNAME[cs], SUBNAME_LONG[subsamp]);
  491. System.out.format("%-5d %-5d ", tilew, tileh);
  492. }
  493. tsubsamp = subsamp;
  494. if (doTile || xformOp != TJTransform.OP_NONE || xformOpt != 0) {
  495. if (xformOp == TJTransform.OP_TRANSPOSE ||
  496. xformOp == TJTransform.OP_TRANSVERSE ||
  497. xformOp == TJTransform.OP_ROT90 ||
  498. xformOp == TJTransform.OP_ROT270) {
  499. tw = h; th = w; ttilew = tileh; ttileh = tilew;
  500. }
  501. if ((xformOpt & TJTransform.OPT_GRAY) != 0)
  502. tsubsamp = TJ.SAMP_GRAY;
  503. if (xformOp == TJTransform.OP_HFLIP ||
  504. xformOp == TJTransform.OP_ROT180)
  505. tw = tw - (tw % TJ.getMCUWidth(tsubsamp));
  506. if (xformOp == TJTransform.OP_VFLIP ||
  507. xformOp == TJTransform.OP_ROT180)
  508. th = th - (th % TJ.getMCUHeight(tsubsamp));
  509. if (xformOp == TJTransform.OP_TRANSVERSE ||
  510. xformOp == TJTransform.OP_ROT90)
  511. tw = tw - (tw % TJ.getMCUHeight(tsubsamp));
  512. if (xformOp == TJTransform.OP_TRANSVERSE ||
  513. xformOp == TJTransform.OP_ROT270)
  514. th = th - (th % TJ.getMCUWidth(tsubsamp));
  515. tntilesw = (tw + ttilew - 1) / ttilew;
  516. tntilesh = (th + ttileh - 1) / ttileh;
  517. if (xformOp == TJTransform.OP_TRANSPOSE ||
  518. xformOp == TJTransform.OP_TRANSVERSE ||
  519. xformOp == TJTransform.OP_ROT90 ||
  520. xformOp == TJTransform.OP_ROT270) {
  521. if (tsubsamp == TJ.SAMP_422)
  522. tsubsamp = TJ.SAMP_440;
  523. else if (tsubsamp == TJ.SAMP_440)
  524. tsubsamp = TJ.SAMP_422;
  525. }
  526. TJTransform[] t = new TJTransform[tntilesw * tntilesh];
  527. jpegBuf =
  528. new byte[tntilesw * tntilesh][TJ.bufSize(ttilew, ttileh, subsamp)];
  529. for (y = 0, tile = 0; y < th; y += ttileh) {
  530. for (x = 0; x < tw; x += ttilew, tile++) {
  531. t[tile] = new TJTransform();
  532. t[tile].width = Math.min(ttilew, tw - x);
  533. t[tile].height = Math.min(ttileh, th - y);
  534. t[tile].x = x;
  535. t[tile].y = y;
  536. t[tile].op = xformOp;
  537. t[tile].options = xformOpt | TJTransform.OPT_TRIM;
  538. if ((t[tile].options & TJTransform.OPT_NOOUTPUT) != 0 &&
  539. jpegBuf[tile] != null)
  540. jpegBuf[tile] = null;
  541. }
  542. }
  543. iter = -1;
  544. elapsed = 0.;
  545. while (true) {
  546. start = getTime();
  547. try {
  548. tjt.transform(jpegBuf, t, flags);
  549. } catch (TJException e) { handleTJException(e); }
  550. jpegSize = tjt.getTransformedSizes();
  551. elapsed += getTime() - start;
  552. if (iter >= 0) {
  553. iter++;
  554. if (elapsed >= benchTime)
  555. break;
  556. } else if (elapsed >= warmup) {
  557. iter = 0;
  558. elapsed = 0.0;
  559. }
  560. }
  561. t = null;
  562. for (tile = 0, totalJpegSize = 0; tile < tntilesw * tntilesh; tile++)
  563. totalJpegSize += jpegSize[tile];
  564. if (quiet != 0) {
  565. System.out.format("%-6s%s%-6s%s",
  566. sigFig((double)(w * h) / 1000000. / elapsed, 4),
  567. quiet == 2 ? "\n" : " ",
  568. sigFig((double)(w * h * ps) /
  569. (double)totalJpegSize, 4),
  570. quiet == 2 ? "\n" : " ");
  571. } else if (quiet == 0) {
  572. System.out.format("Transform --> Frame rate: %f fps\n",
  573. 1.0 / elapsed);
  574. System.out.format(" Output image size: %d bytes\n",
  575. totalJpegSize);
  576. System.out.format(" Compression ratio: %f:1\n",
  577. (double)(w * h * ps) / (double)totalJpegSize);
  578. System.out.format(" Throughput: %f Megapixels/sec\n",
  579. (double)(w * h) / 1000000. / elapsed);
  580. System.out.format(" Output bit stream: %f Megabits/sec\n",
  581. (double)totalJpegSize * 8. / 1000000. / elapsed);
  582. }
  583. } else {
  584. if (quiet == 1)
  585. System.out.print("N/A N/A ");
  586. jpegBuf = new byte[1][TJ.bufSize(ttilew, ttileh, subsamp)];
  587. jpegSize = new int[1];
  588. jpegBuf[0] = srcBuf;
  589. jpegSize[0] = srcSize;
  590. }
  591. if (w == tilew)
  592. ttilew = tw;
  593. if (h == tileh)
  594. ttileh = th;
  595. if ((xformOpt & TJTransform.OPT_NOOUTPUT) == 0)
  596. decomp(null, jpegBuf, jpegSize, null, tw, th, tsubsamp, 0,
  597. fileName, ttilew, ttileh);
  598. else if (quiet == 1)
  599. System.out.println("N/A");
  600. jpegBuf = null;
  601. jpegSize = null;
  602. if (tilew == w && tileh == h) break;
  603. }
  604. }
  605. static void usage() throws Exception {
  606. int i;
  607. TJScalingFactor[] scalingFactors = TJ.getScalingFactors();
  608. int nsf = scalingFactors.length;
  609. String className = new TJBench().getClass().getName();
  610. System.out.println("\nUSAGE: java " + className);
  611. System.out.println(" <Inputfile (BMP)> <Quality> [options]\n");
  612. System.out.println(" java " + className);
  613. System.out.println(" <Inputfile (JPG)> [options]\n");
  614. System.out.println("Options:\n");
  615. System.out.println("-alloc = Dynamically allocate JPEG image buffers");
  616. System.out.println("-bottomup = Test bottom-up compression/decompression");
  617. System.out.println("-tile = Test performance of the codec when the image is encoded as separate");
  618. System.out.println(" tiles of varying sizes.");
  619. System.out.println("-rgb, -bgr, -rgbx, -bgrx, -xbgr, -xrgb =");
  620. System.out.println(" Test the specified color conversion path in the codec (default = BGR)");
  621. System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available in");
  622. System.out.println(" the underlying codec");
  623. System.out.println("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying");
  624. System.out.println(" codec");
  625. System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the");
  626. System.out.println(" underlying codec");
  627. System.out.println("-progressive = Use progressive entropy coding in JPEG images generated by");
  628. System.out.println(" compression and transform operations.");
  629. System.out.println("-subsamp <s> = When testing JPEG compression, this option specifies the level");
  630. System.out.println(" of chrominance subsampling to use (<s> = 444, 422, 440, 420, 411, or");
  631. System.out.println(" GRAY). The default is to test Grayscale, 4:2:0, 4:2:2, and 4:4:4 in");
  632. System.out.println(" sequence.");
  633. System.out.println("-quiet = Output results in tabular rather than verbose format");
  634. System.out.println("-yuv = Test YUV encoding/decoding functions");
  635. System.out.println("-yuvpad <p> = If testing YUV encoding/decoding, this specifies the number of");
  636. System.out.println(" bytes to which each row of each plane in the intermediate YUV image is");
  637. System.out.println(" padded (default = 1)");
  638. System.out.println("-scale M/N = Scale down the width/height of the decompressed JPEG image by a");
  639. System.out.print(" factor of M/N (M/N = ");
  640. for (i = 0; i < nsf; i++) {
  641. System.out.format("%d/%d", scalingFactors[i].getNum(),
  642. scalingFactors[i].getDenom());
  643. if (nsf == 2 && i != nsf - 1)
  644. System.out.print(" or ");
  645. else if (nsf > 2) {
  646. if (i != nsf - 1)
  647. System.out.print(", ");
  648. if (i == nsf - 2)
  649. System.out.print("or ");
  650. }
  651. if (i % 8 == 0 && i != 0)
  652. System.out.print("\n ");
  653. }
  654. System.out.println(")");
  655. System.out.println("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 =");
  656. System.out.println(" Perform the corresponding lossless transform prior to");
  657. System.out.println(" decompression (these options are mutually exclusive)");
  658. System.out.println("-grayscale = Perform lossless grayscale conversion prior to decompression");
  659. System.out.println(" test (can be combined with the other transforms above)");
  660. System.out.println("-copynone = Do not copy any extra markers (including EXIF and ICC profile data)");
  661. System.out.println(" when transforming the image.");
  662. System.out.println("-benchtime <t> = Run each benchmark for at least <t> seconds (default = 5.0)");
  663. System.out.println("-warmup <t> = Run each benchmark for <t> seconds (default = 1.0) prior to");
  664. System.out.println(" starting the timer, in order to prime the caches and thus improve the");
  665. System.out.println(" consistency of the results.");
  666. System.out.println("-componly = Stop after running compression tests. Do not test decompression.");
  667. System.out.println("-nowrite = Do not write reference or output images (improves consistency");
  668. System.out.println(" of performance measurements.)");
  669. System.out.println("-stoponwarning = Immediately discontinue the current");
  670. System.out.println(" compression/decompression/transform operation if the underlying codec");
  671. System.out.println(" throws a warning (non-fatal error)\n");
  672. System.out.println("NOTE: If the quality is specified as a range (e.g. 90-100), a separate");
  673. System.out.println("test will be performed for all quality values in the range.\n");
  674. System.exit(1);
  675. }
  676. public static void main(String[] argv) {
  677. byte[] srcBuf = null;
  678. int w = 0, h = 0, minQual = -1, maxQual = -1;
  679. int minArg = 1, retval = 0;
  680. int subsamp = -1;
  681. try {
  682. if (argv.length < minArg)
  683. usage();
  684. String tempStr = argv[0].toLowerCase();
  685. if (tempStr.endsWith(".jpg") || tempStr.endsWith(".jpeg"))
  686. decompOnly = true;
  687. System.out.println("");
  688. if (!decompOnly) {
  689. minArg = 2;
  690. if (argv.length < minArg)
  691. usage();
  692. try {
  693. minQual = Integer.parseInt(argv[1]);
  694. } catch (NumberFormatException e) {}
  695. if (minQual < 1 || minQual > 100)
  696. throw new Exception("Quality must be between 1 and 100.");
  697. int dashIndex = argv[1].indexOf('-');
  698. if (dashIndex > 0 && argv[1].length() > dashIndex + 1) {
  699. try {
  700. maxQual = Integer.parseInt(argv[1].substring(dashIndex + 1));
  701. } catch (NumberFormatException e) {}
  702. }
  703. if (maxQual < 1 || maxQual > 100)
  704. maxQual = minQual;
  705. }
  706. if (argv.length > minArg) {
  707. for (int i = minArg; i < argv.length; i++) {
  708. if (argv[i].equalsIgnoreCase("-tile")) {
  709. doTile = true; xformOpt |= TJTransform.OPT_CROP;
  710. } else if (argv[i].equalsIgnoreCase("-fastupsample")) {
  711. System.out.println("Using fast upsampling code\n");
  712. flags |= TJ.FLAG_FASTUPSAMPLE;
  713. } else if (argv[i].equalsIgnoreCase("-fastdct")) {
  714. System.out.println("Using fastest DCT/IDCT algorithm\n");
  715. flags |= TJ.FLAG_FASTDCT;
  716. } else if (argv[i].equalsIgnoreCase("-accuratedct")) {
  717. System.out.println("Using most accurate DCT/IDCT algorithm\n");
  718. flags |= TJ.FLAG_ACCURATEDCT;
  719. } else if (argv[i].equalsIgnoreCase("-progressive")) {
  720. System.out.println("Using progressive entropy coding\n");
  721. flags |= TJ.FLAG_PROGRESSIVE;
  722. } else if (argv[i].equalsIgnoreCase("-rgb"))
  723. pf = TJ.PF_RGB;
  724. else if (argv[i].equalsIgnoreCase("-rgbx"))
  725. pf = TJ.PF_RGBX;
  726. else if (argv[i].equalsIgnoreCase("-bgr"))
  727. pf = TJ.PF_BGR;
  728. else if (argv[i].equalsIgnoreCase("-bgrx"))
  729. pf = TJ.PF_BGRX;
  730. else if (argv[i].equalsIgnoreCase("-xbgr"))
  731. pf = TJ.PF_XBGR;
  732. else if (argv[i].equalsIgnoreCase("-xrgb"))
  733. pf = TJ.PF_XRGB;
  734. else if (argv[i].equalsIgnoreCase("-bottomup"))
  735. flags |= TJ.FLAG_BOTTOMUP;
  736. else if (argv[i].equalsIgnoreCase("-quiet"))
  737. quiet = 1;
  738. else if (argv[i].equalsIgnoreCase("-qq"))
  739. quiet = 2;
  740. else if (argv[i].equalsIgnoreCase("-scale") && i < argv.length - 1) {
  741. int temp1 = 0, temp2 = 0;
  742. boolean match = false, scanned = true;
  743. Scanner scanner = new Scanner(argv[++i]).useDelimiter("/");
  744. try {
  745. temp1 = scanner.nextInt();
  746. temp2 = scanner.nextInt();
  747. } catch (Exception e) {}
  748. if (temp2 <= 0) temp2 = 1;
  749. if (temp1 > 0) {
  750. TJScalingFactor[] scalingFactors = TJ.getScalingFactors();
  751. for (int j = 0; j < scalingFactors.length; j++) {
  752. if ((double)temp1 / (double)temp2 ==
  753. (double)scalingFactors[j].getNum() /
  754. (double)scalingFactors[j].getDenom()) {
  755. sf = scalingFactors[j];
  756. match = true; break;
  757. }
  758. }
  759. if (!match) usage();
  760. } else
  761. usage();
  762. } else if (argv[i].equalsIgnoreCase("-hflip"))
  763. xformOp = TJTransform.OP_HFLIP;
  764. else if (argv[i].equalsIgnoreCase("-vflip"))
  765. xformOp = TJTransform.OP_VFLIP;
  766. else if (argv[i].equalsIgnoreCase("-transpose"))
  767. xformOp = TJTransform.OP_TRANSPOSE;
  768. else if (argv[i].equalsIgnoreCase("-transverse"))
  769. xformOp = TJTransform.OP_TRANSVERSE;
  770. else if (argv[i].equalsIgnoreCase("-rot90"))
  771. xformOp = TJTransform.OP_ROT90;
  772. else if (argv[i].equalsIgnoreCase("-rot180"))
  773. xformOp = TJTransform.OP_ROT180;
  774. else if (argv[i].equalsIgnoreCase("-rot270"))
  775. xformOp = TJTransform.OP_ROT270;
  776. else if (argv[i].equalsIgnoreCase("-grayscale"))
  777. xformOpt |= TJTransform.OPT_GRAY;
  778. else if (argv[i].equalsIgnoreCase("-nooutput"))
  779. xformOpt |= TJTransform.OPT_NOOUTPUT;
  780. else if (argv[i].equalsIgnoreCase("-copynone"))
  781. xformOpt |= TJTransform.OPT_COPYNONE;
  782. else if (argv[i].equalsIgnoreCase("-benchtime") &&
  783. i < argv.length - 1) {
  784. double temp = -1;
  785. try {
  786. temp = Double.parseDouble(argv[++i]);
  787. } catch (NumberFormatException e) {}
  788. if (temp > 0.0)
  789. benchTime = temp;
  790. else
  791. usage();
  792. } else if (argv[i].equalsIgnoreCase("-warmup") &&
  793. i < argv.length - 1) {
  794. double temp = -1;
  795. try {
  796. temp = Double.parseDouble(argv[++i]);
  797. } catch (NumberFormatException e) {}
  798. if (temp >= 0.0) {
  799. warmup = temp;
  800. System.out.format("Warmup time = %.1f seconds\n\n", warmup);
  801. } else
  802. usage();
  803. } else if (argv[i].equalsIgnoreCase("-yuv")) {
  804. System.out.println("Testing YUV planar encoding/decoding\n");
  805. doYUV = true;
  806. } else if (argv[i].equalsIgnoreCase("-yuvpad") &&
  807. i < argv.length - 1) {
  808. int temp = 0;
  809. try {
  810. temp = Integer.parseInt(argv[++i]);
  811. } catch (NumberFormatException e) {}
  812. if (temp >= 1)
  813. yuvPad = temp;
  814. } else if (argv[i].equalsIgnoreCase("-subsamp") &&
  815. i < argv.length - 1) {
  816. i++;
  817. if (argv[i].toUpperCase().startsWith("G"))
  818. subsamp = TJ.SAMP_GRAY;
  819. else if (argv[i].equals("444"))
  820. subsamp = TJ.SAMP_444;
  821. else if (argv[i].equals("422"))
  822. subsamp = TJ.SAMP_422;
  823. else if (argv[i].equals("440"))
  824. subsamp = TJ.SAMP_440;
  825. else if (argv[i].equals("420"))
  826. subsamp = TJ.SAMP_420;
  827. else if (argv[i].equals("411"))
  828. subsamp = TJ.SAMP_411;
  829. } else if (argv[i].equalsIgnoreCase("-componly"))
  830. compOnly = true;
  831. else if (argv[i].equalsIgnoreCase("-nowrite"))
  832. write = false;
  833. else if (argv[i].equalsIgnoreCase("-stoponwarning"))
  834. flags |= TJ.FLAG_STOPONWARNING;
  835. else usage();
  836. }
  837. }
  838. if (sf == null)
  839. sf = new TJScalingFactor(1, 1);
  840. if ((sf.getNum() != 1 || sf.getDenom() != 1) && doTile) {
  841. System.out.println("Disabling tiled compression/decompression tests, because those tests do not");
  842. System.out.println("work when scaled decompression is enabled.");
  843. doTile = false;
  844. }
  845. if (!decompOnly) {
  846. int[] width = new int[1], height = new int[1];
  847. srcBuf = loadImage(argv[0], width, height, pf);
  848. w = width[0]; h = height[0];
  849. int index = -1;
  850. if ((index = argv[0].lastIndexOf('.')) >= 0)
  851. argv[0] = argv[0].substring(0, index);
  852. }
  853. if (quiet == 1 && !decompOnly) {
  854. System.out.println("All performance values in Mpixels/sec\n");
  855. System.out.format("Bitmap JPEG JPEG %s %s ",
  856. (doTile ? "Tile " : "Image"),
  857. (doTile ? "Tile " : "Image"));
  858. if (doYUV)
  859. System.out.print("Encode ");
  860. System.out.print("Comp Comp Decomp ");
  861. if (doYUV)
  862. System.out.print("Decode");
  863. System.out.print("\n");
  864. System.out.print("Format Subsamp Qual Width Height ");
  865. if (doYUV)
  866. System.out.print("Perf ");
  867. System.out.print("Perf Ratio Perf ");
  868. if (doYUV)
  869. System.out.print("Perf");
  870. System.out.println("\n");
  871. }
  872. if (decompOnly) {
  873. decompTest(argv[0]);
  874. System.out.println("");
  875. System.exit(retval);
  876. }
  877. System.gc();
  878. if (subsamp >= 0 && subsamp < TJ.NUMSAMP) {
  879. for (int i = maxQual; i >= minQual; i--)
  880. fullTest(srcBuf, w, h, subsamp, i, argv[0]);
  881. System.out.println("");
  882. } else {
  883. for (int i = maxQual; i >= minQual; i--)
  884. fullTest(srcBuf, w, h, TJ.SAMP_GRAY, i, argv[0]);
  885. System.out.println("");
  886. System.gc();
  887. for (int i = maxQual; i >= minQual; i--)
  888. fullTest(srcBuf, w, h, TJ.SAMP_420, i, argv[0]);
  889. System.out.println("");
  890. System.gc();
  891. for (int i = maxQual; i >= minQual; i--)
  892. fullTest(srcBuf, w, h, TJ.SAMP_422, i, argv[0]);
  893. System.out.println("");
  894. System.gc();
  895. for (int i = maxQual; i >= minQual; i--)
  896. fullTest(srcBuf, w, h, TJ.SAMP_444, i, argv[0]);
  897. System.out.println("");
  898. }
  899. } catch (Exception e) {
  900. if (e instanceof TJException) {
  901. TJException tje = (TJException)e;
  902. System.out.println((tje.getErrorCode() == TJ.ERR_WARNING ?
  903. "WARNING: " : "ERROR: ") + tje.getMessage());
  904. } else
  905. System.out.println("ERROR: " + e.getMessage());
  906. e.printStackTrace();
  907. retval = -1;
  908. }
  909. System.exit(retval);
  910. }
  911. }