java.lang.OutOfMemory Exception with Multithreaded Image Generate - multithreading

I am trying to copy a lot of images with Java ImageIO. Each copy resizes the original picture. As the size of image set is huge (60,000). I try to use multi thread to solve the problem. Here is the code:
package generate.image
import scala.util.Random._
import scala.math._
import java.io.File
import java.io.PrintWriter
import java.util.concurrent.{ExecutorService, TimeUnit, Executors}
import java.awt.Image
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
class ImageResizer{
def resizeImage(srcImgPath: String, distImgPath: String, width: Int, height: Int){
val srcFile: File = new File(srcImgPath)
val srcImg: Image = ImageIO.read(srcFile)
val buffImg: BufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
buffImg.getGraphics().drawImage(
srcImg.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0,
0, null
)
ImageIO.write(buffImg, "JPEG", new File(distImgPath))
}
}
class ImageWorker(imgSrc: String, imgName: String, width: Int, height: Int) extends Runnable{
override def run(): Unit = {
val resizer = new ImageResizer()
resizer.resizeImage(imgSrc, imgName, width, height);
}
}
object ImageGenerate {
def main(args:Array[String]): Unit = {
// parameters
val dirName = args(0)
val images = new File(dirName).listFiles.filter(_.getName.endsWith(".JPEG"))
val imgCnt = images.length
// threadpool
val pool = Executors.newFixedThreadPool(25)
// copy with norm
for(i <- 0 until imgCnt){
for(cnt <- 1 to 20){
val width = nextInt(200) + 300
val height = nextInt(200) + 300
val imgSrc: String = images(i).getAbsolutePath
val imgName: String = "img/%s_%d_%d_%d.JPEG".format(splitFilename(images(i).getName), width, height, cnt)
pool.execute(new ImageWorker(imgSrc, imgName, width, height))
}
}
pool.shutdown()
pool.awaitTermination(Long.MaxValue, TimeUnit.NANOSECONDS)
}
// split file name
def splitFilename(fileName: String) = {
fileName.substring(0, fileName.lastIndexOf("."))
}
}
ImageResizer does the copy work. It reads an image into BufferedImage, resizes it into a new BufferedImage, and finally writes to a JPEG file.
ImageWorker does the thread work. It is executed by the worker threads in the ExecuteServive.
ImageGenerate does the dispatching work. It reads all the image files in the args(0) (first arg), generates new random width and height, and submit a new job to pool.
Compile and run: scalac ImageGenerate.scala scala generate.image.ImageGenerate test. The sizes of images are 150kb in average.
As is running, the program throws java.lang.OutOfMemoryError.
Sometimes, there is a Exception in thread "pool-1-thread-36" java.lang.OutOfMemoryError: GC overhead limit exceeded error.
If I set the parameter -J-Xmx2048m, the program will run smoothly. However, I only run 400 images. Is there any optimization for my code?
Thanks for sharing your idea, best wishes.

You should be calling dispose()
Something like this (untested)
val graphics = buffImg.getGraphics()
graphics.drawImage(
srcImg.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0,
0, null
)
ImageIO.write(buffImg, "JPEG", new File(distImgPath))
graphics.dispose()

Related

add Logo on images Android

I am working on Android app that has to put frame and Logo on Image at a Time, The Problem I am facing is Frame is Coming from first Fragment and the Logo from Second Fragment. I am Setting the Frame on Bitmap image on imageView as well as Logo.
The Issue I am facing is, as I successfully Successfully Add Frame on Bitmap image, and I try to also set Logo on Bitmap image it Remove the Frame and Set the Logo on Bitmap and vice versa..
What I really want is Frame and Logo are set on Bitmap at a time...
Here, where Logo Coming From First Fragment Adapter to main Activity via Method..
holder.iconslogo.setOnClickListener {
when (charItemlogo.itemsidlogo) {
1 -> {
var btmp= arrayList[0].iconslogo
(context as MakeStylishActivity).setLogos(btmp)
}
Here the Frame is Coming from Frame Fragment to Main Activity
holder.iconsframe.setOnClickListener {
when (charItemFrame.itemsidframe) {
1 -> {
var btmp= arrayList[0].iconsframe
(context as MakeStylishActivity).setFrames(btmp)
}}
This is Main Activity that is Setting the Logo and Frame to Bitmap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_make_stylish)
val byteArray = intent.getByteArrayExtra("pictures")
bmp = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)!!
img_bitmap1.setImageBitmap(bmp)
stringqrcontent= intent.getStringExtra("qrcontent")
bottom_nav_viewstyle.setOnNavigationItemSelectedListener {
when (it.itemId) {
R.id.action_default -> {
true
}
R.id.action_colors -> {
ShowFColorFragment()
true
}
R.id.action_logos -> {
ShowLogoFragment()
true
}
R.id.action_frames -> {
FunctionAddFrames();
true
}
R.id.action_patterns -> {
true
}
else -> false
}
}
}
fun setLogos(btmp: Bitmap?) {
//img_bitmap1.setImageBitmap(btmp)
PutLogoOnQRBitmaps(btmp, bmp!!)
}
fun setFrames(btmp: Bitmap?) {
// img_bitmap1.setImageBitmap(btmp)
//addWhiteBorder(bmp!!,10)
PutFrameImages(btmp, bmp!!)
}
//Combine Frame Behind QR Code
fun PutFrameImages(frame: Bitmap?, image: Bitmap): Bitmap? {
var cs: Bitmap? = null
var rs: Bitmap? = null
rs = Bitmap.createScaledBitmap(frame!!, image.width, image.height, true)
cs = Bitmap.createBitmap(rs.width, rs.height, Bitmap.Config.RGB_565)
val comboImage = Canvas(cs)
comboImage.drawBitmap(image, 0F, 0F, null)
comboImage.drawBitmap(rs, 0F, 0F, null)
if (rs != null) {
rs.recycle()
rs = null
}
// Runtime.getRuntime().gc()
img_bitmap1.setImageBitmap(cs!!)
return cs
}
//Put Logo on QR Code
fun PutLogoOnQRBitmaps(logo: Bitmap?, qrcode: Bitmap): Bitmap? {
val combined = Bitmap.createBitmap(qrcode.width, qrcode.height, qrcode.config)
val canvas = Canvas(combined)
val canvasWidth = canvas.width
val canvasHeight = canvas.height
canvas.drawBitmap(qrcode, Matrix(), null)
val resizeLogo = Bitmap.createScaledBitmap(logo!!, canvasWidth / 5, canvasHeight / 5, true)
val centreX = (canvasWidth - resizeLogo.width) / 2
val centreY = (canvasHeight - resizeLogo.height) / 2
canvas.drawBitmap(resizeLogo, centreX.toFloat(), centreY.toFloat(), null)
img_bitmap1.setImageBitmap(combined)
return combined
}}
I see a few things here that aren't considered a good idea, but the most important is that the size of an Intent has a limit (very small, that is) and is not designed to pass large amounts of data.
What I would do
Regardless of your simplistic Architecture (no usage of ViewModels, or correct separation of concerns, and a few other SOLID principles ignored here...), I would not pass the image via intent. Instead, I would save the image to the filesystem (temporarily), pass the "path" as a string to the next activity, and have said activity open the file and create the Bitmap from the filesystem.
This means you no longer need to worry about going overboard with the image size/intent size, and that your two activities decouple a little bit. You can now pass any path to a bitmap there and the other activity will pick it up, regardless of where it came from.
A second improvement would be to delegate all this (image storing, passing, retrieving, etc.) to a ViewModel + UseCase (and/or Repository), in which case you'd be further decoupling your code. For this, and much more, the starting point would be getting started with Android Jetpack; I recommend at least trying to leverage a ViewModel in your architecture.
You'd also want to be careful when creating bitmaps out of nowhere, you can easily run out of memory by doing what you're doing; you should take a look at Android's official documentation about handling large bitmaps.

Arduino+processing and string information in an "if" statment

I am using an arduino to read out information from a scale into Processing.
Now I want certain things to happen depending on the weight registered. The weight is read out in a String. I have found out that >= does not work for strings. I have tried if (val >= str ("20.00")) but that doesnt work either.
I have also tried to convert it into a float using float scale = Float.parseFloat(val); But that doesnt work either. Does anyone have an idea. That would be great!
PImage pictureday;
import processing.serial.*;
import cc.arduino.*;
import org.firmata.*;
Serial myPort;
String val;
color textcolor = color(0, 50, 50, 240);
color textcolor2 = color(255, 0, 0);
void setup()
{
fullScreen();
background (189, 215, 239);
String portName = Serial.list()[0];
myPort = new Serial(this, portName, 9600);
}
void draw()
{
if ( myPort.available() > 0)
{ // If data is available,
val = myPort.readStringUntil('\n');
}
if (scale >= 20.00)
{
image(pictureday, 0, 0);
textSize(50);
fill(textcolor);
text(val, 900, 360);
text("KG ", 1030, 360);
println (val);
}
}
Like you've discovered, you can't compare Strings like that. Processing isn't smart enough to know the String contains a number.
Instead, use the float() function:
String val = "123.456";
float f = float(val);
if(f > 100){
println("here!");
}

Nicifying execution contex's thread pool's output for logging/debuging in scala

Is there is nice way to rename a pool in/for an executon context to produce nicer output in logs/wile debugging. Not to be look like ForkJoinPool-2-worker-7 (because ~2 tells nothing about pool's purose in app) but WorkForkJoinPool-2-worker-7.. wihout creating new WorkForkJoinPool class for it?
Example:
object LogSample extends App {
val ex1 = ExecutionContext.global
val ex2 = ExecutionContext.fromExecutor(null:Executor) // another global ex context
val system = ActorSystem("system")
val log = Logging(system.eventStream, "my.nice.string")
Future {
log.info("1")
}(ex1)
Future {
log.info("2")
}(ex2)
Thread.sleep(1000)
// output, like this:
/*
[INFO] [09/14/2015 21:53:34.897] [ForkJoinPool-2-worker-7] [my.nice.string] 2
[INFO] [09/14/2015 21:53:34.897] [ForkJoinPool-1-worker-7] [my.nice.string] 1
*/
}
You need to implement custom thread factory, something like this:
class CustomThreadFactory(prefix: String) extends ForkJoinPool.ForkJoinWorkerThreadFactory {
def newThread(fjp: ForkJoinPool): ForkJoinWorkerThread = {
val thread = new ForkJoinWorkerThread(fjp) {}
thread.setName(prefix + "-" + thread.getName)
thread
}
}
val threadFactory = new CustomThreadFactory("custom prefix here")
val uncaughtExceptionHandler = new UncaughtExceptionHandler {
override def uncaughtException(t: Thread, e: Throwable) = e.printStackTrace()
}
val executor = new ForkJoinPool(10, threadFactory, uncaughtExceptionHandler, true)
val ex2 = ExecutionContext.fromExecutor(executor) // another global ex context
val system = ActorSystem("system")
val log = Logging(system.eventStream, "my.nice.string")
Future {
log.info("2") //[INFO] [09/15/2015 18:22:43.728] [custom prefix here-ForkJoinPool-1-worker-29] [my.nice.string] 2
}(ex2)
Thread.sleep(1000)
Ok. Seems this is not possible (particulary for default global iml) due to current scala ExecutonContext implementation.
What I could do is just copy that impl and replace:
class DefaultThreadFactory(daemonic: Boolean) ... {
def wire[T <: Thread](thread: T): T = {
thread.setName("My" + thread.getId) // ! add this one (make 'My' to be variable)
thread.setDaemon(daemonic)
thread.setUncaughtExceptionHandler(uncaughtExceptionHandler)
thread
}...
because threadFactory there
val threadFactory = new DefaultThreadFactory(daemonic = true)
is harcoded ...
(seems Vladimir Petrosyan was first showing nicer way :) )

How can I get a value from a the ScalaFX thread?

I have a myNode: scalafx.scene.Node that I'd like to turn into a bitmap so that I can save it to disk, compare it to other bitmaps, etc. I think the correct code to turn it into a scalafx.scene.image.WritableImage is
val writableImg = new Scene { root = new Group(myNode) }.snapshot(null)
and then I should be able to turn that into a java.awt.image.RenderedImage with
val renderedImg = SwingFXUtils.fromFXImage(writableImg, null)
The problem, as you've probably realized, is that I have to run the code to get wrImg on the ScalaFX thread. There's a question here that explains how to return a value, but I'm not having any luck translating that to Scala. I tried this:
lazy val writableImg: WritableImage = {
val wrImg = new FutureTask(new Callable[WritableImage]() {
override def call(): WritableImage = {
new Scene { root = new Group(myNode) }.snapshot(null)
}
})
Platform.runLater(wrImg)
wrImg.get()
}
but the code just hangs and never completes. Can anyone provide an idiomatic Scala version and/or tell me why the code never returns?
If you just want to save the image to disk you can simply do it on the same thread avoiding complication passing the image around. Something like this will work:
Platform.runLater {
val node = new Circle {
centerX = 200
centerY = 200
radius = 50
stroke = Color.BROWN
strokeWidth = 2
fill = Color.DARKKHAKI
}
val jfxImage = node.snapshot(new SnapshotParameters(), null)
val bufferedImage = SwingFXUtils.fromFXImage(jfxImage, null)
val file = new File("./snapshot.png")
println("Writing snapshot to: " + file.getAbsolutePath)
javax.imageio.ImageIO.write(bufferedImage, "png", file)
()
}
The empty () to have closure returning Unit, so ScalaFX Platform.runLater is happy.
Update:
If you want to have a value from Platform.runLater the approach you suggested in your question should be in general fine. However, you want to make sure that you do not block the FX Application Thread. If you call Platform.runLater on FX Application Thread you will lock out, so you may want to have something like this
def makeSnapshot() = myNode.snapshot(new SnapshotParameters(), null)
val writableImg = if (Platform.isFxApplicationThread) {
makeSnapshot()
} else {
val futureTask = new FutureTask(new Callable[WritableImage]() {
override def call(): WritableImage = makeSnapshot()
})
Platform.runLater(futureTask)
futureTask.get()
}

How do i use the Emgu CV _SmoothGausian Method

Im trying to get the OCR sample app to recognise some small text and how I'm doing it is to resize the image. Once I have resized the image it is all 'pixel-ee'
I want to use the SmothGaussian method to clean it up but I get an error each time I execute the method
Here is the code:
Image<Bgr, Byte> image = new Image<Bgr, byte>(openImageFileDialog.FileName);
using (Image<Gray, byte> gray = image.Convert<Gray, Byte>().Resize(800, 600, Emgu.CV.CvEnum.INTER.CV_INTER_LINEAR, true))
{
gray.Convert<Gray, Byte>()._SmoothGaussian(4);
_ocr.Recognize(gray);
Tesseract.Charactor[] charactors = _ocr.GetCharactors();
foreach (Tesseract.Charactor c in charactors)
{
image.Draw(c.Region, drawColor, 1);
}
imageBox1.Image = image;
//String text = String.Concat( Array.ConvertAll(charactors, delegate(Tesseract.Charactor t) { return t.Text; }) );
String text = _ocr.GetText();
ocrTextBox.Text = text;
}
Here is the image:
_SmoothGaussian can only handle odd numbers as kernel size so try with 3 or 5 as argument instead.

Resources