その漫画自炊オタクはImageJマクロに恋をする

プログラミングを用いた、自炊漫画の画像処理

【Fiji・Jython】スキャナ読み取り直後に数秒間だけ画像表示させるPlugin (New version)

 





「データ確認のためちょこっとだけ画像表示させるマクロ」のプラグイン版です

 

# FileWatcher_Modoki_new.py

from ij import IJ 
from ij import WindowManager
from ij.gui import ImageWindow
import time
import os


version = "1.2.0"


IJ.log("")
IJ.log("FileWatcher Modoki New")
IJ.log("ver" + version)
IJ.log("")




checkImageTime = 1.8

width = 580
height = 870






#------------------------------------------------------------------------------
# Define getFileList
def getFileList(dir):

	list = []
	for filename in os.listdir(dir):
		if os.path.isdir(os.path.join(dir, filename)):
			if not filename.startswith("."):
				filename += "/"
				list.append(filename)
		if os.path.isfile(os.path.join(dir, filename)):
			if not filename.startswith("."):
				list.append(filename)
	list.sort()

	return list
	
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# define zeroPad
def zeroPad(number, length):

	return str(number).zfill(length)
	
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Define getElapsedTime
def getElapsedTime(initialTime,currentTime):
	global min
	
	Time = currentTime - initialTime;
	msec = Time % 1
	msec = int(msec * 1000)
	sec = int(Time % 60)
	min = int(Time / 60) % 60
	hour = int(Time / 3600)	
	strElapsedTime = zeroPad(hour,2) + "h : " + zeroPad(min,2) + "m : " + zeroPad(sec,2) + "." + zeroPad(msec,3) + "s"
	return strElapsedTime
	
#------------------------------------------------------------------------------
	
	
#------------------------------------------------------------------------------
# Define closeOthers
def closeOthers(frontImageTitle):

	# getTitle of displayed Images
	displayedImages = WindowManager.getImageTitles()

	# close all images except the foremost one
	for title in displayedImages:
		if title != frontImageTitle:
			WindowManager.getFrame(title).close()
		
#------------------------------------------------------------------------------
			
# zero time
initialTime = time.time()


# select directory
surveillanceDir = IJ.getDirectory("Choose a directory")
initialList = getFileList(surveillanceDir)


# initialize
min = 0


while min < 30:
	
	# currentMemory
	bytes = float(IJ.currentMemory())
	bytes = bytes / 1000000
	
	# count elapsed time
	currentTime = time.time()
	elapsedTime = getElapsedTime(initialTime,currentTime)
	IJ.log("\\Update:ElapsedTime ... " + elapsedTime + "        " + "{:.2f}".format(bytes) + "[MB]")
	
	# get current list
	currentList = getFileList(surveillanceDir)
	
	# obtain new files using set difference
	newFiles = list(set(currentList) - set(initialList))
	newFiles.sort()
	
	
	# if new files are detected...
	for newTitle in newFiles:
		path = surveillanceDir + newTitle
		print path
		
		imp = IJ.openImage(path)
		
		
		# show image
		iw = ImageWindow(imp)
		iw.setLocationAndSize(0,0,580,870)
		imp.show()
		
		# close all images except the foremost one 
		frontImageTitle = newTitle
		closeOthers(frontImageTitle)
		
		# ime to check the image
		time.sleep(checkImageTime)
		
		# reset time 
		initialTime = time.time()
		
		
	# update list
	initialList = currentList

	# wait 
	time.sleep(0.1)


# End of proc
IJ.showMessage("Time out.")
raise RuntimeError("Time out")


 

Pythonの本を読んでいて「set型は順番を考慮しないというデメリットがある一方で、集合に関する演算が高速」的な記述を見たので実装してみました。

 

 

こちらが読んだ本。ImageJマクロのおかげでちょっとプログラミングがわかるけどPythonはよう知らん、という私にはちょうどいい感じの本でした。

 

 

肝心な処理速度ですが、前回の「最終更新時間チェック法」と比べてみたところ、残念ながら前回バージョンの方がちょびっとだけ速いという結果でした。最終更新時間チェック法は単ループで回してるので、それが効いてるみたいです。

それでも「set型 差集合法」もしっかり速いので、ひとまずはこれでじゅうぶんかなって感じです。

 

 

最終更新時間チェック法

 

 

 

 

 

 

おしまい

 

 

 

 

 

 

 

 

ライセンスなんかは一切無いので、ぜひぜひ自由に使ってみてください!

 

imagej-jisui.hatenablog.com

 

 

 

 

 

【Fiji・Jython】2024リメイクPluginバージョン 自炊画像処理はこれひとつ!

 

 


  

自炊でいちばんよく使う多目的Macroを、FijiのJython(Python)を用いてPluginにリメイクしました。ちょこっと新機能付き。

 

 

 

※ 2024/02/25 追記. いろいろアップデートしていましたが、ver 10.13.2でいちおう暫定的に完成です。余計な処理を省いたので、Autoの最適コントラスト値探査 (optMinMaxAuto) が爆速になりました。

 

 

 

旧マクロバージョン

imagej-jisui.hatenablog.com

 

 

もくじ

 

 

Jython コード

# Almighty_Processing_Remake.py

from ij import IJ, ImagePlus, ImageStack
from ij import WindowManager as WM
from ij.gui import GenericDialog, WaitForUserDialog, Roi, Toolbar
from ij.measure import ResultsTable
from ij.plugin import ChannelSplitter
from ij.plugin.filter import Convolver
from ij.plugin.frame import RoiManager
from ij.process import ImageProcessor, ByteProcessor, ColorProcessor
from java.awt.event import AdjustmentListener, ItemListener
import os
import shutil
import time






version = "10.16.0"

# 10.3.0 -> apply8bitRangeLUT
# 10.4.0 -> extractOnlyRedChannel
# 10.5.0 -> imp.resize
# 10.6.0 -> saveAs(imp,"JPEG",...)
# 10.7.0 -> sharpeningFilter
# 10.8.0 -> cropRect
# 10.9.0 -> ResultsTable (Output log)
# 10.10.0 -> input toBeCheckedFiles
# 10.11.0 -> Error handling of optMinMaxAuto to multiple directories
# 10.12.0 -> Resize for optAuto
# 10.13.0 -> Stop repetitive task of opening and closing imgs (optAuto)
# 10.14.0 -> Use B&C Window (mokushiNow)
# 10.15.0 -> getRoiNombre
# 10.16.0 -> Modify for Outlier Exclusion (optAuto)




IJ.log("")
IJ.log("Almighty Processing Remake")
IJ.log("ver" + version)
IJ.log("makeDirAuto + listFilesRecursively")
IJ.log("")
logWindow = WM.getFrame("Log") # prepare to bring to front

startTime = time.strftime("%Y-%m-%d %H:%M:%S")





#------------------------------------------------------------------------------
# Define getFileList

def getFileList(dir):

	list = []
	
	for filename in os.listdir(dir):
	
		if os.path.isdir(os.path.join(dir, filename)):
			if not filename.startswith("."):
				filename += "/"
				list.append(filename)
				
		if os.path.isfile(os.path.join(dir, filename)):
			if not filename.startswith("."):
				list.append(filename)
	list.sort()

	return list
	
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Define countFiles

def countFiles(dir):
	global totalFiles
	
	list = getFileList(dir)
	
	for i in range(len(list)):
		if list[i].endswith("/"):
			countFiles(dir + list[i])
		else:
			totalFiles += 1

#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Define listFiles

def listFiles(dir):

	list = getFileList(dir)
	
	for i in range(len(list)):
		if list[i].endswith("/"):
			listFiles(dir + list[i])
		else:
			filepath = dir + list[i]
			mainOpe(filepath)
			
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# define zeroPad

def zeroPad(number, length):

	return str(int(number)).zfill(length)
	
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Define getTimeStamp

def getTimeStamp():

	timestamp = time.strftime("%Y%m%d_%Hh%Mm%Ss", time.localtime())
	return timestamp
	
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Define getElapsedTime

def getElapsedTime(initialTime,completionTime):

	Time = completionTime - initialTime;
	msec = Time % 1
	msec = int(msec * 1000)
	sec = int(Time % 60)
	min = int(Time / 60) % 60
	hour = int(Time / 3600)	
	
	strElapsedTime = zeroPad(hour, 2) + "h : " + zeroPad(min, 2) + "m : " + zeroPad(sec, 2) + "." + zeroPad(msec, 3) + "s"
	
	return strElapsedTime
	
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
#Define waitForUser

def waitForUser(msg):

	WaitForUserDialog(msg).show()
	
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Define getBoundingRect

def getBoundingRect(impA):

	# Get ROI info
	roi = impA.getRoi()
	
	# Check ROI
	if roi and isinstance(roi, Roi) and roi.getType() == Roi.RECTANGLE:
		x = roi.getXBase()
		y = roi.getYBase()
		width =  int(roi.getFloatWidth())
		height = int(roi.getFloatHeight())
	else:
		IJ.showMessage("Error: Rectangle ROI not found.")
		raise RuntimeError("yamepi")
		
	return x, y, width, height
	
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Define cropRect

def cropRect(impA):
	
	"""" for [one image] or [RBG stack image] """
	
	# Get ROI Info
	x, y, width, height = getBoundingRect(impA)
	
	# Crop section
	if impA.getStackSize() == 1:
	
		ip = impA.getProcessor()
		
		roi = Roi(x, y, width, height)
		impA.setRoi(roi)
		
		# Crop image and construct new imp
		new_ip = ip.crop()
		new_imp = ImagePlus("Cropped Image", new_ip)
		
		impA.changes = False
		impA.close()
		
		new_imp.changes = False
		new_imp.close()
		
		return new_imp
		
	elif impA.getStackSize() == 3:
		
		# Get stack from impA
		stack = impA.getImageStack()
	
		# Construct new stack
		new_stack = ImageStack(width, height)
	
		# Add slice to new stack
		for i in range(1, stack.getSize() + 1):
			ip = stack.getProcessor(i)
			
			roi = Roi(x, y, width, height)
			ip.setRoi(roi)
			
			new_ip = ip.crop()
			new_stack.addSlice(stack.getSliceLabel(i), new_ip)
	
		# Construct new imp
		new_imp = ImagePlus("Cropped RGB Stack", new_stack)
	
		impA.changes = False
		impA.close()
		
		new_imp.changes = False
		new_imp.close()
		
		return new_imp
		
	else:
		IJ.showMessage("Not support this stack size")
		raise RuntimeError("yamepi")
		
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Define apply8bitRangeLUT

def apply8bitRangeLUT(impA):
	# for 8bit Gray scale image. Not support "RGB image" or "Stacked images"
	
	if impA.getStackSize() > 1 or impA.getBitDepth() != 8:
		IJ.showMessage("Not support [RGB image] or [Stacked images]")
		raise RuntimeError("yamepi")

	ip = impA.getProcessor()
	
	displayMin = impA.getDisplayRangeMin()
	displayMax = impA.getDisplayRangeMax()
	
	# Changeing the display Range affect pixel values when saving. 
	# To prevent additional effects.
	impA.resetDisplayRange() 
	
	lut = [0] * 256
	
	for i in range(256):
		if i <= displayMin:
			lut[i] = 0
		elif i >= displayMax:
			lut[i] = 255
		else:
			lut[i] = int((float(i-displayMin) /(displayMax - displayMin)) * 256)
	
	ip.applyTable(lut)
	impA.setProcessor(ip)
	
	return impA

#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Define extractOnlyRedChannel

def extractOnlyRedChannel(impA):

	impMulti = ChannelSplitter.split(impA)
	ipRed = impMulti[0].getProcessor()
	impA.setProcessor(ipRed)
	
	return impA
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Define referenceCheck

def referenceCheck(dir):
	global referLength
	
	list = getFileList(dir)
	if list[0].endswith("/"):
		referenceCheck(dir + list[0])
	else:
		name = list[0]
		dotIndex = name.rfind(".")
		title = name[:dotIndex]
		space = title.rfind(" ")
		trueTitle = title[:space]
		number = title[space+1:]
		
		reference = trueTitle
		referLength = len(reference)
		
		IJ.log("Check trueTitle. (e.g. NARUTO_60)")
		IJ.log("trueTitle=[" + trueTitle + "]")
		logWindow.toFront()
		
		IJ.showMessage("Check trueTitle (except : number.ext), and click [OK].")
			
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Define getRoiNombre1

def getRoiNombre1():

	while True:
		waitForUser("ROI setting \n Open an image and set [ROI] using rectangle tool, then click [OK].")

		nImages = WM.getImageCount()
		if nImages != 1:
			IJ.showMessage("Open only one image.")
			continue
		
		imp = IJ.getImage()
		
		if imp.getRoi() is None:
			continue
		else:
			break
	
	IJ.run("Add to Manager") # Not use in this script.
	imp = IJ.getImage()
	
	# Add ROI position to memory
	x1_nb, y1_nb, w1_nb, h1_nb = getBoundingRect(imp)
	roi1_nb = Roi(x1_nb, y1_nb, w1_nb, h1_nb)
	IJ.run("Close All") # close all "Images"
	
	return roi1_nb	
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Define getRoiNombre2

def getRoiNombre2():
	
	#ODD
	while True:
		waitForUser("ODD(1,3,5,7,...) \n Open an [ODD number] file and set [ROI] using rectangle tool, then click [OK].")
	
		nImages = WM.getImageCount()	
		if nImages != 1:
			IJ.showMessage("Open only one image.")
			continue
		
		imp = IJ.getImage()
		
		if imp.getRoi() is None:
			continue
		else:
			break		
	
	IJ.run("Add to Manager") # Not use in this script.
	
	imp = IJ.getImage()
	
	# Add ROI position to memory
	x1_nb, y1_nb, w1_nb, h1_nb = getBoundingRect(imp)
	roi1_nb = Roi(x1_nb, y1_nb, w1_nb, h1_nb)
	IJ.run("Close All")
	
	
	#EVEN
	while True:
		waitForUser("EVEN(2,4,6,8,...) \n Open an [EVEN number] file and set [ROI] using rectangle tool, then click [OK].")
		
		nImages = WM.getImageCount()
		if nImages != 1:
			IJ.showMessage("Open only one image.")
			continue
		
		imp = IJ.getImage()
		
		if imp.getRoi() is None:
			continue
		else:
			break			
	
	IJ.run("Add to Manager") # Not use in this script.
	
	imp = IJ.getImage()
	
	# Add ROI position to memory
	x2_nb, y2_nb, w2_nb, h2_nb = getBoundingRect(imp)
	roi2_nb = Roi(x2_nb, y2_nb, w2_nb, h2_nb)
	IJ.run("Close All")

	return roi1_nb, roi2_nb
#------------------------------------------------------------------------------
	

#------------------------------------------------------------------------------
# Define showMeOptMinAndMax3Trials

def showMeOptMinAndMax3Trials():
	global flagExtactRedChannel

	sumMin = 0
	sumMax = 0
	trials = 3
	
	# create array
	OPT_MAX = [0] * trials # [0,0,0,....0,0]
	OPT_MIN = [0] * trials
	
	# Enter Parameter
	
	gd = GenericDialog("OptMinAndMax Setting")
	gd.addNumericField("targetMeanWhite", 254.97)
	gd.addNumericField("targetMeanBlack", 13)
	gd.addCheckbox("Extract Red Channel", False)
	gd.showDialog()
			
	if gd.wasCanceled():
		IJ.showMessage("Exit : Processing was canceled.")
		raise RuntimeError("yamepi")
			
	targetMeanWhite = float(gd.getNextNumber())
	targetMeanBlack = float(gd.getNextNumber())
	flagExtactRedChannel = gd.getNextBoolean()
			
	if flagExtactRedChannel:
		IJ.log("flagExtactRedChannel = ON")
	
	
	

	# Define optimiseMinMax3Trials
	
	def optimiseMinMax3Trials(now, trials, OPT_MIN, OPT_MAX):
		
		IJ.log("")
		IJ.log("optimiseMinAndMax")
		
		nImages = WM.getImageCount()
		if nImages == 0:
		
			while True:
				waitForUser("OptMinAndMax.  Open an image, then click [OK]")
				nImages = WM.getImageCount()
				
				if nImages > 1:
					IJ.showMessage("Open only one image.")	
					
				elif nImages == 1:
					break
					
		imp = IJ.getImage()
		name = imp.getTitle()
	
		if flagExtactRedChannel:
			imp = extractOnlyRedChannel(imp)
	
		# make tempDir and save original Image
		tempDir = saveDir + "Temp_optMinMax/"
		if not os.path.exists(tempDir):
			os.makedirs(tempDir)
		IJ.saveAs(imp, "PNG", tempDir + "CheckMaxAndMin.png")
	
		# optimise max (white)
		Toolbar.getInstance().setTool(Toolbar.RECTANGLE)
		roi = Roi(10,10,100,100)
		imp.setRoi(roi)
		
		while True:
			waitForUser("ROI selection, Set ROI on the [WHITE BACK GROUND], then click [OK].")
			
			if imp.getRoi() is None:
				continue
			
			stats = imp.getStatistics() # histogram, area, mean, min, max, stdDev, mode
			mean = stats.mean
			area = stats.area
		
			if mean < 150:
				IJ.showMessage("Error!! This area is not WHITE BACK GROUND. Try Again")
			else:
				break
			
		imp = cropRect(imp)
		
		IJ.saveAs(imp, "PNG", tempDir + "CheckMax.png")
		
		imp.changes = False
		imp.close()
	
		max = 254
		min = 0
		IJ.log("")
		IJ.log("optimised Max Macro, TargetMean = " + str(targetMeanWhite))
	
		for i in range(254):
			imp = IJ.openImage(tempDir + "CheckMax.png")
			ip = imp.getProcessor()
			
			imp.setDisplayRange(min, max)
			imp = apply8bitRangeLUT(imp)
			
			stats = imp.getStatistics() # histogram, area, mean, min, max, stdDev, mode
			mean = stats.mean
			area = stats.area
			
			IJ.log("Max = " + str(max) + ", mean = " + str(mean))
			
			if mean >= targetMeanWhite:
				optimisedMax = max
				break
			max = max - 1
			imp.changes = False
			imp.close()
			
		IJ.log("optimisedMax = " + str(optimisedMax))
		
		# optimise min (black)
		imp = IJ.openImage(tempDir + "CheckMaxAndMin.png")
		imp.show()
		
		Toolbar.getInstance().setTool(Toolbar.RECTANGLE)
		roi = Roi(10,10,100,100)
		imp.setRoi(roi)
		
		while True:
			waitForUser("ROI selection, Set ROI on the [BLACK ZONE], then click [OK].")
			
			if imp.getRoi() is None:
				continue
			
			stats = imp.getStatistics() # histogram, area, mean, min, max, stdDev, mode
			mean = stats.mean
			area = stats.area
		
			if mean > 100:
				IJ.showMessage("Error!! This area is not BLACK ZONE. Try Again")
			else:
				break
	
			
		imp = cropRect(imp)
		
		IJ.saveAs(imp, "PNG", tempDir + "CheckMin.png")
		imp.changes = False
		imp.close()
	
		max = optimisedMax
		min = 1
		IJ.log("")
		IJ.log("optimisedMin Macro, TargetMean = " + str(targetMeanBlack))
	
		for i in range(254):
			imp = IJ.openImage(tempDir + "CheckMin.png")
			ip = imp.getProcessor()
			
			imp.setDisplayRange(min, max)
			imp = apply8bitRangeLUT(imp)
			
			stats = imp.getStatistics() # histogram, area, mean, min, max, stdDev, mode
			mean = stats.mean
			area = stats.area
			
			IJ.log("min = " + str(min) + ", mean = " + str(mean))
			if mean <= targetMeanBlack:
				optimisedMin = min
				break
			min = min + 1
			imp.changes = False
			imp.close()
		
		# results
		OPT_MIN[now] = optimisedMin
		OPT_MAX[now] = optimisedMax
	
		IJ.log("")
		IJ.log(str(now + 1) + "/" + str(trials))
		IJ.log("Title =" + name)
		IJ.log("optimisedMin =" + str(optimisedMin))
		IJ.log("optimisedMax =" + str(optimisedMax))
		
		return OPT_MIN, OPT_MAX
		# >>>>>>>> The End of optimiseMinMax3Trials
		
		
		
		
	# trials and getting sum
	for now in range(trials):
		OPT_MIN, OPT_MAX = optimiseMinMax3Trials(now, trials, OPT_MIN, OPT_MAX)
		sumMin += OPT_MIN[now]
		sumMax += OPT_MAX[now]
	
	# get average
	min = int(sumMin / trials)
	max = int(sumMax / trials)
	
	IJ.log("")
	time.sleep(1)
	
	for i in range(trials):
		IJ.log(str(i+1) + "..." + "min = " + str(OPT_MIN[i]) + ", max = " + str(OPT_MAX[i]))
	
	IJ.log("")
	IJ.beep()
	time.sleep(1)
	
	IJ.log("Result..." + "optMin = " + str(min) + ", optMax = " + str(max))
	time.sleep(3)
	IJ.log("")
	
	return min, max
	
# showMeOptMinAndMax3Trials End
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Define showMeOptMinAndMaxAuto

def showMeOptMinAndMaxAuto(dir):
	global flagExtactRedChannel
	
	gd = GenericDialog("Processing Setting")
	gd.addNumericField("targetMeanWhite", 254.97, 2) #3rd argument is digit to right of decimal point
	gd.addNumericField("targetMeanBlack", 13, 2)  # default->18,27
	gd.addMessage("")
	gd.addNumericField("hoseiMax", -3)
	gd.addNumericField("toBeCheckedFiles", 11)
	gd.addMessage("")
	gd.addCheckbox("Extract Red Channel", False)
	gd.showDialog()
	if gd.wasCanceled():
		IJ.showMessage("Exit : Processing was canceled.")
		raise RuntimeError("yamepi")
	targetMeanWhite = gd.getNextNumber()
	targetMeanBlack = gd.getNextNumber()
	hoseiMax = gd.getNextNumber()
	toBeCheckedFiles = int(gd.getNextNumber())
	flagExtactRedChannel = gd.getNextBoolean()
	
	if flagExtactRedChannel:
		IJ.log("flagExtactRedChannel = ON")

	# tempDir
	tempDir = saveDir + "Temp_optMinMax/"
	IJ.log("Temp dir :" + tempDir)
	logWindow.toFront()
	time.sleep(1)

	# Make tempDir
	if not os.path.exists(tempDir):
		os.makedirs(tempDir)

	# Create Array
	extractStep = int(totalFiles / toBeCheckedFiles)
	if extractStep <= 1:
		IJ.showMessage("Error : extractStep <= 1. Mottto toBeCheckedFiles Sukunaku.")
		raise RuntimeError("yamepi")
		
	IJ.log("toBeCheckedFiles = " + str(toBeCheckedFiles) + ", extractStep = " + str(extractStep))	
	
	PATH_OMMA = []
	
	
	#Define listFilesOMMA
	
	def listFilesOMMA(dir, PATH_OMMA):
		global seqPageNum, optAutoDetectCount
		
		
		list = getFileList(dir)
		for i in range(len(list)):	
			if list[i].endswith("/"):
				PATH_OMMA= listFilesOMMA(dir + list[i], PATH_OMMA)
			elif  optAutoDetectCount < toBeCheckedFiles:
				seqPageNum += 1
				if seqPageNum % extractStep == 0:
					PATH_OMMA = PATH_OMMA + [dir + list[i]]
					optAutoDetectCount += 1
					print "seqPageNum", seqPageNum

		return PATH_OMMA
	# >>>>>>>> The End of ListFilesOMMA
		

		
			
	# Define optimiseMinMaxAuto
	
	def optimiseMinMaxAuto():
		
		optAutoCount = 0
		
		OPT_MAX = []
		OPT_MIN = []
		
		for n in range(len(PATH_OMMA)):		
			imp = IJ.openImage(PATH_OMMA[n])	
			
			name = imp.getTitle()
			
			# progress
			IJ.log("")
			IJ.log(str(optAutoCount + 1) +  "/" + str(toBeCheckedFiles) + "... Progress =  {:.2f}".format((float(optAutoCount + 1) / toBeCheckedFiles * 100)) + "%" + " [OptMinMaxAuto]")
			IJ.log("..." + name)
			IJ.log("")
		
			if flagExtactRedChannel == 1:
				imp = extractOnlyRedChannel(imp)
			
			# resize for optAuto
			width_org = imp.getWidth()
			height_org = imp.getHeight()
			height_re = height_org * 1400 / width_org
			imp = imp.resize(1400, height_re, "bicubic")
			
			imp.show()
			time.sleep(1)
			
			ip = imp.getProcessor()
		
			depth = imp.getBitDepth()
			if depth == 8:
				width = imp.getWidth()
				height = imp.getHeight()
		
				x = 0
				y = 0
		
				sum = 0
				whiteCheckCount = 0
				blackCheckCount = 0
		
				whiteSegCount = 0
				blackSegCount = 0
		
				whiteMax = 0
				blackMin = 255
		
				# whiteBackGround
				segCount = 0
				xSeg = 50
				ySeg = 50
				xStep = xSeg
				yStep = ySeg
				
				xSeg_white = xSeg
				ySeg_white = ySeg
		
				for x in range(0, width - xSeg, xStep):
					for xi in range(xSeg):
						for yi in range(ySeg):
							pixelValue = ip.getPixel(x + xi, y + yi)
							sum = sum + pixelValue
							
					mean = float(sum) / xSeg / ySeg
					
					IJ.log("\\Update:White... count = "+str(segCount) + ", x = " + str(x) + ", y = " + str(y) + ", mean = " + str(mean) )
					
					if mean > whiteMax:
						whiteMax = mean
						whiteSegCount = segCount
						x_white = x
						y_white = y
						whiteCheckCount += 1
					
					sum = 0
					segCount += 1
					
				# blackKuroBeta
				segCount = 0
				xSeg = 40
				ySeg = 40
				xStep = xSeg
				yStep = ySeg
				
				xSeg_black = xSeg
				ySeg_black = ySeg
		
				for y in range(0, height - ySeg, yStep):
					for x in range(0, width - xSeg, xStep):
						for xi in range(xSeg):
							for yi in range(ySeg):
								pixelValue = ip.getPixel(x + xi, y + yi)
								sum = sum + pixelValue
								
						mean = float(sum) / xSeg / ySeg
						
						IJ.log("\\Update:Black... count = "+str(segCount) + ", x = " + str(x) + ", y = " + str(y) + ", mean = " + str(mean) )
					
						if mean < blackMin:
							blackMin = mean
							blackSegCount = segCount
							x_black = x
							y_black = y
							blackCheckCount += 1
		
						sum = 0
						segCount += 1
		
				imp.changes = False
				imp.close() 
				
				# create cropped images of most white and most black
				imp = IJ.openImage(PATH_OMMA[n])
				imp = imp.resize(1400, height_re, "bicubic")
				
				Toolbar.getInstance().setTool(Toolbar.RECTANGLE)
				roi = Roi(x_white, y_white, xSeg_white, ySeg_white)
				imp.setRoi(roi)
				imp = cropRect(imp)
				
				if flagExtactRedChannel == 1:
					imp = extractOnlyRedChannel(imp)
				
				saveTitle = "wSegNo" + zeroPad(whiteSegCount, 6) + ".png"
				IJ.saveAs(imp, "PNG", tempDir + saveTitle)
				
				imp.changes = False
				imp.close()
				
				imp = IJ.openImage(PATH_OMMA[n])
				imp = imp.resize(1400, height_re, "bicubic")
				
				Toolbar.getInstance().setTool(Toolbar.RECTANGLE)
				roi = Roi(x_black, y_black, xSeg_black, ySeg_black)
				imp.setRoi(roi)
				imp = cropRect(imp)
				
				if flagExtactRedChannel == 1:
					imp = extractOnlyRedChannel(imp)
				
				saveTitle = "bSegNo" + zeroPad(blackSegCount, 6) + ".png"
				IJ.saveAs(imp, "PNG", tempDir + saveTitle)
				
				imp.changes = False
				imp.close()
				
				# checkMax Loop
			
				optimisedMax = 0  # If not found
				max = 254
				min = 0
		
				IJ.log("Checking Max..." + "wSegNo" + zeroPad(whiteSegCount,6) + ".png")
		
				for i in range(254):
					imp = IJ.openImage(tempDir + "wSegNo" + zeroPad(whiteSegCount, 6) + ".png")
					ip = imp.getProcessor()
					
					imp.setDisplayRange(min, max)
					imp = apply8bitRangeLUT(imp)
					
					stats = imp.getStatistics() # histogram, area, mean, min, max, stdDev, mode
					mean = stats.mean
					area = stats.area
					
					if mean >= targetMeanWhite:
						optimisedMax = max
						break
					
					max = max - 1
					
					imp.changes = False
					imp.close()
					
				IJ.log("Temp_optimisedMax = " + str(optimisedMax))
				OPT_MAX = OPT_MAX + [optimisedMax]
		
				# checkMin Loop
		
				optimisedMin = 255  # If not found
				max = 220  # Daitai 200 Gurai Ooi kara
				min = 1
		
				IJ.log("Checking Min..." + "bSegNo" + zeroPad(blackSegCount, 6) + ".png")
		
				for i in range(254):
					imp = IJ.openImage(tempDir + "bSegNo" + zeroPad(blackSegCount, 6) + ".png")
					ip = imp.getProcessor()
					
					imp.setDisplayRange(min, max)
					imp = apply8bitRangeLUT(imp)
					
					stats = imp.getStatistics() # histogram, area, mean, min, max, stdDev, mode
					mean = stats.mean
					area = stats.area
					
					if mean <= targetMeanBlack:
						optimisedMin = min
						break
						
					min = min + 1
		
					if min == max:
						break
		
					imp.changes = False
					imp.close()
		
				IJ.log("Temp_optimisedMin = " + str(optimisedMin))
				OPT_MIN = OPT_MIN + [optimisedMin]
		
			else:  # else if(depth != 8bit)
				imp.changes = False # original Image
				imp.close()
				
				OPT_MAX = OPT_MAX + [0]
				OPT_MIN = OPT_MIN + [255]
				
				IJ.log("detect color image")
				IJ.log("Temp_optimisedMax = 0")
				IJ.log("Temp_optimisedMin = 255")
				
			optAutoCount += 1 
		
		return OPT_MIN, OPT_MAX
		# >>>>>>>> The End of optimiseMinAndAuto
	
	
	
	
	# Define optmiseMinMaxResults 
	
	def optimiseMinMaxResults(OPT_MIN, OPT_MAX):
		
		# Max
		
		# Outlier Exclusion (OPT_MAX[i] > 150)
		OPT_MAX = [x for x in OPT_MAX if x > 150]
		OPT_MAX.sort()
		
		acceptCountMax = len(OPT_MAX)
				
		if acceptCountMax == 0:
			IJ.showMessage("Error! extractStep motto sukunaku.")
			raise RuntimeError("yamepi")
	
		median = int(acceptCountMax / 2)
		if acceptCountMax % 2 == 0:
			median = median - 1
		optimisedMax = OPT_MAX[median] + hoseiMax
	
	
		# Min
		
		# Outlier Exclusion (OPT_MIN[i] < 100)
		OPT_MIN = [x for x in OPT_MIN if x < 100]
		OPT_MIN.sort()
		
		acceptCountMin = len(OPT_MIN)
				
		if acceptCountMin == 0:
			IJ.showMessage("Error! extractStep motto sukunaku.")
			raise RuntimeError("yamepi")
	
		median = int(acceptCountMin / 2)
		if acceptCountMin % 2 == 0:
			median = median - 1
		optimisedMin = OPT_MIN[median]
	
		# Result
		IJ.log("")
		IJ.log("OPT_MAX = " + str(OPT_MAX))
		IJ.log("OPT_MIN = " + str(OPT_MIN))
		
		IJ.log("")
		time.sleep(1)
	
		IJ.log("acceptCount... (min, max) = (" + str(acceptCountMin) +  ", " + str(acceptCountMax) + ")")
		IJ.log("Correction to max... " + str(hoseiMax))
		
		IJ.log("")
		IJ.beep()
		time.sleep(1)
	
		IJ.log("Result...")
		IJ.log("optMin = " + str(optimisedMin) +  ", optMax = " + str(optimisedMax))
		time.sleep(3)
		IJ.log("")
		
		return optimisedMin, optimisedMax
		# >>>>>>>> The End of optimiseMinAndAutoResults
	
	
		
	
	PATH_OMMA= listFilesOMMA(openDir, PATH_OMMA)
	
	IJ.log("")
	IJ.log("Check for...")
	for i in range(len(PATH_OMMA)):
		IJ.log(PATH_OMMA[i])
	IJ.log("")
		
	
	OPT_MIN, OPT_MAX = optimiseMinMaxAuto()
	
	optimisedMin, optimisedMax = optimiseMinMaxResults(OPT_MIN, OPT_MAX)
	
	min = optimisedMin
	max = optimisedMax
	
	return min, max
	
# showMeOptMinAndMaxAuto end
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Define showMeOptMinAndMaxMokushiNow

def showMeOptMinAndMaxMokushiNow():
	global flagExtactRedChannel


	gd = GenericDialog("Processing Setting")
	items = ["Use Interactive UI", "Use Normal B&C Window"]
	gd.addRadioButtonGroup("Select Proc Type", items, 2, 1, "Use Interactive UI")
	gd.addCheckbox("Extract Red Channel", False)
	gd.showDialog()
	if gd.wasCanceled():
		IJ.showMessage("Exit : Processing was canceled.")
		raise RuntimeError("yamepi")
	mokushiRadio = gd.getNextRadioButton()
	flagExtactRedChannel = gd.getNextBoolean()
	
	if mokushiRadio == "Use Interactive UI":
		mokushiType = 1
	elif mokushiRadio == "Use Normal B&C Window":
		mokushiType = 2
		
	if flagExtactRedChannel:
		IJ.log("flagExtactRedChannel = ON")
		
	
	
	while True:
		if mokushiRadio == "Use Interactive UI":
			waitForUser("Open an image, and Please [Zoom In] the image \nto make it easier to see. \nThen click [OK]")
		elif mokushiRadio == "Use Normal B&C Window":
			waitForUser("Open an image. Then click [OK]")
	
		nImages = WM.getImageCount()
	
		if nImages > 1:
					IJ.showMessage("Open only one image.")
		elif nImages == 1:
			break
	
	imp = IJ.getImage()
	
	
	

	# Define contrrAdjustUI
	
	class ContrAdjustPreviewer(AdjustmentListener, ItemListener):
		def __init__(self, imp, sliderMin, sliderMax):
			self.imp = imp
			self.ip = imp.getProcessor()
			self.original_ip = imp.getProcessor().duplicate() # store original Image
			self.sliderMin = sliderMin
			self.sliderMax = sliderMax
			
		def adjustmentValueChanged(self, event):
			# If the change in the Slider's Value has been detected...
				self.contrAdjust()
	
		def reset(self):
			#restore original Image
			self.imp.setProcessor(self.original_ip)
		
		def contrAdjust(self):
			global minMokushi, maxMokushi
			
			# SetMinAndMax...main method
			minMokushi = self.sliderMin.getValue()
			maxMokushi = self.sliderMax.getValue()
			
			self.ip.setMinAndMax(minMokushi,maxMokushi)
			print minMokushi,maxMokushi
			self.imp.setProcessor(self.ip)
	
	def contrrAdjustUI(imp):
		gd = GenericDialog("Set Min And Max")
		gd.addSlider("min", 0, 255, 0)
		gd.addSlider("max", 0, 255, 255)
		sliderMin = gd.getSliders().get(0)
		sliderMax = gd.getSliders().get(1)
		imp = WM.getCurrentImage()
				
		depth = imp.getBitDepth()
		
		if depth == 24:
			imp = extractOnlyRedChannel(imp)
			
		previewer = ContrAdjustPreviewer(imp, sliderMin, sliderMax)
		sliderMin.addAdjustmentListener(previewer)
		sliderMax.addAdjustmentListener(previewer)
		
		gd.showDialog()
	
		if gd.wasCanceled():
			previewer.reset()
			print "Reset Image"
			IJ.showMessage("Exit : Processing was canceled.")
			raise RuntimeError("yamepi")
		else:
			previewer.contrAdjust()
		# >>>>>>>> The End of contrrAdjustUI
		
		
		
	def conventionalUI(imp):
		global minMokushi, maxMokushi
		
		IJ.run("Brightness/Contrast...")
		
		depth = imp.getBitDepth()
		if depth == 24:
			imp = extractOnlyRedChannel(imp)
			
		waitForUser("Adjust min and max with B&C Slider, then click [OK]. \n(Don't click [Apply]!)")
		
		minMokushi = imp.getDisplayRangeMin()
		maxMokushi = imp.getDisplayRangeMax()
		print minMokushi, maxMokushi
		# >>>>>>>> The End of conventionalUI
		
		
		
		
	# Obtain optimised min,max by mokushi
	if mokushiType == 1:
		contrrAdjustUI(imp)
		
	elif mokushiType == 2:
		conventionalUI(imp)
	
	
	# Results
	min = minMokushi
	max = maxMokushi
	
	imp.changes = False
	imp.close()

		
	IJ.log("")
	time.sleep(1)

	IJ.log("min = " + str(min) + ", max = " + str(max))
	time.sleep(3)
	IJ.log("")

	return min, max
	
# showMeOptMinAndMaxMokushiNow End
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------

def convKernel(ip):

	kernel = [-1, -1, -1,    -1, 12, -1,    -1,-1,-1]
	
	Convolver().convolve(ip, kernel, 3,3)
	
	imp_new = ImagePlus("Filterd Image", ip)
	
	return imp_new
	
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
#Define shapeningFilter

def shapeningFilter(impA):

	if impA.getStackSize() > 1 :
		IJ.showMessage("Not support [Stacked images]")
		raise RuntimeError("yamepi")
		
	elif impA.getBitDepth() == 8:
		ip = impA.getProcessor()
		impFiltered = convKernel(ip)
		
		return impFiltered
	
	elif impA.getBitDepth() == 24:
		
		# Split to R,G,B imp
		impMulti = ChannelSplitter.split(impA)
		
		impRed = impMulti[0]
		impGreen = impMulti[1]
		impBlue = impMulti[2]
		
		# Convolve R, G, B channels with a sharpening filter.
		ipRed = impRed.getProcessor()
		ipGreen = impGreen.getProcessor()
		ipBlue = impBlue.getProcessor()
		
		impRed = convKernel(ipRed)
		impGreen = convKernel(ipGreen)
		impBlue = convKernel(ipBlue)
		
		# Get pixel arrays
		redPixelsArray = impRed.getProcessor().getPixels()
		greenPixelsArray = impGreen.getProcessor().getPixels()
		bluePixelsArray = impBlue.getProcessor().getPixels()
		
		# Create new ImageProcessor (ColorProcessor)
		width = impRed.width
		height = impRed.height
		rgbProcessor = ColorProcessor(width, height)
		
		# Set pixel values
		rgbProcessor.setRGB(redPixelsArray, greenPixelsArray, bluePixelsArray)
		
		# Convert to RGB image
		impFilteredMerged = ImagePlus("Filterd RGB Image", rgbProcessor)
		
		return impFilteredMerged

#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Define mainOpe

def mainOpe(filepath):
	global procCount, trueTitle, numberOfPage, previousTitle

	procCount += 1

	# Progress display
	IJ.log(str(procCount) + "/" + str(totalFiles) + "... Progress =  {:.2f}".format(float(procCount) / totalFiles * 100) + "%")

	imp = IJ.openImage(filepath)
	ip = imp.getProcessor()


	# Rename Section
	if not flagRenumATMT: # Normal 
	
		name = imp.getTitle()
		dotIndex = name.rfind(".")
		title = name[:dotIndex]
		underBar = title.rfind("_")
		trueTitle = title[:underBar]
		number = title[underBar+1:]
		newname = title + ext

		if flagAddSuffix:
			trueTitle = trueTitle + "_" + suffix
			newname = trueTitle + "_" + number + ext

		if flagRenumFrom1:
			if not trueTitle == previousTitle:
				numberOfPage = 1
			previousTitle = trueTitle
			newname = trueTitle + "_" + zeroPad(numberOfPage, digit) + ext
			numberOfPage += 1
			
			# Output Correspondence table
			rt.incrementCounter() # Adds a row
			rt.addValue("", procCount)
			rt.addValue("Original", name)
			rt.addValue("New name", newname)
			
	elif flagRenumATMT:
	
		name = imp.getTitle()
		dotIndex = name.rfind(".")
		title = name[:dotIndex]

		# ReNumbering
		titleLength = len(title)

		if referLength < titleLength:
			space = title.rfind(" ")
			trueTitle = title[:space]
			number = title[space+1:]

			newname = trueTitle + "_" + zeroPad(number, digit) + ext

		elif referLength == titleLength:
			newname = title + "_" + zeroPad(1, digit) + ext

			dotIndex = newname.rfind(".")
			title = newname[:dotIndex]
			underBar = title.rfind("_")
			trueTitle = title[:underBar]
			number = title[underBar+1:]
			

	# Nombre Cut
	if flagNombre:
	
		if NombreType == 1:
			imp.setRoi(roi1_nb)
			imp = cropRect(imp)
			
		if NombreType == 2:
			number = int(number)
			if number == 0:
				IJ.showMessage("Error!! Number must not be null.")
				raise RuntimeError("yamepi")

			# ODD(1,3,5,7,...)
			if number % 2 == 1:
				imp.setRoi(roi1_nb)
				imp = cropRect(imp)
				
			# EVEN(2,4,6,8,...)
			if number % 2 == 0:
				imp.setRoi(roi2_nb)
				imp = cropRect(imp)


	# Filtering
	if flagSharpen:
		imp = shapeningFilter(imp)


	# SetResolution
	if flagReso:
		width = int(imp.getWidth() * compressionRate)
		height = int(imp.getHeight() * compressionRate)
		imp = imp.resize(width, height, "bicubic")
		ip = imp.getProcessor()

	# ContrastAdjust 8 bit
	depth = imp.getBitDepth()
	if not (flagFullColor or flagExtactRedChannel):
		if depth == 8:
			# ContrastAdjust
			if flagContr:
				imp.setDisplayRange(min, max)
				imp = apply8bitRangeLUT(imp)
			
				
	if flagFullColor:
		# ContrastAdjust
		if flagContr:
			imp.setDisplayRange(min, max)
			IJ.run(imp, "Apply LUT", "")

	if flagExtactRedChannel:
		# extract red channel to suppress yellowing
		imp = extractOnlyRedChannel(imp)
		
		ip = imp.getProcessor()
		
		# ContrastAdjust
		if flagContr:
			imp.setDisplayRange(min, max)
			imp = apply8bitRangeLUT(imp)


	# Save to SubDirectory
	subDir = parentDir + trueTitle + "/"
	if not os.path.exists(subDir):
		os.makedirs(subDir)

	if not (flagContr or flagReso or flagNombre or flagSharpen):
		if flagRenumFrom1 or flagRenumATMT:
			shutil.copy2(filepath, subDir + newname)
			IJ.log("Copy to..." + subDir + newname)
			
	elif ext == ".jpg":
		IJ.saveAs(imp, "JPEG", subDir + newname)
		IJ.log("Save to..." + subDir + newname)
		
	elif ext == ".png":		
		IJ.saveAs(imp, "PNG", subDir + newname)
		IJ.log("Save to..." + subDir + newname)
		
	IJ.run("Close All")
	
# mainOpe End
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Define GenerateImg

def GenerateImg():
	w = 108
	h = 127
	
	img = [
	[234,213,214,208,211,226,216,199,220,166,75,98,187,90,63,67,62,67,60,69,70,56,68,79,78,80,87,74,89,86,86,94,96,104,96,97,181,169,171,176,129,103,214,255,238,237,232,249,241,246,237,234,237,226,205,185,181,190,198,200,190,189,184,199,200,207,194,192,197,227,248,246,234,232,240,252,244,241,231,237,249,250,242,229,234,241,246,240,230,228,238,249,248,237,230,235,247,251,242,230,230,245,253,241,234,229,238,248,],
	[228,212,221,219,209,206,223,214,220,182,103,122,180,150,149,105,108,143,129,126,137,131,120,108,110,120,124,119,130,135,130,132,130,123,143,150,208,183,172,169,162,152,209,232,244,229,240,245,234,242,247,250,234,196,146,100,87,95,91,87,68,61,66,96,98,94,84,90,107,164,214,246,251,247,237,233,235,248,247,242,236,233,242,247,247,241,237,241,249,251,244,236,241,244,245,242,237,236,240,245,242,241,237,240,246,245,248,247,],
	[233,215,219,214,213,217,207,205,211,212,215,212,208,146,157,170,218,172,177,195,169,174,176,175,175,179,174,167,175,172,171,180,183,183,175,174,194,180,165,183,173,184,186,194,200,196,248,232,222,235,254,251,236,207,163,103,93,117,105,88,56,33,60,126,130,84,70,98,161,203,225,246,250,249,237,230,230,250,254,244,230,225,241,255,243,231,223,233,250,254,239,221,227,244,255,249,231,225,241,255,255,235,216,232,250,255,255,251,],
	[230,210,211,203,214,223,214,213,222,220,213,205,217,106,100,165,145,58,78,74,79,68,77,91,80,88,100,101,102,98,112,107,93,115,98,124,113,119,106,154,198,195,179,181,112,172,247,230,246,248,243,245,250,250,220,137,118,156,139,106,61,40,79,163,171,110,88,126,204,240,240,250,249,250,240,234,235,246,245,243,239,235,241,246,243,239,238,241,248,249,245,240,236,241,245,244,240,238,241,245,248,239,228,244,243,240,249,251,],
	[234,217,220,215,206,208,211,206,222,222,197,211,220,101,65,161,151,149,115,79,102,110,102,107,107,95,82,101,96,91,107,103,106,116,103,108,98,115,128,158,203,155,149,162,101,179,231,240,253,248,228,229,240,255,235,140,111,153,133,98,56,45,80,168,178,126,95,131,217,253,241,238,229,236,242,251,240,237,228,236,248,245,238,228,237,244,249,244,233,231,241,252,252,238,228,234,250,254,242,226,231,246,250,255,234,222,238,247,],
	[230,212,216,212,209,226,216,208,217,215,209,210,216,219,214,207,155,170,135,220,193,196,194,198,186,183,177,182,175,181,188,173,190,166,193,179,200,175,188,201,204,176,186,198,193,203,204,227,192,211,232,227,234,247,235,145,115,152,129,98,58,45,74,176,183,129,86,128,207,255,249,246,228,230,241,255,248,238,226,238,255,253,240,225,237,249,255,245,227,222,237,254,251,234,222,229,247,254,243,227,227,247,250,255,229,223,241,244,],
	[230,216,217,210,212,217,217,209,216,221,215,217,214,215,212,222,139,119,111,194,104,81,89,91,86,99,94,94,112,104,102,109,116,115,115,117,126,114,113,122,127,133,136,169,175,192,178,132,145,208,255,246,254,239,225,140,122,155,151,101,66,37,75,180,178,128,94,141,211,244,251,248,238,248,235,242,244,244,244,243,240,239,239,240,243,242,242,243,244,243,242,243,242,240,241,246,245,239,239,245,243,242,237,241,247,242,242,254,],
	[229,214,215,209,211,216,214,208,210,216,207,209,222,217,209,215,170,77,109,163,140,115,87,77,76,83,91,97,87,79,76,82,87,88,90,95,90,101,103,106,107,102,120,198,185,145,174,121,133,200,251,250,242,224,213,144,133,164,147,104,70,51,84,171,159,114,97,147,220,233,227,236,248,255,236,227,236,248,255,245,231,229,241,255,251,234,223,234,252,255,242,228,229,246,255,246,228,225,239,255,251,241,227,230,247,252,247,248,],
	[229,215,216,210,211,218,213,211,211,220,208,212,213,213,218,220,193,195,185,190,166,157,160,150,175,155,174,180,99,92,87,88,89,89,92,96,104,102,106,111,101,99,135,222,130,93,177,124,147,215,252,254,239,228,206,136,125,166,145,108,64,44,71,160,162,119,102,138,226,236,223,233,251,251,237,222,235,248,254,245,231,230,242,253,251,234,224,235,252,253,241,228,232,246,255,244,229,227,241,255,251,240,227,232,248,249,246,251,],
	[230,216,217,211,211,219,213,215,212,225,210,214,218,199,202,231,219,207,221,201,138,118,159,98,138,134,154,157,84,82,82,85,88,91,95,100,94,100,94,97,109,108,123,199,172,152,178,104,131,206,238,236,246,233,186,126,111,144,133,110,83,82,115,184,175,126,104,120,204,243,246,243,242,236,247,246,244,239,237,240,245,245,238,232,237,242,247,244,236,233,238,246,249,236,231,239,248,247,241,238,237,239,242,247,243,229,234,255,],
	[228,214,216,210,210,218,211,212,209,221,204,208,225,220,211,218,214,215,219,189,90,81,47,101,79,71,81,86,85,88,92,97,99,101,104,106,104,96,111,119,105,112,119,138,105,123,119,117,155,221,243,213,238,204,138,113,106,116,116,112,126,111,115,145,149,133,125,129,152,221,247,237,223,219,245,255,250,233,225,239,255,253,237,223,228,247,255,245,225,223,239,255,254,232,220,234,255,255,240,224,228,243,253,251,236,220,229,255,],
	[229,215,217,211,209,216,212,211,212,221,206,208,222,213,200,200,240,197,219,213,182,162,173,166,170,172,186,178,172,176,179,179,178,177,176,176,181,184,185,181,180,186,170,176,186,195,166,185,161,198,250,234,232,186,111,88,88,94,90,79,91,80,107,135,126,92,83,104,147,213,244,245,237,238,247,251,244,236,233,242,248,245,239,237,238,244,245,239,234,238,244,246,244,241,238,238,242,246,241,233,236,248,248,240,237,235,238,249,],
	[230,217,219,213,211,216,215,212,217,223,211,210,226,207,215,216,216,209,194,237,222,216,227,215,218,207,219,213,212,215,216,215,215,217,218,217,221,212,214,215,212,227,219,219,206,212,211,224,167,195,255,254,238,198,142,104,104,123,105,80,72,49,89,127,136,103,84,105,159,196,217,240,249,253,235,232,235,244,249,244,234,233,242,253,248,237,231,238,250,252,243,233,233,246,253,246,234,231,240,249,250,244,233,233,247,251,248,249,],
	[229,216,218,212,213,217,217,209,216,218,207,205,230,207,208,215,216,207,226,218,201,222,198,221,207,219,209,207,215,215,212,206,204,207,208,208,207,213,211,208,210,211,198,219,211,208,220,206,168,213,249,236,182,152,136,113,124,151,128,110,94,80,122,137,146,129,106,115,159,172,187,229,253,255,227,225,231,250,255,243,225,227,244,255,247,230,225,242,255,251,236,226,230,240,251,250,235,225,237,255,255,233,218,233,254,252,246,254,],
	[193,172,228,203,215,217,217,216,209,222,202,210,220,210,205,222,218,208,204,224,207,210,213,213,202,211,214,216,207,207,205,203,212,216,207,210,208,210,207,213,207,209,206,211,208,214,203,215,168,201,255,225,198,185,125,123,159,174,127,112,89,66,84,154,162,132,99,124,163,187,202,232,243,242,238,242,243,240,240,243,242,238,239,245,246,240,237,239,240,240,241,244,248,237,244,239,235,243,242,241,239,242,240,243,242,236,243,253,],
	[253,205,220,208,210,219,213,204,210,225,204,211,222,207,210,214,225,207,214,227,205,207,206,220,166,173,198,192,173,213,208,209,210,209,205,211,212,213,211,216,211,213,210,214,209,212,208,212,172,206,240,223,245,233,175,155,189,207,156,127,83,55,88,181,200,140,127,130,186,237,252,246,227,229,244,255,243,232,228,239,253,253,239,225,236,247,252,243,230,228,242,255,249,232,232,240,248,254,244,231,232,244,252,247,236,229,236,253,],
	[244,245,220,225,208,213,221,211,207,223,213,203,225,208,211,210,223,207,215,222,210,214,205,210,131,143,149,142,130,217,214,211,207,208,211,210,209,210,208,212,208,209,207,210,210,209,212,210,174,211,230,229,245,231,182,152,195,226,179,138,88,48,91,192,215,154,145,137,196,251,255,241,223,230,245,251,243,233,228,239,255,255,241,224,233,248,255,244,229,229,242,255,248,235,229,243,254,253,245,228,234,243,255,246,234,231,233,254,],
	[249,217,183,145,183,210,219,208,221,224,208,213,227,212,207,214,217,212,213,217,212,216,206,206,175,199,142,125,153,216,210,209,212,210,213,209,211,210,209,210,209,210,210,212,210,206,212,211,171,210,239,244,243,219,175,146,200,233,191,145,86,39,89,189,215,186,163,162,197,235,232,239,245,246,242,235,238,243,246,242,238,239,243,246,241,240,238,239,243,246,241,234,241,245,241,245,241,235,241,237,250,238,242,237,241,246,239,255,],
	[245,248,228,217,195,198,210,210,207,210,206,197,225,211,208,218,217,215,216,219,206,216,212,203,196,220,193,211,212,210,205,208,218,207,210,209,211,209,208,206,208,209,212,212,213,207,210,215,165,205,255,255,243,213,170,152,213,237,196,153,79,36,86,189,224,194,166,166,197,229,224,244,255,248,235,224,234,249,255,245,227,225,241,255,249,234,225,236,254,255,240,223,232,251,252,245,229,222,239,248,255,232,228,232,249,255,244,254,],
	[236,251,255,239,246,243,218,212,226,226,211,216,225,208,211,217,218,210,216,217,214,207,201,212,216,213,216,212,216,202,218,210,212,208,214,210,209,208,208,204,208,208,211,210,215,210,207,217,165,201,255,252,237,213,167,159,220,236,197,156,82,42,86,197,234,178,169,163,195,238,230,241,250,244,240,232,237,245,251,247,237,233,240,249,247,236,232,240,249,248,239,233,234,249,251,244,236,233,242,251,251,233,231,237,249,250,241,252,],
	[235,236,237,240,234,212,180,183,192,210,208,205,224,208,209,221,213,207,212,217,223,213,212,216,208,193,215,203,206,203,222,211,207,211,217,211,213,212,214,209,214,212,214,210,213,212,205,214,171,205,249,234,246,237,181,169,224,235,201,160,80,41,92,206,227,180,182,177,193,252,244,240,239,239,250,251,242,236,235,243,250,248,240,233,237,242,247,244,237,234,240,248,246,240,237,239,250,249,240,244,235,240,245,244,239,234,238,252,],
	[208,141,117,206,227,213,174,178,192,216,212,217,221,209,203,230,206,214,212,225,203,201,216,211,216,215,218,201,236,213,206,210,212,210,209,211,210,209,213,208,214,209,211,205,210,211,204,210,178,211,235,219,239,245,181,164,215,232,206,166,72,33,99,208,201,184,170,167,188,255,253,240,225,219,242,255,243,228,222,236,252,252,238,224,229,246,255,246,230,228,240,253,255,231,221,231,255,255,232,232,225,248,255,247,230,222,237,253,],
	[154,61,82,112,136,187,212,230,243,221,213,209,233,202,204,224,220,211,213,223,213,206,215,198,196,197,205,198,199,208,202,207,197,209,206,202,203,207,199,199,209,199,206,206,195,218,223,200,170,206,248,228,249,227,188,170,213,247,211,166,79,35,98,200,215,175,174,164,205,245,238,255,221,232,250,243,244,243,243,242,240,240,238,240,243,243,243,240,237,240,240,246,246,244,233,239,243,247,240,239,241,240,246,247,231,241,238,253,],
	[171,62,92,65,76,88,126,153,172,205,181,201,212,220,211,206,219,205,219,213,210,215,201,159,193,176,171,181,178,160,177,171,182,162,173,175,168,178,178,176,178,169,177,170,150,198,207,207,171,202,254,250,247,214,168,165,215,234,192,157,77,43,98,203,212,183,174,167,181,239,233,227,255,255,233,229,233,253,246,233,251,234,242,240,231,241,229,243,254,253,229,232,225,247,255,242,224,238,233,244,252,245,231,230,242,249,255,245,],
	[201,130,97,99,87,79,85,90,144,126,147,155,203,213,220,214,226,200,217,217,216,211,207,160,175,174,189,181,170,199,178,181,175,183,190,190,174,185,185,177,179,184,187,152,119,198,211,218,171,197,251,252,244,201,144,149,197,192,144,121,72,46,88,191,183,152,141,155,194,226,198,175,222,202,240,221,236,246,244,248,213,228,251,255,255,219,229,224,248,250,248,221,233,248,237,255,239,217,241,255,255,240,219,231,254,251,240,252,],
	[193,162,173,134,112,93,80,75,92,107,129,184,244,208,214,220,223,213,213,214,211,211,198,167,190,211,210,194,216,201,206,203,154,179,152,193,214,209,209,208,208,213,212,166,100,197,218,214,171,204,248,243,239,199,133,136,179,158,110,94,51,47,76,148,146,136,104,76,81,113,113,87,34,54,80,94,143,85,157,150,234,225,231,250,229,236,248,247,242,242,238,240,243,245,237,243,237,238,243,243,236,238,238,248,242,226,175,242,],
	[235,175,138,163,144,122,96,101,70,101,105,123,204,197,199,219,210,212,212,222,222,204,185,166,191,199,204,213,220,205,216,179,169,140,116,183,221,204,208,220,213,203,205,168,103,191,218,203,172,208,239,230,239,207,134,136,181,160,115,95,60,50,78,132,108,65,41,35,25,14,24,62,52,56,33,62,57,61,72,60,111,148,169,143,214,255,239,251,229,221,254,250,252,243,224,234,244,255,245,216,226,255,255,214,161,133,100,229,],
	[210,158,82,90,116,121,112,65,88,89,90,114,136,142,105,168,218,218,213,211,210,217,208,156,192,214,227,211,195,233,211,190,188,170,166,198,219,208,214,219,213,212,213,167,120,193,219,206,175,207,233,229,243,211,133,139,188,167,122,92,63,56,89,137,133,108,98,85,97,94,79,43,59,50,77,84,135,86,92,94,61,73,63,104,141,177,246,251,228,230,243,249,254,237,213,249,254,242,250,235,234,235,193,146,116,96,97,239,],
	[162,38,64,59,73,97,120,127,102,52,90,65,91,78,36,119,214,197,212,221,207,213,193,155,207,190,174,186,194,176,188,203,160,188,179,187,185,182,185,183,179,190,196,146,112,195,216,213,172,209,246,248,238,204,127,144,191,162,119,86,52,45,96,167,163,129,125,128,166,185,195,152,120,92,71,71,88,140,155,143,110,102,71,82,75,124,132,189,226,241,242,239,235,235,254,250,230,241,237,244,201,151,106,107,121,136,125,241,],
	[194,96,93,57,53,64,106,143,162,99,71,64,58,24,19,111,230,210,226,221,207,220,198,118,87,89,101,99,108,80,94,89,102,96,95,99,99,89,97,102,92,90,109,86,99,200,215,216,160,203,253,253,226,192,124,151,196,160,121,95,58,47,81,141,142,134,132,119,158,221,230,233,223,155,109,69,42,68,80,131,161,141,129,85,107,69,86,101,170,221,225,233,235,247,251,246,232,240,225,190,144,104,95,91,67,113,101,233,],
	[211,141,119,105,53,58,38,90,141,128,107,52,35,53,19,64,120,126,136,207,204,205,225,184,200,195,176,190,190,189,202,177,199,185,182,192,191,190,196,190,189,194,195,193,192,215,213,212,158,206,251,246,249,186,129,156,187,166,130,99,52,44,75,143,136,129,133,128,151,217,227,253,239,218,177,131,93,87,93,89,106,166,156,167,110,99,81,98,94,149,205,219,235,247,254,251,244,200,149,109,86,76,83,106,102,110,73,230,],
	[202,70,66,74,93,35,33,23,49,58,46,26,67,27,41,61,72,68,86,190,224,210,218,206,219,202,224,225,203,214,207,215,208,213,230,203,211,217,202,214,222,198,203,221,213,214,202,211,174,210,244,236,247,197,133,148,196,169,125,98,53,42,79,139,133,132,125,121,180,232,254,231,168,172,101,82,52,95,42,53,56,50,120,144,132,115,100,96,96,98,128,207,238,241,222,239,214,109,54,49,86,108,146,126,99,107,130,229,],
	[195,101,98,99,80,39,38,30,9,34,21,43,6,75,110,173,116,52,72,202,225,210,212,219,214,218,209,213,218,221,214,222,208,214,214,211,214,210,217,211,212,225,213,208,200,210,213,218,182,201,229,225,243,203,133,143,203,172,123,99,58,43,82,136,131,132,115,113,171,222,254,222,171,118,135,149,173,168,139,89,71,56,52,75,117,105,105,96,73,101,92,140,193,224,242,150,81,35,63,89,126,147,98,113,130,155,201,253,],
	[183,95,88,100,81,54,73,25,23,6,51,121,100,87,151,144,169,81,73,160,133,140,152,130,146,153,147,154,149,133,155,163,161,157,157,168,162,138,163,163,150,162,149,162,166,163,149,147,155,208,250,245,238,194,127,149,201,173,129,101,52,38,74,132,131,129,118,121,171,225,253,248,233,226,231,253,251,253,216,166,118,85,78,47,98,106,116,98,87,69,82,127,123,136,141,83,42,99,124,148,111,99,121,169,220,237,242,255,],
	[203,121,145,144,95,96,57,3,31,19,137,158,79,90,118,115,92,80,100,75,71,81,38,43,50,92,98,95,101,115,111,124,197,230,235,225,215,222,221,231,231,228,211,209,220,239,237,216,200,225,254,255,239,181,118,158,195,172,137,101,53,43,68,133,131,120,119,125,163,225,224,238,255,254,240,230,235,254,246,223,167,122,104,76,27,81,105,154,107,64,71,73,64,48,30,67,102,123,107,117,122,144,183,214,241,253,241,253,],
	[224,255,253,234,213,199,106,92,49,55,121,121,98,105,71,84,96,104,79,68,65,65,73,57,33,51,93,118,121,153,134,103,145,230,251,231,204,188,193,221,224,231,226,238,239,248,243,236,231,242,248,252,244,178,112,153,193,168,138,98,52,47,69,136,136,121,122,128,159,229,224,243,227,214,155,145,139,154,135,142,119,77,81,90,99,24,73,123,139,112,60,39,34,15,49,83,121,99,98,126,184,243,221,246,252,255,245,254,],
	[255,245,221,193,155,86,73,112,114,129,101,121,95,84,92,87,71,100,96,74,75,53,73,72,29,42,34,69,94,131,177,167,115,168,199,161,124,107,119,124,115,167,190,202,214,227,233,248,251,250,233,235,245,186,106,129,199,163,128,94,41,40,71,133,137,130,121,124,150,197,152,133,133,109,121,90,103,106,104,110,110,82,82,89,31,40,16,36,66,65,47,45,51,23,37,61,65,93,108,166,175,188,220,220,217,226,238,253,],
	[238,194,131,110,116,119,96,86,95,115,157,183,102,90,112,88,97,106,104,63,44,16,16,43,61,63,47,60,76,111,99,130,122,108,137,122,129,105,74,106,119,130,130,124,142,185,221,251,252,244,219,226,244,195,102,105,206,158,119,90,49,47,82,130,129,124,96,93,112,104,78,85,75,95,97,126,109,88,96,92,114,121,113,74,52,61,43,5,22,29,16,35,45,82,83,90,92,89,80,95,108,139,164,174,164,180,202,245,],
	[183,113,109,113,146,98,97,103,104,129,153,161,108,86,92,96,118,123,133,93,79,60,49,10,41,21,51,57,104,105,116,108,108,129,165,168,139,131,88,110,134,143,141,156,154,195,232,250,250,244,238,225,243,196,102,125,202,165,136,92,47,38,94,110,95,74,41,55,79,116,124,141,147,173,181,191,184,169,175,150,151,140,123,89,68,68,13,20,18,65,111,78,111,152,160,86,47,68,83,96,111,105,134,169,142,113,138,240,],
	[197,126,131,103,94,120,118,109,97,103,146,139,105,100,122,108,140,158,186,152,69,155,124,94,16,10,17,21,50,147,130,94,93,129,169,199,171,136,97,110,121,201,223,220,232,236,237,233,236,247,252,250,249,187,108,136,192,160,146,102,40,42,76,84,67,66,57,91,156,208,217,238,248,252,229,233,235,250,254,238,237,218,227,217,184,86,52,10,45,148,146,81,106,149,136,127,83,82,83,106,92,108,115,109,152,151,137,238,],
	[196,102,127,163,161,119,109,101,132,128,164,132,120,81,85,132,156,170,181,198,173,121,183,208,123,53,25,35,43,122,140,129,90,88,118,150,189,142,108,137,142,208,238,218,249,255,253,232,221,237,252,255,234,168,103,139,190,158,136,90,52,39,42,75,94,118,117,147,191,232,233,245,244,255,239,237,233,247,254,241,233,222,198,180,124,92,86,74,123,152,108,80,94,82,89,83,78,94,67,61,62,68,89,119,117,127,150,236,],
	[175,125,184,213,130,118,90,102,121,129,134,146,158,118,119,144,150,144,169,212,224,173,128,199,219,198,144,59,81,68,110,133,136,117,111,119,111,114,111,112,143,211,250,239,237,246,247,243,231,231,247,244,230,170,103,121,160,125,101,82,35,44,79,137,136,114,104,125,183,223,233,250,245,251,233,227,233,252,235,220,147,123,139,105,112,83,103,112,131,98,124,83,95,89,79,102,84,76,73,88,61,34,45,103,95,121,115,228,],
	[208,191,221,190,160,134,109,140,139,110,131,123,155,152,147,167,149,157,161,193,177,181,146,114,191,246,253,190,137,103,89,66,111,112,88,86,108,74,53,65,145,214,237,255,247,235,222,249,255,241,245,224,210,132,74,104,144,113,76,53,56,57,98,161,160,122,108,106,159,226,251,242,220,234,246,254,247,212,156,133,141,129,113,127,97,112,96,111,120,173,122,98,102,91,85,85,103,65,56,51,84,69,19,64,98,103,128,247,],
	[247,234,223,165,134,135,126,157,122,93,83,135,163,148,152,148,174,142,144,133,80,130,147,136,129,205,255,238,227,181,129,81,72,82,78,58,14,55,70,125,151,140,171,213,230,228,223,250,255,241,231,189,159,102,90,111,104,87,88,84,69,79,117,160,153,116,109,106,133,169,226,253,230,228,246,238,179,139,114,135,135,101,118,114,126,87,118,97,154,152,107,99,93,96,113,120,86,74,34,16,25,55,70,29,53,85,132,234,],
	[240,255,240,167,133,111,148,105,67,56,66,112,174,183,159,145,143,128,131,122,145,129,109,127,134,181,218,240,255,243,233,172,68,47,55,59,58,62,54,147,193,123,124,179,202,189,185,187,182,181,170,138,114,116,151,154,120,114,105,76,62,68,84,129,147,131,118,127,110,133,168,193,232,247,221,149,111,148,142,105,93,169,172,133,104,105,106,123,136,160,109,96,101,115,141,165,150,83,88,78,24,28,42,52,69,74,105,234,],
	[251,250,197,170,128,98,140,136,140,96,67,71,130,155,140,152,147,134,152,167,173,140,130,85,114,153,188,229,241,255,249,171,118,101,44,51,65,44,51,87,175,141,108,188,200,125,87,75,76,90,87,86,75,140,197,172,136,143,116,67,56,65,81,159,208,191,140,142,110,119,115,160,252,197,128,127,137,123,81,153,174,233,165,131,114,106,126,130,176,115,100,100,84,136,164,156,193,134,128,176,101,32,3,29,20,109,117,241,],
	[252,250,219,147,130,100,100,129,128,134,117,67,87,146,168,161,138,135,144,147,133,132,123,118,86,82,133,172,234,251,236,171,120,154,141,69,47,45,51,83,151,140,145,193,202,123,60,67,74,68,62,72,84,156,201,169,140,140,112,85,62,50,84,153,211,185,141,139,126,122,114,159,180,133,141,143,90,110,197,238,227,189,148,131,101,117,110,128,126,104,110,80,86,142,160,177,174,220,134,158,219,120,53,22,31,57,148,244,],
	[243,226,184,163,137,105,85,105,129,147,140,109,123,149,169,146,117,113,139,129,130,136,135,177,123,117,148,145,207,231,247,195,144,135,174,139,89,40,34,63,125,136,112,137,166,157,137,140,132,126,128,137,126,158,148,143,143,131,110,67,58,67,74,134,181,165,129,121,142,159,158,123,94,131,112,107,127,209,234,239,252,175,148,109,121,119,131,150,97,113,119,86,84,112,163,186,220,235,244,132,162,225,202,100,60,69,110,231,],
	[255,226,182,154,108,82,70,94,110,96,97,109,114,135,150,149,101,132,148,119,58,66,88,151,142,104,145,160,187,226,228,248,182,116,157,181,120,69,46,48,75,106,89,90,160,201,212,230,238,233,218,197,220,207,131,136,178,178,164,92,49,50,60,98,100,125,133,138,197,228,168,121,137,103,104,162,220,230,229,239,240,179,123,93,133,134,132,131,102,123,127,101,99,132,165,163,226,255,241,209,114,209,252,224,139,105,77,221,],
	[246,225,190,98,103,99,95,97,97,93,108,120,133,123,156,159,122,112,145,142,140,109,85,130,142,102,103,148,175,186,240,240,182,148,164,158,143,93,60,80,91,86,83,87,163,226,236,246,251,250,248,233,245,217,131,131,201,228,208,122,58,33,59,80,78,130,146,168,223,211,147,111,110,83,182,235,246,233,235,227,203,147,144,89,89,146,144,140,116,102,107,102,95,115,142,195,221,249,244,230,189,134,242,255,224,177,143,234,],
	[240,242,188,78,72,73,101,134,135,133,139,126,103,104,138,143,157,161,169,138,160,163,136,100,163,121,79,140,160,192,208,218,172,131,170,181,156,102,51,69,122,128,104,76,174,247,252,238,228,233,249,252,249,209,150,128,172,192,169,119,69,69,106,120,133,151,150,181,215,131,80,96,89,178,242,242,223,248,255,251,179,154,131,115,112,129,99,120,101,125,122,104,115,137,169,205,231,225,252,241,255,167,161,247,251,238,227,252,],
	[243,247,158,93,116,94,116,152,117,107,130,137,130,142,158,167,174,181,183,196,195,180,160,125,108,151,100,77,140,158,179,210,163,148,169,169,145,87,60,90,143,127,89,102,177,247,252,233,224,236,249,250,240,199,177,143,129,99,78,82,83,85,104,106,114,133,180,199,140,75,94,69,176,248,230,223,247,248,246,236,175,128,135,154,109,109,114,135,130,151,122,94,84,106,194,223,218,229,235,253,247,232,127,200,252,251,247,252,],
	[248,207,140,128,117,116,135,145,84,101,145,154,144,165,180,166,192,197,191,189,213,204,202,169,123,146,150,128,153,124,98,133,195,152,173,179,141,90,59,104,169,163,97,81,183,238,249,240,234,242,247,253,252,236,241,205,139,57,36,47,37,34,66,90,120,179,232,187,111,99,83,176,251,234,246,230,241,229,249,235,161,142,107,141,148,138,121,100,115,129,117,109,88,121,184,236,229,241,230,239,246,248,220,143,236,242,245,250,],
	[235,140,139,155,117,117,136,158,104,123,153,152,157,171,163,176,180,192,167,186,187,201,202,215,170,122,124,138,129,108,96,98,176,177,161,165,149,96,38,103,163,155,108,91,184,214,231,250,255,249,225,219,236,248,255,228,160,86,95,100,96,98,126,121,144,206,210,133,90,87,187,240,231,226,253,244,254,240,218,214,174,124,109,122,130,138,103,103,111,124,108,86,85,125,193,219,255,247,245,227,221,254,232,205,183,237,233,254,],
	[181,106,151,128,119,131,143,138,123,145,142,151,151,163,168,177,186,179,191,183,197,197,211,209,231,185,157,116,138,153,151,114,167,179,178,167,145,98,54,105,165,148,100,96,172,221,235,246,255,249,230,230,244,254,255,202,143,75,109,112,95,103,133,117,121,189,182,108,56,159,254,238,222,227,253,244,250,229,216,200,153,156,130,133,150,125,77,87,131,138,114,95,98,122,198,239,247,254,246,232,228,244,251,242,174,219,236,253,],
	[198,98,134,149,114,138,138,128,142,146,148,151,155,159,167,189,201,195,191,187,199,213,203,211,210,234,203,146,128,143,161,185,167,170,172,161,141,97,55,107,167,149,99,94,177,240,245,238,243,241,244,238,241,244,240,211,143,71,91,87,76,81,83,94,108,158,149,79,96,218,241,239,248,251,238,241,241,243,253,223,176,143,125,114,140,106,92,107,124,131,107,98,106,125,212,241,236,237,241,243,241,241,238,237,192,207,245,253,],
	[220,125,116,151,128,133,139,150,165,137,153,158,165,159,158,178,185,193,185,197,200,183,185,196,222,187,217,161,109,144,152,177,167,174,173,162,143,98,55,107,167,150,101,96,177,254,251,230,227,230,254,249,243,222,231,246,166,113,142,113,65,60,100,104,129,144,126,108,189,241,221,242,248,249,227,239,226,247,255,205,144,108,132,153,151,105,118,120,107,118,103,104,110,126,234,251,232,223,238,254,251,239,226,233,221,198,248,252,],
	[244,198,125,132,142,111,128,160,147,135,161,155,163,148,131,136,150,191,190,211,197,169,180,150,185,195,226,192,104,121,144,156,200,178,168,163,147,98,52,102,164,149,102,99,179,253,250,239,230,235,251,255,243,232,239,244,152,97,137,119,75,77,104,122,117,154,134,191,255,234,231,247,242,245,233,242,233,248,247,212,152,131,133,163,153,105,123,110,93,102,103,102,96,109,238,255,237,228,240,251,246,242,228,236,245,197,232,250,],
	[243,241,133,102,121,93,109,125,95,144,160,137,142,131,105,89,117,186,196,211,185,161,147,167,176,165,193,214,102,89,143,131,170,187,193,168,148,97,48,98,160,147,101,97,177,232,235,248,244,246,232,240,233,255,249,229,176,97,102,103,90,103,97,121,139,186,187,229,240,242,253,243,238,246,240,245,247,235,223,197,149,141,120,163,141,98,112,103,106,98,108,105,82,86,219,245,246,245,243,234,232,249,244,242,248,199,205,251,],
	[245,250,149,99,83,89,108,100,73,143,139,132,147,152,128,81,100,167,196,213,187,164,183,191,202,187,167,197,140,72,132,153,158,178,165,163,146,97,52,102,162,147,100,96,177,219,225,248,254,252,221,223,234,255,254,234,209,127,116,122,86,94,111,115,184,228,224,220,229,255,253,242,222,233,247,252,244,227,232,208,172,144,111,157,135,98,103,103,116,96,107,107,90,85,206,238,252,255,245,224,225,252,255,245,239,208,188,252,],
	[243,242,148,89,66,82,107,93,85,134,122,134,163,180,163,100,114,161,203,222,208,192,169,187,208,212,196,189,140,58,101,108,115,159,156,172,142,98,57,105,162,144,98,98,182,234,238,243,251,247,239,236,246,244,249,237,203,122,114,126,76,80,98,117,178,245,226,231,251,247,243,248,228,233,247,254,250,237,240,214,179,131,97,141,117,94,100,98,102,94,100,98,95,94,209,242,246,244,243,236,234,244,246,244,238,232,195,244,],
	[247,246,147,80,67,68,90,75,91,132,126,129,148,164,157,102,132,168,208,218,222,206,188,175,194,202,215,208,165,65,118,108,112,132,150,167,138,97,57,104,156,137,95,98,178,248,250,229,233,228,255,253,245,226,231,250,246,158,106,103,80,84,91,132,195,236,250,255,249,224,235,242,252,251,230,235,233,247,254,227,168,109,90,149,90,88,106,102,92,107,107,96,81,93,211,245,235,226,241,255,247,234,230,243,246,255,211,235,],
	[243,255,155,74,86,89,89,89,89,137,135,143,143,151,142,136,149,177,218,208,228,207,216,210,211,216,208,239,203,88,129,170,136,145,160,165,138,106,58,107,159,148,101,108,185,252,254,238,221,232,254,254,243,222,221,250,244,183,132,128,88,88,104,138,205,225,247,253,255,220,225,242,249,247,247,222,241,249,255,235,177,96,97,134,93,104,94,79,112,98,112,116,89,103,217,255,234,222,238,255,255,238,220,228,254,255,221,237,],
	[250,240,139,105,103,100,90,90,103,160,159,168,164,161,155,144,154,171,208,207,216,221,203,202,216,210,214,205,213,149,142,177,150,162,162,150,134,93,62,109,158,123,75,103,176,235,240,243,241,246,242,244,241,242,245,251,242,209,182,181,147,157,165,194,236,237,247,234,239,235,242,240,246,242,238,238,244,244,247,248,189,89,116,89,101,136,110,108,136,120,131,130,78,100,214,249,237,236,241,238,242,239,239,240,246,238,234,245,],
	[254,216,119,129,121,120,99,90,109,170,167,177,169,159,160,145,147,159,203,223,199,222,194,196,218,213,229,209,213,167,135,167,160,170,136,90,70,64,52,86,122,75,54,95,191,232,232,244,253,249,227,226,241,251,245,224,221,231,243,248,227,225,210,233,253,244,247,227,231,255,253,240,234,227,244,253,241,238,225,234,204,139,102,89,112,146,157,164,151,174,181,113,67,85,199,244,245,251,248,232,230,240,255,250,236,220,242,250,],
	[250,211,124,133,139,148,119,93,106,166,160,168,171,162,158,132,111,123,168,205,192,211,195,195,209,212,216,224,222,169,158,181,184,184,138,86,70,78,54,83,117,75,66,86,125,139,139,178,225,223,203,178,186,195,190,168,169,182,190,181,184,184,186,227,241,233,224,208,222,247,235,232,217,213,255,254,240,241,227,226,220,177,81,116,116,151,224,222,163,211,224,128,72,79,191,245,249,248,245,237,235,240,254,249,234,223,240,253,],
	[247,232,168,137,146,158,120,79,91,158,152,158,166,164,154,127,94,112,150,185,192,205,193,194,200,212,202,219,207,173,165,180,183,179,148,118,93,62,25,67,122,112,80,72,80,63,53,96,148,142,160,146,148,148,156,154,163,159,153,138,151,142,140,157,162,178,177,183,173,184,181,187,186,193,217,223,229,243,244,247,231,164,105,138,104,152,229,248,235,253,220,105,82,90,202,249,239,229,236,244,248,240,239,240,242,245,237,255,],
	[249,254,220,147,143,148,102,61,80,157,154,158,160,166,162,148,130,151,181,196,210,225,199,198,203,216,210,208,206,202,161,173,188,188,156,124,113,64,49,74,115,127,80,61,63,46,48,92,138,120,139,137,140,127,133,134,138,120,114,104,111,109,113,123,145,181,176,176,185,183,187,175,179,182,156,181,221,251,248,255,229,173,125,155,114,184,221,234,248,249,226,109,73,94,211,249,230,223,239,253,251,237,228,232,247,255,236,255,],
	[252,249,237,143,142,139,96,66,87,161,158,161,164,174,176,165,158,159,180,171,205,224,215,206,211,211,214,204,214,215,183,172,182,183,153,113,102,71,87,111,125,121,104,83,73,60,42,49,82,81,72,74,70,59,69,71,78,64,66,57,58,60,62,75,102,123,115,112,114,111,119,111,113,123,108,144,201,250,242,241,234,214,138,170,150,217,232,236,251,255,235,99,65,85,204,247,237,234,244,249,244,239,234,233,244,246,237,252,],
	[253,229,226,128,136,131,97,75,93,155,148,152,159,168,175,155,148,124,140,116,149,176,214,208,216,207,212,214,208,205,226,178,155,150,145,122,117,85,99,150,152,104,115,86,81,101,81,35,32,44,20,40,36,24,29,21,30,28,38,27,33,37,31,39,45,32,32,44,39,38,39,47,35,52,86,115,151,220,234,230,254,242,183,191,170,234,253,244,238,235,214,115,72,80,193,247,249,243,239,231,239,247,248,240,241,231,241,247,],
	[255,228,211,112,105,73,72,91,113,158,169,157,155,165,165,142,148,140,154,152,138,147,157,196,223,221,205,219,215,201,175,157,145,173,148,126,115,81,85,121,136,109,94,79,82,127,118,100,61,58,46,67,59,65,66,70,63,62,58,72,67,57,55,68,65,50,55,66,61,53,50,69,54,77,139,118,146,212,229,237,246,255,209,217,206,248,255,244,223,229,227,103,92,66,178,242,247,254,246,223,230,243,255,252,226,230,237,252,],
	[241,226,224,113,85,121,96,82,75,135,167,151,155,165,176,163,138,165,182,187,199,198,188,184,213,218,215,216,207,169,104,136,182,188,137,114,122,99,79,85,102,103,99,84,82,113,124,134,105,72,83,71,70,70,74,95,91,77,65,83,73,68,74,70,68,76,73,71,78,75,61,65,67,108,180,186,188,239,238,243,243,247,233,246,229,249,241,242,240,235,218,105,101,82,187,247,242,242,242,235,238,240,244,241,242,238,238,253,],
	[244,255,255,134,91,78,75,112,71,129,165,164,151,151,163,160,139,170,165,168,178,207,222,211,216,211,216,223,204,137,68,141,193,158,118,132,110,100,84,98,105,90,77,82,163,205,211,202,170,150,174,155,164,146,132,138,116,81,77,91,78,62,71,67,76,113,132,147,150,147,114,88,92,141,211,240,228,255,251,246,231,227,245,253,250,245,219,234,255,249,220,121,104,102,202,253,234,228,240,250,251,236,228,229,255,249,242,254,],
	[246,239,251,206,90,60,64,109,98,134,165,156,158,158,167,158,161,166,173,198,193,216,224,228,218,213,204,215,208,120,92,163,199,162,143,162,141,102,60,85,108,93,77,97,198,255,251,234,221,241,241,249,237,221,210,207,152,77,89,83,82,65,74,82,111,178,225,247,232,221,188,168,184,216,232,224,229,253,248,242,229,228,252,247,251,240,217,233,254,255,220,121,93,112,212,253,231,229,241,252,255,236,227,230,253,252,245,255,],
	[255,232,238,235,156,90,113,101,98,143,160,153,153,151,155,151,166,162,182,199,212,218,214,221,217,215,206,212,182,114,138,168,191,169,150,156,133,107,72,93,114,102,83,95,203,242,211,221,230,247,235,252,239,245,249,230,147,63,106,82,73,78,86,91,119,188,243,248,238,249,253,240,233,242,246,236,239,234,238,241,241,243,246,235,236,244,241,243,238,245,208,105,78,106,211,247,238,244,243,233,243,239,243,246,237,239,241,253,],
	[246,237,235,218,243,147,110,117,97,151,160,168,155,147,147,154,155,167,183,171,190,204,220,216,218,209,211,211,151,127,169,174,189,156,138,163,151,110,69,91,125,120,88,68,136,140,93,109,128,151,215,236,228,255,246,184,91,52,112,71,56,73,84,86,93,138,201,226,236,245,250,240,226,233,248,249,255,228,231,242,251,252,235,230,226,248,255,246,225,236,204,107,81,97,207,240,246,255,243,219,230,242,255,255,228,227,234,250,],
	[241,228,239,251,239,231,105,106,125,152,164,161,155,148,149,146,131,151,169,174,188,198,211,205,205,196,196,191,112,127,151,165,179,143,137,161,147,110,88,110,136,119,85,50,73,71,38,31,35,73,198,237,230,251,233,184,118,117,133,94,83,91,107,110,110,136,188,232,230,247,251,245,230,224,239,250,247,229,233,239,247,252,237,244,232,247,247,241,232,242,195,107,103,98,213,238,245,250,241,230,233,241,252,248,236,231,234,250,],
	[251,243,239,244,231,244,196,103,143,159,155,150,161,148,146,135,137,142,135,154,154,169,181,193,182,181,180,189,85,147,161,172,163,135,150,149,143,109,96,115,143,133,95,36,59,70,74,66,56,88,199,254,252,237,225,243,224,226,194,183,194,191,203,197,201,221,229,246,242,246,232,241,255,246,241,236,231,242,248,240,234,242,236,247,244,246,234,239,249,249,176,85,125,105,224,240,237,235,240,253,245,240,237,231,249,244,240,252,],
	[244,248,255,247,227,223,238,185,140,158,154,155,151,154,145,149,136,129,120,117,113,133,158,157,163,161,182,181,102,153,154,176,198,137,139,154,144,113,89,112,155,133,94,75,131,161,90,47,73,147,231,255,229,228,222,255,253,249,225,224,236,250,255,244,228,234,249,255,245,229,226,239,249,255,231,223,233,255,255,241,232,222,247,251,255,234,220,246,243,255,178,77,105,105,237,255,228,232,238,252,254,233,222,232,253,254,242,247,],
	[253,242,242,248,242,231,244,219,160,159,154,151,143,147,141,147,138,130,121,115,118,116,131,137,157,189,224,168,87,155,157,197,207,142,138,149,141,115,91,104,156,144,97,79,178,220,137,101,154,228,248,240,251,245,230,237,243,252,246,243,236,239,246,243,233,233,240,250,247,237,232,241,248,243,239,236,236,249,246,238,236,232,247,238,246,234,232,245,248,247,172,74,112,116,221,244,235,232,241,249,247,240,236,235,244,246,243,255,],
	[254,230,223,239,255,243,245,232,167,158,154,142,146,146,137,140,132,128,126,125,127,142,163,179,203,211,230,151,109,148,166,240,213,139,143,142,142,115,97,113,159,139,97,99,191,230,161,135,172,236,240,231,236,248,252,236,230,228,242,253,243,228,235,243,251,246,232,232,243,250,248,241,237,226,248,252,245,237,230,238,249,247,247,224,237,239,248,238,241,230,163,79,96,113,207,245,255,249,245,228,232,245,252,242,234,232,237,255,],
	[253,231,225,235,253,249,244,208,157,160,160,146,153,155,150,154,154,157,168,174,192,208,211,210,216,205,220,135,116,148,190,255,211,135,143,143,142,116,100,121,158,136,107,107,176,214,149,130,189,249,233,219,253,248,248,235,235,234,245,255,246,224,225,232,253,252,229,220,236,255,255,239,226,224,247,255,250,227,221,242,255,252,245,223,235,245,255,236,237,224,155,85,105,115,202,232,252,248,247,222,226,244,255,247,235,227,230,251,],
	[247,241,242,236,234,244,245,184,154,163,166,165,168,173,175,180,182,183,197,205,217,216,216,212,210,213,225,117,107,168,220,241,206,137,134,151,139,117,97,117,155,146,123,92,181,248,161,100,180,242,233,245,244,239,246,250,242,240,237,245,248,243,245,233,244,246,241,242,239,241,243,239,234,243,238,240,241,234,236,246,239,238,243,239,239,243,248,238,249,245,153,81,134,128,224,241,242,236,245,239,240,240,243,243,245,241,237,251,],
	[243,246,252,240,226,236,239,178,161,160,164,176,184,188,189,186,185,181,195,202,202,208,211,213,219,215,214,156,164,196,235,229,201,137,135,150,139,114,101,125,160,145,116,88,192,251,166,107,178,237,242,249,223,197,195,213,214,235,229,229,229,247,255,234,233,226,241,254,248,226,222,240,252,255,231,227,232,250,255,248,221,227,245,255,249,234,228,230,254,255,154,81,129,124,241,255,244,226,238,254,254,235,224,232,250,253,245,255,],
	[250,248,250,248,237,229,212,171,161,162,171,178,190,192,193,190,193,188,202,206,221,226,213,204,212,202,205,232,238,225,241,238,203,136,141,146,143,112,109,132,167,136,82,81,194,236,166,127,163,226,255,249,170,149,178,214,198,198,185,187,214,221,216,215,237,229,249,255,248,232,228,242,253,251,236,231,237,251,255,246,227,235,243,254,252,232,230,231,247,250,148,92,111,122,235,238,236,225,238,253,252,235,226,232,247,248,242,253,],
	[251,239,236,246,251,225,182,158,156,170,189,181,193,196,200,200,204,194,201,198,208,209,212,212,219,224,221,250,246,250,250,243,210,137,140,149,146,115,115,122,167,138,54,58,194,231,144,121,173,239,221,159,92,62,120,199,213,213,200,201,186,161,132,156,218,224,242,235,240,250,250,242,240,231,246,243,247,238,231,241,245,249,236,239,244,235,253,246,244,231,140,102,108,141,244,234,246,243,245,238,241,240,242,243,245,238,234,250,],
	[255,231,224,232,239,197,149,163,163,167,181,178,194,200,206,197,203,190,203,194,202,213,217,208,206,207,239,252,228,215,233,254,218,139,146,151,144,119,107,128,163,114,39,61,167,178,113,97,127,154,125,77,31,20,52,91,146,190,195,228,217,212,197,181,197,213,206,215,227,254,255,231,233,232,241,254,253,230,224,234,255,252,238,223,234,249,255,240,237,221,120,103,116,124,218,233,254,255,236,233,226,240,255,248,237,221,233,252,],
	[239,235,240,244,210,165,178,158,165,176,168,182,186,196,205,211,209,192,194,190,204,208,213,213,213,213,239,247,234,235,238,240,212,139,141,154,142,125,107,134,167,110,38,46,135,111,74,90,67,40,36,38,21,18,20,34,53,75,116,165,186,211,222,211,206,201,187,183,197,219,248,251,229,230,246,252,240,241,231,240,247,247,242,225,239,246,246,237,245,214,126,108,105,136,237,239,245,252,241,236,234,245,253,243,237,229,240,252,],
	[250,250,244,243,160,164,182,162,169,162,170,180,189,200,206,218,214,206,200,196,202,200,210,216,212,210,230,232,253,255,245,229,205,143,143,162,137,122,95,127,163,111,52,52,141,98,55,75,54,24,18,21,19,27,31,27,18,12,29,54,106,141,171,193,211,221,220,207,204,192,200,231,235,248,248,240,227,253,245,246,231,234,246,240,251,235,235,230,255,218,126,108,98,147,252,245,231,235,241,247,248,240,236,234,245,243,247,251,],
	[255,244,248,249,178,148,179,190,174,167,172,180,194,201,203,210,214,220,217,210,204,204,216,219,206,208,226,226,249,249,242,228,192,135,137,149,135,124,105,133,163,124,81,86,174,178,129,119,117,96,45,14,13,9,32,23,14,14,35,83,111,120,124,131,132,144,176,186,201,198,182,198,235,251,229,217,231,253,253,244,224,227,246,255,255,229,231,228,255,221,117,104,101,151,250,249,229,224,237,255,255,234,222,230,254,253,250,250,],
	[244,239,254,242,168,170,194,180,170,167,168,184,191,193,202,207,216,219,219,218,214,213,221,217,206,222,239,238,248,239,253,251,193,138,146,149,137,122,111,130,160,143,116,132,200,250,230,219,226,212,150,112,98,59,51,35,28,24,52,103,97,102,107,105,68,41,64,87,112,130,129,146,216,248,242,237,246,237,243,237,239,238,240,250,243,239,236,242,248,197,111,103,103,151,237,247,241,236,238,246,249,240,235,236,249,245,246,248,],
	[244,241,233,225,158,180,199,163,164,161,172,183,192,193,211,218,220,211,209,217,217,213,213,209,208,238,252,250,238,224,234,224,161,122,138,143,128,104,92,117,158,149,106,97,131,204,238,254,254,255,231,214,215,194,172,157,153,133,137,120,123,103,100,112,93,53,33,26,35,37,71,133,209,235,248,255,255,223,229,234,255,254,234,232,225,254,245,255,237,160,110,110,98,157,229,240,250,254,240,227,233,249,255,242,234,227,237,245,],
	[249,233,220,234,223,157,186,183,168,171,171,178,193,199,211,216,212,216,210,217,210,208,209,209,214,242,248,249,237,223,197,164,128,114,125,134,113,93,78,114,154,130,72,32,76,149,214,252,241,239,232,232,240,255,255,233,227,214,210,166,125,102,114,159,192,183,143,113,99,101,146,206,230,227,237,254,249,229,230,239,252,251,234,231,229,252,249,251,232,147,105,115,99,174,236,236,246,252,241,229,229,245,255,244,234,224,235,247,],
	[251,237,244,247,230,216,177,183,173,157,161,172,186,197,196,195,193,227,221,220,205,208,216,221,222,241,239,245,241,232,174,125,121,125,118,122,108,96,68,97,127,109,85,58,72,100,137,197,216,215,198,203,211,205,216,198,209,217,216,192,170,159,172,192,212,218,200,193,176,189,198,206,200,214,226,238,238,243,243,246,235,236,237,246,246,237,249,236,233,158,97,116,108,194,250,239,238,240,241,248,239,235,240,245,249,236,241,255,],
	[254,245,254,245,233,224,218,197,159,174,167,170,172,184,181,178,172,189,219,220,205,212,217,217,223,238,225,226,253,240,158,119,143,147,140,130,112,100,97,103,139,156,113,85,111,91,96,145,143,141,153,144,139,131,113,107,114,110,107,119,117,113,113,119,121,116,118,131,125,128,121,125,126,158,205,224,227,255,254,246,227,219,249,254,255,220,222,233,244,149,96,124,105,212,251,245,229,224,242,254,252,238,224,227,249,252,243,255,],
	[253,249,252,247,224,230,247,228,168,166,164,169,164,170,184,187,190,179,204,200,212,209,215,229,218,219,230,239,242,205,134,128,141,139,135,136,120,99,77,76,111,136,102,82,102,87,81,102,87,88,108,103,82,68,42,28,28,23,22,35,26,28,31,34,33,28,33,47,49,42,50,59,63,112,198,236,233,251,255,239,233,230,250,249,244,240,235,240,231,140,110,132,111,218,250,245,235,232,241,249,247,240,232,232,246,249,243,252,],
	[250,228,224,249,251,255,246,207,163,163,153,158,171,164,170,179,195,191,215,210,214,209,213,228,209,218,251,253,235,173,97,100,113,133,127,116,82,54,24,42,103,138,91,57,60,64,81,113,108,108,120,113,98,88,69,60,62,58,56,65,59,58,57,60,66,66,66,71,75,70,80,83,86,116,193,241,244,238,242,233,246,242,243,233,231,255,245,247,220,124,110,114,115,219,238,238,246,246,240,235,237,243,249,242,238,237,237,249,],
	[247,245,225,229,248,249,245,205,155,174,191,186,165,166,164,180,189,203,222,216,198,199,208,221,203,214,241,248,220,166,106,102,108,124,117,108,88,76,52,61,100,126,92,74,63,69,99,144,153,144,143,136,142,139,134,136,142,140,138,143,135,132,128,130,140,143,140,138,130,139,136,139,147,145,172,223,251,232,223,235,255,249,238,221,230,254,250,251,211,104,102,101,116,220,228,233,253,255,240,227,231,243,255,250,233,226,232,251,],
	[246,235,227,252,251,249,221,188,179,195,197,200,211,212,203,200,187,191,217,223,213,204,208,215,203,206,219,249,222,158,106,105,121,119,113,128,137,140,124,114,118,122,105,104,104,98,106,123,129,123,126,126,125,121,119,121,126,126,125,128,122,125,123,123,126,128,128,130,124,135,126,137,138,132,134,183,241,243,226,244,247,243,243,231,241,242,247,248,201,91,101,106,117,233,238,239,247,247,240,239,240,238,244,244,238,233,238,255,],
	[243,239,226,213,191,175,173,177,172,188,210,216,213,214,226,228,221,195,210,206,210,208,208,211,217,226,215,232,215,153,105,96,118,118,115,140,149,150,142,133,137,133,119,108,110,112,112,103,105,105,110,111,112,108,108,110,111,110,109,108,115,116,113,114,118,119,118,121,116,117,113,128,110,114,119,159,223,255,243,249,229,232,250,249,251,227,231,242,192,93,100,89,122,247,253,246,233,228,237,254,253,235,226,232,247,251,247,255,],
	[221,182,177,171,157,150,145,163,189,196,197,202,210,208,219,227,236,222,239,230,232,244,240,235,232,231,203,187,192,177,161,132,113,116,114,139,151,146,143,128,129,122,124,113,107,115,120,108,112,109,107,105,107,105,107,111,111,110,109,106,115,112,107,110,116,117,115,117,117,118,118,127,113,119,125,163,221,250,249,248,227,228,247,254,248,228,226,242,173,96,105,71,134,251,250,247,231,227,237,253,251,239,229,231,249,254,245,253,],
	[219,184,194,181,193,192,187,182,181,187,205,212,213,222,222,227,221,223,232,220,206,219,210,208,197,198,210,215,210,210,201,161,111,117,113,135,157,146,144,128,130,119,123,104,114,112,111,102,112,107,106,113,115,110,109,111,110,112,114,112,115,116,116,117,118,114,118,128,118,127,120,117,121,122,120,162,233,239,245,247,240,234,239,249,240,243,236,247,149,91,117,81,146,248,237,244,242,241,242,242,237,246,246,240,246,243,236,250,],
	[207,158,167,173,180,186,189,187,186,189,191,191,189,196,197,191,188,190,191,188,194,198,201,202,205,211,216,218,214,212,214,156,114,115,127,130,144,141,131,124,126,126,119,115,115,112,110,111,111,109,109,110,111,111,109,108,107,109,113,116,113,112,112,114,117,119,119,119,121,123,121,123,117,125,124,172,236,228,221,237,248,255,239,228,232,250,253,248,129,92,99,87,159,237,229,231,243,255,244,226,227,244,255,247,230,225,237,251,],
	[211,163,170,175,166,172,175,175,176,180,183,183,182,184,187,190,191,193,197,201,211,217,221,222,222,221,218,215,218,222,225,162,105,95,113,129,148,145,134,126,126,124,116,111,114,112,112,113,112,110,110,111,114,112,110,110,112,113,113,113,113,113,114,115,117,119,119,119,117,118,118,125,118,120,118,165,232,234,236,243,244,245,237,233,234,245,252,246,130,102,103,67,160,238,223,239,255,253,241,226,233,242,251,249,241,236,239,245,],
	[207,161,163,165,157,160,161,160,160,163,164,162,170,174,189,208,216,214,216,222,218,219,218,216,214,213,211,209,210,209,212,166,121,105,105,108,121,120,113,110,114,114,109,106,110,110,110,111,109,107,107,109,109,106,104,106,109,110,107,104,109,110,111,112,112,113,114,114,119,115,113,119,114,115,117,161,227,238,248,243,238,234,240,243,241,237,234,218,113,101,104,52,165,255,245,241,240,231,242,247,245,240,236,239,242,244,245,248,],
	[208,169,165,165,173,174,173,171,170,170,169,166,171,178,196,218,226,221,218,221,222,219,215,213,213,215,217,218,217,211,216,206,196,191,178,167,163,161,152,146,144,138,129,126,132,133,135,134,132,130,131,133,129,127,126,128,131,132,128,125,131,132,133,133,132,132,133,135,131,129,128,137,138,144,149,182,234,245,254,241,237,234,247,252,254,239,219,193,113,109,111,63,166,248,252,247,235,227,244,255,253,239,230,234,244,249,251,253,],
	[247,234,239,242,241,242,242,241,240,241,240,239,238,241,247,253,253,250,248,248,247,246,246,248,249,249,249,249,246,242,245,249,251,255,249,246,240,240,238,238,242,240,236,237,242,244,246,245,243,242,243,244,241,241,241,242,243,243,242,241,241,242,243,243,242,242,243,244,246,247,245,247,245,243,237,247,251,255,255,245,245,244,252,252,250,251,245,245,221,234,244,222,253,255,249,251,246,243,248,255,253,244,240,247,255,255,253,253,],
	[255,252,255,255,253,255,255,255,255,255,255,255,255,255,255,254,252,252,252,251,252,252,253,254,254,252,251,252,255,255,253,255,251,253,252,255,255,255,253,254,255,255,252,253,253,255,255,254,253,254,254,254,252,253,254,254,253,253,254,255,254,254,255,254,254,254,254,255,249,252,251,252,254,255,252,252,255,255,254,250,254,255,255,250,250,255,252,253,251,254,255,248,255,240,246,255,255,255,254,247,254,251,251,255,255,255,253,253,],
	[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,],
	[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,],
	[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,],
	[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,],
	[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,],
	[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,],
	[255,255,255,255,252,138,199,255,255,255,255,255,255,255,220,118,119,115,189,255,255,255,255,255,255,238,128,99,146,252,255,255,255,255,255,201,115,121,121,115,200,255,255,255,255,255,212,168,255,225,158,255,255,255,255,255,255,218,117,119,114,191,255,255,255,255,255,228,123,119,122,114,168,255,255,255,255,255,255,152,117,117,157,255,255,255,255,255,255,255,234,128,101,176,255,255,255,255,255,255,251,151,99,124,237,255,255,255,],
	[255,255,255,255,214,10,94,255,255,255,255,255,255,255,177,47,177,171,215,255,255,255,255,255,255,106,105,188,146,246,255,255,255,255,255,220,173,79,77,173,220,255,255,255,255,255,167,82,255,199,60,255,255,255,255,255,255,173,48,177,171,216,255,255,255,255,255,238,176,117,43,168,200,255,255,255,255,255,255,195,73,78,199,255,255,255,255,255,255,252,78,118,179,165,255,255,255,255,255,255,156,72,188,146,224,255,255,255,],
	[255,255,255,255,145,98,73,236,255,255,255,255,255,255,179,66,214,210,240,255,255,255,255,255,255,68,146,255,255,255,255,255,255,255,254,255,255,132,129,255,255,255,255,255,255,255,173,67,212,153,66,255,255,255,255,255,255,175,68,214,210,240,255,255,255,255,255,255,255,189,79,255,255,255,255,255,255,255,255,255,127,134,255,255,255,255,255,255,255,203,54,254,255,255,255,255,255,255,255,255,119,94,253,255,255,254,255,255,],
	[255,255,255,255,81,199,93,181,255,255,255,255,255,255,183,24,91,86,195,255,255,255,255,255,255,191,56,51,139,254,255,255,255,255,255,255,255,127,124,255,255,255,255,255,255,255,176,29,94,64,63,255,255,255,255,255,255,180,24,91,86,197,255,255,255,255,255,255,255,183,76,255,255,255,255,255,255,255,255,255,122,129,255,255,255,255,255,255,255,175,74,255,255,255,255,255,255,255,255,255,222,74,47,107,241,255,255,255,],
	[255,255,255,229,28,97,44,115,255,255,255,255,255,255,177,86,255,255,255,254,255,255,255,255,254,255,254,205,42,192,255,255,255,255,255,255,255,127,124,255,255,255,255,255,255,255,170,89,255,202,67,255,255,255,255,255,255,173,89,255,255,255,255,255,255,255,255,255,255,183,76,255,255,255,255,255,255,255,255,255,124,131,255,255,255,255,255,255,255,190,63,255,255,255,255,255,255,255,255,254,255,255,225,70,139,255,255,255,],
	[255,255,255,160,72,203,161,55,247,255,255,255,255,255,176,67,230,226,239,255,255,255,255,255,255,194,226,234,58,187,255,255,255,255,255,255,255,124,121,255,255,255,255,255,255,255,168,82,255,195,62,255,255,255,255,255,255,173,69,231,225,239,255,255,255,255,255,255,255,181,71,255,255,255,255,255,255,255,255,242,104,110,244,255,255,255,255,255,255,240,52,176,237,208,255,255,255,255,255,255,207,211,246,96,133,255,255,255,],
	[255,255,255,133,169,255,253,96,219,255,255,255,255,255,200,56,74,66,153,255,255,255,255,255,255,141,66,64,119,250,255,255,255,255,255,255,255,155,153,255,255,255,255,255,255,255,189,123,255,209,108,255,255,255,255,255,255,197,55,74,66,156,255,255,255,255,255,255,255,199,115,255,255,255,255,255,255,255,255,118,59,59,125,255,255,255,255,255,255,255,194,73,66,138,255,255,255,255,255,255,179,66,67,90,233,255,255,255,],
	[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,247,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,243,255,255,255,255,255,255,255,255,251,242,255,255,255,255,255,],
	[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,],
	[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,],
	[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,],
	[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,],
	[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,]
	]
	
	ip = ByteProcessor(w, h)
	
	for y in range(h):
		for x in range(w):
			value = img[y][x]
			ip.putPixelValue(x, y, value)

	imp = ImagePlus("Generated Image", ip)
	
	return imp
	
# GenerateImg end
#------------------------------------------------------------------------------

	
	





			


#????????????????????????????????????????????????????????????????????????????????????
#????????????????????????????????????????????????????????????????????????????????????
#????????????????????????????????????????????????????????????????????????????????????
#????????????????????????????????????????????????????????????????????????????????????
#????????????????????????????????????????????????????????????????????????????????????
#????????????????????????????????????????????????????????????????????????????????????



# Main Program



#????????????????????????????????????????????????????????????????????????????????????
#????????????????????????????????????????????????????????????????????????????????????
#????????????????????????????????????????????????????????????????????????????????????
#????????????????????????????????????????????????????????????????????????????????????
#????????????????????????????????????????????????????????????????????????????????????
#????????????????????????????????????????????????????????????????????????????????????







# Define flag
flagSave = 1
flagOpenDir = 0
flagFullColor = 0
flagExtactRedChannel = 0

imp = GenerateImg()

# Select Processing
gd = GenericDialog("Processing setting")
gd.addMessage("Select Processing")
gd.addMessage("")
gd.addMessage("- Main -")
gd.addCheckbox("Contrast Adjustment", False)
gd.addCheckbox("Change the Resolution              ", False)
gd.addCheckbox("Nombre Cut", False)
gd.addCheckbox("Sharpen", False)
gd.addMessage("")
gd.addMessage("- Optimise minAndMax -")
gd.addMessage("")
gd.addCheckbox("Full Auto", False)
gd.addCheckbox("Manual 3 Trials", False)
gd.addCheckbox("Mokushi Now", False)
gd.addMessage("")
gd.addMessage("- Renumbering -")
gd.addMessage("")
gd.addCheckbox("Renumbering [from 1]", False)
gd.addCheckbox("Renumbering [Automator]", False)
gd.addMessage("")
gd.addMessage("- Rename -")
gd.addMessage("")
gd.addCheckbox("Add Suffix", False)
gd.addMessage("")
gd.addMessage("")
gd.addImage(imp)
gd.showDialog()
if gd.wasCanceled():
	IJ.log("")
	IJ.showMessage("Exit : Processing was canceled.")
	raise RuntimeError("yamepi")

flagContr = gd.getNextBoolean()
flagReso = gd.getNextBoolean()
flagNombre = gd.getNextBoolean()
flagSharpen = gd.getNextBoolean()
flagOptMinMaxAuto = gd.getNextBoolean()
flagOptMinMax3Trials = gd.getNextBoolean()
flagOptMinMaxMokushi = gd.getNextBoolean()
flagRenumFrom1 = gd.getNextBoolean()
flagRenumATMT = gd.getNextBoolean()
flagAddSuffix = gd.getNextBoolean()



# print flag and set procTag
procTag = ""

if flagContr:
	IJ.log("flagContr = ON")
	procTag = procTag + "Contr"
	
if flagReso:
	IJ.log("flagReso = ON")
	procTag = procTag + "Reso"
	
if flagNombre:
	IJ.log("flagNombre = ON")
	procTag = procTag + "Nombre"
	
if flagSharpen:
	IJ.log("flagSharpen = ON")
	procTag = procTag + "Sharpen"
	
if flagOptMinMaxAuto:
	IJ.log("flagOptMinMaxAuto = ON")
	
if flagOptMinMax3Trials:
	IJ.log("flagOptMinMax3Trials = ON")
	
if flagOptMinMaxMokushi:
	IJ.log("flagOptMinMaxMokushi = ON")
	
if flagRenumFrom1:
	IJ.log("flagRenumFrom1 = ON")
	procTag = procTag + "From1"
	
if flagRenumATMT:
	IJ.log("flagRenumATMT = ON")
	procTag = procTag + "ATMT"
	
if flagAddSuffix:
	IJ.log("flagAddSuffix = ON")
	procTag = procTag + "Suffix"



# check flag
if flagRenumFrom1 and flagRenumATMT:
	IJ.showMessage("Exit : Two \"Renumbering Processing\" were selected.")
	raise RuntimeError("yamepi")
	
if flagOptMinMax3Trials and flagOptMinMaxAuto and flagOptMinMaxMokushi:
	IJ.showMessage("Exit : Three \"OptMinMax Processing\" were selected.")
	raise RuntimeError("yamepi")
	
if (flagOptMinMax3Trials and flagOptMinMaxAuto) or (flagOptMinMax3Trials and flagOptMinMaxMokushi) or (flagOptMinMaxAuto and flagOptMinMaxMokushi):
	IJ.showMessage("Exit : Two \"OptMinMax Processing\" were selected.")
	raise RuntimeError("yamepi")
	
if flagOptMinMax3Trials or flagOptMinMaxAuto or flagOptMinMaxMokushi:
	if not flagContr:
		IJ.showMessage("Sorry. Only optMinMax processing are not supported")
		raise RuntimeError("yamepi")
		
if not(flagContr or flagReso or flagNombre or flagSharpen):
	if flagRenumFrom1 or flagRenumATMT:
		IJ.showMessageWithCancel("Wait","Only Renumbering processing")

if flagOptMinMax3Trials or flagOptMinMaxAuto or flagOptMinMaxMokushi:
	if not (flagContr or flagReso or flagNombre or flagSharpen or flagRenumFrom1 or flagRenumATMT):
		flagSave = 0
		IJ.log("flagSave = OFF")

if (flagContr or flagReso or flagNombre or flagSharpen or flagRenumFrom1 or flagRenumATMT or flagOptMinMaxAuto):
	flagOpenDir = 1
else:
	IJ.log("flagOpenDir = OFF")

if flagAddSuffix and flagRenumATMT:
	IJ.showMessage("[flagAddSuffix and flagRenumATMT] are not supported.")
	raise RuntimeError("yamepi")
	
if not (flagContr or flagReso or flagNombre or flagSharpen or flagRenumFrom1 or flagRenumATMT or flagOptMinMaxAuto or flagOptMinMax3Trials or flagOptMinMaxMokushi):
	IJ.showMessage("Exit : Please Select any proc.")
	raise RuntimeError("yamepi")



# Directory Selection
if flagOpenDir:
	IJ.showMessage("openDir", "Select Directory for Processing")
	openDir = IJ.getDirectory("Choose a directory")
	IJ.log("")
	IJ.log("Processing : " +  openDir)
	
	# Count totalFiles
	totalFiles = 0
	countFiles(openDir)
	logWindow.toFront()
	IJ.log("... totalFiles = " + str(totalFiles) + " Files")
	
if flagSave:
	IJ.showMessage("saveDir", "Select Directory for Saving")
	saveDir = IJ.getDirectory("Choose a directory")
	IJ.log("Save to : " +  saveDir)
	IJ.log("")
	logWindow.toFront()

time.sleep(1)



# Select Nombre Type
if flagNombre==1:
	gd = GenericDialog("Nombre Cut setting")
	items = ["Use One ROI", "Case Divided by ODD/EVEN"]
	gd.addRadioButtonGroup("Select NombreCut Type", items, 2, 1, "Use One ROI")
	gd.showDialog()
	nombreRadio = gd.getNextRadioButton()
	if nombreRadio == "Use One ROI":
		NombreType = 1
	elif nombreRadio == "Case Divided by ODD/EVEN":
		NombreType = 2
		
		
		
# Clear ROI Manager and get ROI
if flagNombre:

	rm = RoiManager() # open RoiManager
	rm = RoiManager.getInstance()
	roiCount = rm.getCount()
	
	if roiCount > 0:
		rm.reset() # delete all rois in RoiManager

	if NombreType == 1:
		
		roi1_nb = getRoiNombre1()
		
	elif NombreType == 2:
	
		roi1_nb, roi2_nb = getRoiNombre2()

	
		
# Enter parameter
if flagSave:
	gd = GenericDialog("Processing Setting")
	if not (flagOptMinMax3Trials or flagOptMinMaxAuto or flagOptMinMaxMokushi):
		if flagContr:
			gd.addNumericField("min (0-255)", 0)
			gd.addNumericField("Max (0-255)", 255)
			gd.addCheckbox("Full Color", False)
			gd.addCheckbox("Extract Red Channel", False)
			gd.addMessage("")
	if flagReso:
		gd.addNumericField("pre dpi", 600)
		gd.addNumericField("post dpi(Target)", 400)
	if flagAddSuffix:
		gd.addStringField("Suffix", "suffix")
	if flagRenumATMT or flagRenumFrom1:
		gd.addNumericField("Digit of Page Number", 4)
	gd.addRadioButtonGroup("Output format", ["JPEG", "PNG"], 1, 2, "JPEG")
	gd.showDialog()
	if gd.wasCanceled():
		IJ.showMessage("Exit : Processing was canceled.")
		raise RuntimeError("yamepi")

	if not (flagOptMinMax3Trials or flagOptMinMaxAuto or flagOptMinMaxMokushi):
		if flagContr:
			min = int(gd.getNextNumber())
			max = int(gd.getNextNumber())
			flagFullColor = gd.getNextBoolean()
			flagExtactRedChannel = gd.getNextBoolean()
	if flagReso:
		preDpi = int(gd.getNextNumber())
		postDpi = int(gd.getNextNumber())
	if flagAddSuffix:
		suffix = gd.getNextString()
	if flagRenumATMT or flagRenumFrom1:
		digit = int(gd.getNextNumber())
	output = gd.getNextRadioButton()

	if flagFullColor and flagExtactRedChannel:
		IJ.showMessage("flagFullColor and flagExtactRedChannel can't be used together.")
		raise RuntimeError("yamepi")

	if output == "JPEG":
		ext = ".jpg"
	elif output == "PNG":
		ext = ".png"

	# JPEG quality setting 
	quality = 90 
	ioOption = "jpeg=" + str(quality) + " gif=-1 file=.csv save_column save_row"
	IJ.run("Input/Output...", ioOption);
	
	if output == "JPEG":
		IJ.log("JPEG quality =" + str(quality))



# SetResolution Setting
if flagReso:
	compressionRate = float(postDpi) / preDpi
	IJ.log(str(compressionRate))



# Reference setting
if flagRenumATMT:
	referenceCheck(openDir)



# net processing time
initialTime = time.time()



# optimiseMinAndMax Manual 3 Trials
if flagOptMinMax3Trials:
	
	min, max = showMeOptMinAndMax3Trials()



# optimiseMinAndMax Auto
if flagOptMinMaxAuto:

	seqPageNum = 0 # Sequential Number (0 to totalFiles)
	optAutoDetectCount = 0 # detecting images to be checked
	
	min, max = showMeOptMinAndMaxAuto(openDir)


	
# optimiseMinAndMax MokushiNow
if flagOptMinMaxMokushi:
	
	min, max = showMeOptMinAndMaxMokushiNow()
	
	
	
# Exit (only optMinMax)
if not flagSave:
	IJ.log("oshimai")
	raise RuntimeError("yamepi")	



# Make parentDirectory
parentDir = saveDir + "postProc_" + getTimeStamp() + "_" + procTag + "/"
os.makedirs(parentDir)




# Create Table
if flagRenumFrom1:
	rt = ResultsTable()
	


# Main Operation
procCount = 0
numberOfPage = 1
previousTitle = ""
listFiles(openDir)



# Clear ROI Manager
if flagNombre:
	rm = RoiManager()
	rm = RoiManager.getInstance()
	rm.reset()



# Fin


finishTime = time.strftime("%Y-%m-%d %H:%M:%S")
completionTime = time.time()

IJ.log("")
IJ.log(trueTitle)

rt2 = ResultsTable()


if flagContr:	
	IJ.log("Contrast Adjusted by...")
	IJ.log("min = " + str(min) + ", max = " + str(max))
	rt2.addValue("min", min)
	rt2.addValue("max", max)
	
if flagReso:
	IJ.log("Compression Rate =" + str(compressionRate))
	rt2.addValue("pre DPI", preDpi)
	rt2.addValue("post DPI", postDpi)

if flagNombre:
	rt2.addValue("NombreCut", "ON")
	
if flagSharpen:
	IJ.log("Sharpen")
	rt2.addValue("Sharpen", "ON")
	
if output == "JPEG":
	IJ.log("JPEG quality = " + str(quality))
	rt2.addValue("JPEG Quality", quality)
		
if flagRenumFrom1:
	rt.show("Correspondence table")
	rt.saveAs(parentDir + "Table_RenumFrom1.txt")
	rt2.addValue("RenumFrom1", "ON")


rt2.saveAs(parentDir + "Settings_Log.txt")

IJ.log("")	
IJ.log("Start Time .... " + startTime)
IJ.log("FinishTime ... " + finishTime)

elapsedTime = getElapsedTime(initialTime,completionTime)
IJ.log("ElapsedTime ... " + elapsedTime)

IJ.log("")
IJ.log("oshimai")
IJ.beep()

print "oshimai"



 

 

起動方法

 

① Fijiをダウンロード、インストール

 

imagej.net

 

 

② 上記プラグインをコピペして、「.pyファイル」に

 

Scriptを起動し、そのまま全てコピペ。

 

 

③ Fiji内「plugins」フォルダに保存

 

ファイル名「Almighty_Plocessing_Remake.py」と名付け、pluginsフォルダ、もしくはその配下のサブフォルダに保存。

 

Fiji再起動でプラグインがインストールされます。

 

※ 保存フォルダのPathのどこかに日本語文字を含んでいると [起動時エラー] が起きるので注意。

 

 

④ Fiji上部の「Plugins」タブから起動

 

起動すると処理選択画面が表示されるので、お好みの処理を。

 

 

 

雑記

 

初のJythonプログラミングでしたが、Python系の本を読んだり、Fiji Tutorialの記事やImageJ APIを読みながらで、試行錯誤しつつも楽しく作ることができました。

 

Macro版から翻訳するだけと思いきやアルゴリズム構造を変えなければならない部分もいくつかあったので、まともに動くようになるだけでもRPG一本やり込みクリア出来ちゃうぐらいの時間がかかった気がします。

 

syn.mrc-lmb.cam.ac.uk

imagej.net

 

 

最初はナニがナニやらだったのですが、APIの使い方がわかるようになると意外となんとかなる。

 

いままでのMacro版と同じ処理のほか、Pluginにしか出来ない+αも少しだけ実装しています。

 

思ったよりもいい感じに仕上がったので、「Almighty Processing」に関しては、このままこのFijiプラグインで自炊画像処理とアップデートもしていく予定です。

 

 

 

 

 

 

おしまい

 

 

 

 

 

 

ライセンスなんかは一切無いので、ぜひぜひ自由に使ってみてください!

  

imagej-jisui.hatenablog.com

 

 

 

 

【ImageJマクロ応用編】#5 FijiとJythonを使ったPlugin開発

 



 


皆さまこんにちは。

サイバーパンク2077 』から抜け出せません、yu3xx(ゆーさんちょめちょめ)です。

 

 

このブログではこれまで、ImageJマクロの様々な使い方について実例を提示しながら紹介してきました。

imagej-jisui.hatenablog.com

 


ImageJマクロはIJ1 Macroという独自の言語で書く必要がありますが、FijiというImageJの上位互換版を利用することで、人気のプログラミング言語であるPythonJython)でプラグインを書くことが可能となります。

 

私自身は今までFijiやJythonは使用していませんでしたが、ちょこちょこ勉強していくためのきっかけとしてJythonを用いて漫画自炊用Pluginを作成してみました。


今回はこのJython Pluginを紹介しつつ、同じくこれからPlugin開発を始めてみたい人向けのかんたんな解説や、マクロと比較した処理時間の違いを書いていきたいと思います。

 

 

 

 

2024.02.20追記.   Jythonを用いたPluginを本格的に作ってみました

 

 

 

 

 

 もくじ

 

 

Jythonコードはどこに書けばいいの?

 

① まずはFijiをダウンロード・インストールします。

imagej.net

 

② Fiji起動後、上部タブから

[File] → [New] → [Script...] スクリプトエディタが起動します。

 

          

 

③ 言語設定を変更したいので、上部タブから

[Language] → [Python (Jython)] を選択します。

          

 

④ これで準備はOKです。 このスクリプトエディタにコードを書いていきます。

 

 

作成したJythonコード

 


from ij import IJ
from ij.io import DirectoryChooser
from ij.process import ImageProcessor
import os
import time
	

openDir = DirectoryChooser("").getDirectory()
IJ.log("openDir : " + openDir)
saveDir = DirectoryChooser("").getDirectory()
IJ.log("saveDir : " + saveDir)


startTime = time.time()

for root, dirs, files in os.walk(openDir):
	files.sort()
	for name in files:
		if name.endswith(".jpg"):
			path = os.path.join(root,name)
			
			scale = 0.5
			min = 20
			max = 230
			
			IJ.log(path)
			imp = IJ.openImage(path)
			w = int(imp.getWidth() * scale)
			h = int(imp.getHeight() * scale)
			imp = imp.resize(w,h,"bicubic")
			ip = imp.getProcessor()
			ip.setMinAndMax(min,max)
			IJ.run(imp,"Apply LUT", "")
			
			savepath = saveDir + name
			IJ.saveAs(imp,"JPEG", savepath)	
			

finishTime = time.time()

processingTime = finishTime - startTime
print processingTime
IJ.log("Processing time: " + str(processingTime))

 

 

Jythonの基礎的な解説

 

① os.walkで再帰的にディレクトリを探索


マクロでは「ListFiles」というユーザー定義関数を使ってやっていたものですが、Jythonではosというモジュールをインポートすることで、初期装備として使うことができます。

 

② クラス、オブジェクト、インスタンス、メソッド


このあたりはImageJマクロでは使わなかった概念です。

諸説あるところも含みますので、もしかすると練達の先輩方から怒られてしまうかもしれませんが、ふわっとゆるや〜かに書いていきます。

 

◇ クラスについて

ImagePlusとかImagePricessorとか、なぞのワードが出て来ますが、これらは「クラス」と呼ばれる設計図のようなものです。

ImagePlusは画像ファイルそのものの設計図で、ImageProcessorは画像から取り出した画素データの設計図です。

 

◇ オブジェクト、インスタンスについて

この設計図から作り出した実体のあるもの「オブジェクト」とかインスタンスと呼びます。

なので変数impは、クラスImagePlusという設計図から作り出したオブジェクト(インスタンス)となります。

変数ipは、設計図ImageProcessorから作ったオブジェクト(インスタンス)です。

 

インスタンス化について

通常、Jythonのようなオブジェクト指向なプログラミングでは、「クラス(ほにゃらら)」の文法でインスタンスを召喚します。これをインスタンスと呼びます。

例えばMonsterというクラスがあるとして、「Monster("スライム")」と唱えると、スライムという名前の実体のあるモンスターがインスタンスとして召喚されます。この召喚手順をコンストラクと呼びます。

Jythonではこの正式なインスタンス化の召喚手順を踏まずにオブジェクトを生成することもあります。

上記コード内の「imp = IJ.openImage(path)」の部分は正式な手順を省略してimpというオブジェクトを生成している一例です。

「openImage(ほにゃらら)」という命令は「ImagePlusクラスのものを出力する」ということをあらかじめ定められている命令なので、結果としてimpというImagePlusクラスのオブジェクトが生成されています。

この「命令」は次に解説するメソッドのことになります。

 

◇ メソッドについて

それぞれのクラスには「メソッド」という処理や機能が用意されています。例えば「車」というクラスに、「前に進む」というメソッドが用意されているようなものです。

メソッドは基本的には、オブジェクトとして実体を持ってはじめて実行出来ます。メソッドを実行するときには「オブジェクト.メソッド」という文法で書くことになります。上記コード内の「ip.setMinAndMax」のような書き方です。

ところが、クラスのままメソッドを実行するという特別な場合もあります。これをstaticなメソッド」といい、「クラス.メソッド」の文法で書きます。

上記コードによく出てくる「IJ」はクラスなのですが「IJ.log(ほにゃらら)」はこの「staticなメソッド」を使っています。

 

API Documentationという虎の巻について

どのクラスにどんなメソッドがあるのか、そしてそのメソッドの出力はどんなタイプなのか、staticメソッドなのかどうか、についてはImageJの公式サイトに掲載されているAPI Documentation」を見るとわかります。

imagej.net

 

 

例えば「ijパッケージ」の中にある「ImagePlusクラス」の中を見てみます。

「Method Summary」という項目がメソッドについて記載された部分なのですが、1列目「Modifier and Type」が出力されるタイプ2列目「Method」がメソッド名3列目「Description」がメソッドの内容となっています。

書き方がややこしいのでわかりにくく感じるかもしれませんが、2列目「Method」を見ながらいろいろ探すことになると思います。

『書き方に迷ったら、この虎の巻を参照する』というのが、Jython Plugin開発の一番のキモになりそうです。

staticメソッドは1列目「Modifier and Type」の欄にstaticと書かれています。

 

この項をまとめると

 クラス : 設計図。
 インスタンス、オブジェクト : クラスをもとに実際に作った物。モノ。
 メソッド : オブジェクトやクラスが使うことの出来る呪文。

となります。

 

 

保存場所と起動方法

 

スクリプトエディタに入力して作成した「.pyファイル」は、「Fiji.appフォルダ内のPluginsフォルダ」の中に保存します。

Pluginsフォルダ直下に適当な名前のサブフォルダを作成して、その中に.pyファイルを入れることも出来ます。

 

ただしサブフォルダ名に日本語文字を含んでしまうとPlugin起動時にエラーとなるため、英数字のみで命名してください。


② ファイルを保存したら、Fijiを一度閉じてからアプリを再起動すればインストール完了です。

 

!!!注意!!!
このときファイル名に _(アンダーバー)を「最低でも1つ」入れて下さい!
アンダーバーを名前に含む.pyファイルのみが自動インストールされます!

 

③ 起動するときは、上部タブ[Plugins]から使いたいPluginを選択してください。

 



Macroとの処理時間の比較

 

//Macro Code

openDir = getDirectory("");
list = getFileList(openDir);
print(openDir);
saveDir = getDirectory("");
print(saveDir);

setBatchMode(true);

startTime = getTime();

listFiles(openDir);

finishTime = getTime();

processingTime = (finishTime - startTime) / 1000;
print("Processing Time : ",processingTime);



//-----------------------------------------------------------------------------
//Define listFiles

function listFiles(dir){
	list = getFileList(dir);
	for(i=0; i<list.length; i++){
		if(endsWith(list[i],"/")){
			listFiles(dir+list[i]);
		}else{
			open(dir+list[i]);
			operation();
		}
	}
	
}
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
//Define operation

function operation(){
	name = getTitle();
	print(dir + list[i]);
	w = getWidth();
	scale = 0.5;
	targetW = scale * w;
	run("Size...", "width=targetW constrain average interpolation=Bicubic");
	min = 20;
	max = 230;
	setMinAndMax(min, max);
	run("Apply LUT");
	saveAs("JPEG", saveDir+name);
	//bytes = parseInt(IJ.currentMemory());
	//bytes = bytes / 1000000;
	//print(bytes);
	close();
	
}
//-----------------------------------------------------------------------------

 

Jython Pluginと同様な処理をするマクロです。自炊画像処理で最もよく使う「Almighty_Processing.ijm」を基本とした処理内容にしています。

imagej-jisui.hatenablog.com

 

処理時間は手動のディレクトリ選択が済んでからの、処理完了までの時間を測定しています。

 

処理対象の資料は『地獄のアリス』第一巻(松本次郎集英社)の400dpiスキャンJPEG画像計217ファイルです。

 

 

結果は


Jython Plugin ... 63.3 sec
Macro ...  66.4 sec

 

Jython Pluginのほうがちょび〜っとだけ速い」という結果でした。

 

 

 

FijiやJythonをはじめて使ってみた感想

 

Fijiはスクリプトエディタが高機能で、

・お気に入り表示的なウィンドウ追加

・入力領域の色彩テーマを変更可能

対応するカッコのハイライト表示

検索と置換機能の改良

ユーザー定義関数を含む関数のサジェスト機能

などなど、良いことがたくさんあって結構気に入っています。

 

ImageJのStartUp Macrosもそのまま使用出来るので、ImageJのカスタマイズをそのまま引き継ぐことが出来ます。

ダークでポップなテーマカラーに変更



 

アイコンや右クリックポップアップのカスタマイズも引き継ぎ可能

 

 

今後は今までに作成したMacro「.ijmファイル」をFijiに移して、MacroとJython Pluginを共存させつつ、Fijiをベースに使っていこうと思っています。

 

 

JythonによるPlugin開発

・処理時間はMacroよりちょびっと速い
オブジェクト指向の理解が難しい
Javaよりも書きやすい
・困ったらAPI Documentatipnを眺める

 

  

 

 

 

おしまい 

 

 

 

 

 

 

・公開したマクロのまとめ

imagej-jisui.hatenablog.com

 

 

 

 

 

【続・改良版】スキャナ読み取り直後に数秒間だけ画像表示させるMacro (ver.3)

 





前回同様、「データ確認のためちょこっとだけ画像表示させるマクロ」の改良版(ver.3)です。

 

//FileWatcher_Modoki.ijm
//Detect if there are "New created files" or "Modified files" in selected dir.

version = "3.3.0";

print("");
print("FileWatcher_Modoki.ijm");
print("ver",version);
print("");



checkImageTime = 1800;

var width = 570; //seinen size comic
//var width = 550; //syounen size comic
var height = 1000;



//define var
var zeroTime;
var tempTime = 0;
var flagDetect = 0;
var sCount = 0;
var RECENT = newArray("","","","","");

//select dir
dir = getDirectory("Choose a Directory");
print("Watching :",dir);
print("");

//get zeroTime
zeroTime = getTime();

//Main ope
getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
var min = 0;

while(min < 30){
	//Show WaitingCount
	getDateAndTime(year,month,week,day,hour,min,sec,msec);

	msec = floor(msec/10 -msec0/10);
	if(msec < 0){
		msec = 100+msec;
		sec = sec-1;
	}
	sec = sec - sec0;
	if(sec < 0) {
		sec = 60 + sec;
		min = min -1;
	}
	min = min - min0;
	if(min < 0) {
		min = 60 + min;
		hour = hour -1;
	}
	hour = hour - hour0;
	if(hour <0){
		hour = 24 + hour;
	}

	bytes = parseInt(IJ.currentMemory());
	bytes = d2s(bytes/1000000,2);

	print("\\Update:"+pad2(hour)+":"+pad2(min)+":"+pad2(sec)+":"+pad2(msec),"<",bytes,"MB >");

	//Detect New File
	list = getFileList(dir);
	
	for(i=0;i<list.length;i++){
		lastModifiedTime = File.lastModified(dir + list[i]);
		lastModifiedTime = parseFloat(lastModifiedTime);
	
		if(lastModifiedTime > zeroTime){
			call("ij.gui.ImageWindow.setNextLocation",0,0);
			open(dir+list[i]);
			setLocation(0,0,width,height);
			close("\\Others");
			wait(checkImageTime);

			flagDetect = 1;
			if(lastModifiedTime > tempTime) tempTime = lastModifiedTime;

			sCount++;
			RECENT[sCount%5] = list[i];
			
		}else if(lastModifiedTime == zeroTime){
			if(fileExistInArray(list[i],RECENT) == 0){
				call("ij.gui.ImageWindow.setNextLocation",0,0);
				open(dir+list[i]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImageTime);

				flagDetect = 1;
				if(lastModifiedTime > tempTime) tempTime = lastModifiedTime;
				
				sCount++;
				RECENT[sCount%5] = list[i];
			}
		}
	}
	if(flagDetect == 1) {
		zeroTime = tempTime;
		getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
	}
	flagDetect = 0;
	wait(100);
}

showMessage("Time out");	


//-----------------------------------------------------------------------------

function pad2(n){
	n = toString(n);
	if(lengthOf(n) == 1) n="0"+n;
	return n;
}


//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function fileExistInArray(fileName,ARRAY){
	output = 0;
	for(i=0;i<ARRAY.length;i++){
		if(ARRAY[i] == fileName) {
			output = 1;
			break;
		}
	}
	return output;
}

//-----------------------------------------------------------------------------

 

 

コードのざっくり説明

 

マクロ開始時の時間を取得し、これをファイル最終更新時間と比較して、新規ファイルを検出する」というシンプルなアルゴリズムです。

 

ファイルの最終更新時間を調べるために、File.lastModified(path)という命令を使っています。

このFile.lastModified(path)を実行すると、1970年1月1日午前0時0分から数えた時間である「UNIXTIME(単位はmsec)」が出力されます。

 

UNIXTIMEについてはこちらの記事を。

 

「UNIXTIME⇄普通の東京時間」で変換するのは面倒なので、『マクロ開始時にgetTimeを使うことで「マクロ開始UNIXTIME」を取得。これをディレクトリ内ファイルの「最終更新UNIXTIME」と比較する』という方法にしました。

 

マクロ開始UNIXTIME(zeroTime)は、新規ファイル検出時の最終更新UNIXTIMEで随時上書きしていくことで、継続して新規ファイルを検出することができます。

 

 

ちょっと脱線して、ややこしいけど大事な解説もしておきます。

getTime()で出力されるUNIXTIMEは「浮動小数点型の数値」、File.lastModifiedで出力されるUNIXTIMEは「文字列」となっています。

ImageJマクロでは、if内で比較を行う際に、「先に書いたモノの型」のルールで比較を行うっぽいです。つまり、if(文字列>数値)と書けば「文字列のルールで比較(昇順ソートで上か下か)」if(数値>文字列)と書けば「数値のルールで比較(数値として大きいか小さいか)」となります。

コード内のif(lastModifiedTime > zeroTime)のところで、文字列のルールで比較されてしまうと処理が狂ってしまいます。しかも多くの場合で、文字列の比較は求めていないはずです。そこでImageJ側が変な誤解をしないように、parseFloatを利用して、lastModifiedTimeを「浮動小数点の数値」として扱うように明確に指示しています。

 

 

以前のverとの比較

 

せっかくなので前回のデータベース分割式(ver.2)と今回のModifiedTime式(ver.3)と、で処理時間にどれぐらい変化が出るのかを調べてみました。 

 

 

比較の環境は、

・監視ディレクトリのファイル数は0個、214個、2172個、8100個、10272個の 5条件

・新規ファイルなしで、監視ループ処理を1回だけ回す

・データベース分割式はデータベース作成の時間を含む(毎回ループで更新される部分)

 

 

結果

 

 

 

データベース分割式では、ファイル数の二乗に比例して処理時間、つまり、画像表示までの遅延時間が長くなるようです。

 

データベース分割式では、ファイル数が10,272個では画像表示までの遅延時間が最大1.998秒かかることがわかりました。

 

Modified Time式では、ファイル数が10,272個でも遅延時間が最大0.252秒で表示できることがわかりました。

 

以上より、Modified Time式だと、遅延時間をかな〜り抑えることが出来るので、「安心して使える」ということがわかりました。

 

 

 

 

おしまい

 

 

 

 

imagej-jisui.hatenablog.com

 

 

 

 

ライセンスなんかは一切無いので、ぜひぜひ自由に使ってみてください!

 

imagej-jisui.hatenablog.com

 

 

 

 

 

【改良版】スキャナ読み取り直後に数秒間だけ画像表示させるMacro (ver.2)

 




以前公開した「データ確認のためちょこっとだけ画像表示させるマクロ」をちょこちょこカスタマイズしたものです。

 

//FileWatcher_Modoki.ijm
//Detect if there are any "Newly created files" in selected dir.
//"Deleted files" and "Modified files" are not the subject.


version = "2.2.0";

print("");
print("FileWatcher_Modoki.ijm");
print("ver",version);
print("");


var checkImgTime = 1800;

var width = 570; //seinen size comic
//var width = 550; //syounen size comic
var height = 1000;


//select dir
dir = getDirectory("Choose a Directory");
print("Watching :",dir);
print("");

//Database setting
var TEMP;
var DATABASE0;
var DATABASE1;
var DATABASE2;
var DATABASE3;
var DATABASE4;
var DATABASE5;
var DATABASE6;
var DATABASE7;
var DATABASE8;
var DATABASE9;
var DATABASEx;

//Main ope
resetDatabase();
list0 = getFileList(dir);
createDatabase(list0);

var year0,month0,week0,day0,hour0,min0,sec0,msec0;
getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
var min=0;

while(min < 30){
	//Show WaitingCount
	getDateAndTime(year,month,week,day,hour,min,sec,msec);

	msec = floor(msec/10 -msec0/10);
	if(msec < 0){
		msec = 100+msec;
		sec = sec-1;
	}
	sec = sec - sec0;
	if(sec < 0) {
		sec = 60 + sec;
		min = min -1;
	}
	min = min - min0;
	if(min < 0) {
		min = 60 + min;
		hour = hour -1;
	}
	hour = hour - hour0;
	if(hour <0){
		hour = 24 + hour;
	}

	bytes = parseInt(IJ.currentMemory());
	bytes = d2s(bytes/1000000,2);

	print("\\Update:"+pad2(hour)+":"+pad2(min)+":"+pad2(sec)+":"+pad2(msec),"");
	
	//Create list1
	list1 = getFileList(dir);
	
	//Check Missmatch list1 and Database
	 newImageGlance(list1);

	//OverWrite database
	resetDatabase();
	createDatabase(list1);

	wait(100);
	
	
}

showMessage("Time out");	


//-----------------------------------------------------------------------------

function fileExistInArray(fileName,ARRAY){
	output = 0;
	for(i=0;i<ARRAY.length;i++){
		number = ARRAY.length-1-i;
		if(ARRAY[number] == fileName) {
			output = 1;
			break;
		}
	}
	return output;
}

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function pad2(n){
	n = toString(n);
	if(lengthOf(n) == 1) n="0"+n;
	return n;
}

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function resetDatabase(){
	TEMP = newArray(1);
	DATABASE0 = newArray(0);
	DATABASE1 = newArray(0);
	DATABASE2 = newArray(0);
	DATABASE3 = newArray(0);
	DATABASE4 = newArray(0);
	DATABASE5 = newArray(0);
	DATABASE6 = newArray(0);
	DATABASE7 = newArray(0);
	DATABASE8 = newArray(0);
	DATABASE9 = newArray(0);
	DATABASEx = newArray(0);

}

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function createDatabase(list){
	
	for(i=0;i<list.length;i++){
		fName = list[i];
		dot = lastIndexOf(fName,".");
		assH = substring(fName,dot-1,dot);
		TEMP[0] = fName;
	
		if(assH == "0"){
			DATABASE0 = Array.concat(DATABASE0,TEMP);
		}else if(assH == "1"){
			DATABASE1 = Array.concat(DATABASE1,TEMP);
		}else if(assH == "2"){
			DATABASE2 = Array.concat(DATABASE2,TEMP);
		}else if(assH == "3"){
			DATABASE3 = Array.concat(DATABASE3,TEMP);
		}else if(assH == "4"){
			DATABASE4 = Array.concat(DATABASE4,TEMP);
		}else if(assH == "5"){
			DATABASE5 = Array.concat(DATABASE5,TEMP);
		}else if(assH == "6"){
			DATABASE6 = Array.concat(DATABASE6,TEMP);
		}else if(assH == "7"){
			DATABASE7 = Array.concat(DATABASE7,TEMP);
		}else if(assH == "8"){
			DATABASE8 = Array.concat(DATABASE8,TEMP);
		}else if(assH == "9"){
			DATABASE9 = Array.concat(DATABASE9,TEMP);
		}else{
			DATABASEx = Array.concat(DATABASEx,TEMP);
		}
	}				
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function newImageGlance(list){
	
	for(i=0;i<list.length;i++){
		index = list.length-1-i;
		fName = list[index];
		dot = lastIndexOf(fName,".");
		assH = substring(fName,dot-1,dot);
		if(assH == "0"){
			if(fileExistInArray(list[index],DATABASE0) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "1"){
			if(fileExistInArray(list[index],DATABASE1) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "2"){
			if(fileExistInArray(list[index],DATABASE2) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "3"){
			if(fileExistInArray(list[index],DATABASE3) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "4"){
			if(fileExistInArray(list[index],DATABASE4) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "5"){
			if(fileExistInArray(list[index],DATABASE5) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "6"){
			if(fileExistInArray(list[index],DATABASE6) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "7"){
			if(fileExistInArray(list[index],DATABASE7) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "8"){
			if(fileExistInArray(list[index],DATABASE8) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "9"){
			if(fileExistInArray(list[index],DATABASE9) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else{
			if(fileExistInArray(list[index],DATABASEx) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}
	}
}
//-----------------------------------------------------------------------------


 

かなり魔改造っぽくなっていますが、ちゃんと動いてくれるのが嬉しいところです。

 

 

以前のバージョンはこちら。

 

 

変更点

 

① 画像表示時間(CheckImgTime)をちょびっと変更。お好みに調整してください。

② 監視ディレクトリ内のデータが多くなると検索時間が長くなる問題を、データベースを分割することで無理矢理高速化。

③ 新規ファイルを降順で番号の大きい方から検索・表示することで、表示までにかかる時間を無理矢理高速化。

④ 表示画像が大きく見やすくなるように調整した。PCのディスプレイサイズや本のサイズ等で変わるので、width, heightでお好みに調整してください。 

 

 

従来式との違い

 

年末でヒマだったので、データベースを分割したことで処理時間にどれぐらい変化が出るのかを検証してみました。 

 

検証の条件は、

・監視フォルダには2,270ファイル保存されている状態

・新規ファイルなしで、監視処理を100回ループ

・従来式とデータベース分割式のコードは以下の通り

 

従来式

checkImgTime = 1800;


//select dir
dir = getDirectory("Choose a Directory");
print("Watching :",dir);
print("");

initialTime = getTime();

//Main ope
list0 = getFileList(dir);
getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
var min=0;

for(d=0;d<100;d++){
	//Show Progress
	//Show WaitingCount
	getDateAndTime(year,month,week,day,hour,min,sec,msec);

	msec = floor(msec/10 -msec0/10);
	if(msec < 0){
		msec = 100+msec;
		sec = sec-1;
	}
	sec = sec - sec0;
	if(sec < 0) {
		sec = 60 + sec;
		min = min -1;
	}
	min = min - min0;
	if(min < 0) {
		min = 60 + min;
		hour = hour -1;
	}
	hour = hour - hour0;
	if(hour <0){
		hour = 24 + hour;
	}

	bytes = parseInt(IJ.currentMemory());
	bytes = d2s(bytes/1000000,2);

	print("\\Update:"+pad2(hour)+":"+pad2(min)+":"+pad2(sec)+":"+pad2(msec),"");
	wait(100);
	
	//Create list1
	list1 = getFileList(dir);
	
	//Check Missmatch list0 and list1
	for(i=0;i<list1.length;i++){
		index = list1.length-1-i;
		if(fileExistInArray(list1[index],list0) == 0){
			
			open(dir+list1[index]);
			call("ij.gui.ImageWindow.setNextLocation",0, 0); 
			setLocation(0,0,570,1000);
			close("\\Others");

			wait(checkImgTime);
			
			getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
		}
	}
	//OverWrite list0
	list0 = Array.copy(list1);
}


completionTime=getTime();

print("old type");
elapsedTime = getElapsedTime(initialTime,completionTime);
print ("Elapsed Time ..." , elapsedTime); 

//-----------------------------------------------------------------------------

function fileExistInArray(fileName,ARRAY){
	output = 0;
	for(i=0;i<ARRAY.length;i++){
		if(ARRAY[i] == fileName) {
			output = 1;
			break;
		}
	}
	return output;
}

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function pad2(n){
	n = toString(n);
	if(lengthOf(n) == 1) n="0"+n;
	return n;
}

//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------

function getElapsedTime(initialTime,completionTime){
	eTime = completionTime - initialTime;
	e_msec = eTime % 1000;
	eTime=eTime / 1000;
	e_sec = floor(eTime % 60);
	e_min = floor(eTime / 60) % 60;
	e_hour=floor(eTime / 3600);
	strElapsedTime = ""+e_hour + " : "+e_min + " : " + e_sec + "." + zeroPad(e_msec,3);

	return strElapsedTime;
}

//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
//Define function zeroPadding

function zeroPad(int,digitZeroPad){
	if(int < 0){
		exit("ZeroPadding Error!!  int<0");
	}
	stringInt = ""+int;
	digitStringInt = lengthOf(stringInt);
	digitSubtra = digitZeroPad-digitStringInt;
	if(digitSubtra < 0){
		exit("ZeroPadding Error!!  digitSubtra<0");
	}
	if(digitZeroPad > 0){
		for(i=0; i<digitSubtra; i++){
			stringInt = "0"+stringInt;
		}
	}
	return stringInt;
}	

//-----------------------------------------------------------------------------
	

 

 

データベース分割式

var checkImgTime = 1800;

var width = 570;
var height = 1000;


//select dir
dir = getDirectory("Choose a Directory");
print("Watching :",dir);
print("");

initialTime = getTime();

//Database setting
var TEMP;
var DATABASE0;
var DATABASE1;
var DATABASE2;
var DATABASE3;
var DATABASE4;
var DATABASE5;
var DATABASE6;
var DATABASE7;
var DATABASE8;
var DATABASE9;
var DATABASEx;

//Main ope
resetDatabase();
list0 = getFileList(dir);
createDatabase(list0);

var year0,month0,week0,day0,hour0,min0,sec0,msec0;
getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
var min=0;

for(d=0;d<100;d++){
	//Show Progress
	//Show WaitingCount
	getDateAndTime(year,month,week,day,hour,min,sec,msec);

	msec = floor(msec/10 -msec0/10);
	if(msec < 0){
		msec = 100+msec;
		sec = sec-1;
	}
	sec = sec - sec0;
	if(sec < 0) {
		sec = 60 + sec;
		min = min -1;
	}
	min = min - min0;
	if(min < 0) {
		min = 60 + min;
		hour = hour -1;
	}
	hour = hour - hour0;
	if(hour <0){
		hour = 24 + hour;
	}

	bytes = parseInt(IJ.currentMemory());
	bytes = d2s(bytes/1000000,2);

	print("\\Update:"+pad2(hour)+":"+pad2(min)+":"+pad2(sec)+":"+pad2(msec),"");
	wait(100);
	
	//Create list1
	list1 = getFileList(dir);
	
	//Check Missmatch list1 and Database
	 newImageGlance(list1);

	//OverWrite database
	resetDatabase();
	createDatabase(list1);
	
}


completionTime=getTime();

print("New type");
elapsedTime = getElapsedTime(initialTime,completionTime);
print ("Elapsed Time ..." , elapsedTime); 

//-----------------------------------------------------------------------------

function fileExistInArray(fileName,ARRAY){
	output = 0;
	for(i=0;i<ARRAY.length;i++){
		if(ARRAY[i] == fileName) {
			output = 1;
			break;
		}
	}
	return output;
}

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function pad2(n){
	n = toString(n);
	if(lengthOf(n) == 1) n="0"+n;
	return n;
}

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function resetDatabase(){
	TEMP = newArray(1);
	DATABASE0 = newArray(0);
	DATABASE1 = newArray(0);
	DATABASE2 = newArray(0);
	DATABASE3 = newArray(0);
	DATABASE4 = newArray(0);
	DATABASE5 = newArray(0);
	DATABASE6 = newArray(0);
	DATABASE7 = newArray(0);
	DATABASE8 = newArray(0);
	DATABASE9 = newArray(0);
	DATABASEx = newArray(0);

}

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function createDatabase(list){
	
	for(i=0;i<list.length;i++){
		fName = list[i];
		dot = lastIndexOf(fName,".");
		assH = substring(fName,dot-1,dot);
		TEMP[0] = fName;
	
		if(assH == "0"){
			DATABASE0 = Array.concat(DATABASE0,TEMP);
		}else if(assH == "1"){
			DATABASE1 = Array.concat(DATABASE1,TEMP);
		}else if(assH == "2"){
			DATABASE2 = Array.concat(DATABASE2,TEMP);
		}else if(assH == "3"){
			DATABASE3 = Array.concat(DATABASE3,TEMP);
		}else if(assH == "4"){
			DATABASE4 = Array.concat(DATABASE4,TEMP);
		}else if(assH == "5"){
			DATABASE5 = Array.concat(DATABASE5,TEMP);
		}else if(assH == "6"){
			DATABASE6 = Array.concat(DATABASE6,TEMP);
		}else if(assH == "7"){
			DATABASE7 = Array.concat(DATABASE7,TEMP);
		}else if(assH == "8"){
			DATABASE8 = Array.concat(DATABASE8,TEMP);
		}else if(assH == "9"){
			DATABASE9 = Array.concat(DATABASE9,TEMP);
		}else{
			DATABASEx = Array.concat(DATABASEx,TEMP);
		}
	}				
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function newImageGlance(list){
	
	for(i=0;i<list.length;i++){
		index = list1.length-1-i;
		fName = list[index];
		dot = lastIndexOf(fName,".");
		assH = substring(fName,dot-1,dot);
		if(assH == "0"){
			if(fileExistInArray(list[index],DATABASE0) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
			
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "1"){
			if(fileExistInArray(list[index],DATABASE1) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "2"){
			if(fileExistInArray(list[index],DATABASE2) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "3"){
			if(fileExistInArray(list[index],DATABASE3) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "4"){
			if(fileExistInArray(list[index],DATABASE4) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "5"){
			if(fileExistInArray(list[index],DATABASE5) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "6"){
			if(fileExistInArray(list[index],DATABASE6) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "7"){
			if(fileExistInArray(list[index],DATABASE7) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "8"){
			if(fileExistInArray(list[index],DATABASE8) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else if(assH == "9"){
			if(fileExistInArray(list[index],DATABASE9) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}else{
			if(fileExistInArray(list[index],DATABASEx) == 0){		
				call("ij.gui.ImageWindow.setNextLocation",0, 0); 
				open(dir+list[index]);
				setLocation(0,0,width,height);
				close("\\Others");
				wait(checkImgTime);
				
				getDateAndTime(year0,month0,week0,day0,hour0,min0,sec0,msec0);
			}
		}
	}
}
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------

function getElapsedTime(initialTime,completionTime){
	eTime = completionTime - initialTime;
	e_msec = eTime % 1000;
	eTime=eTime / 1000;
	e_sec = floor(eTime % 60);
	e_min = floor(eTime / 60) % 60;
	e_hour=floor(eTime / 3600);
	strElapsedTime = ""+e_hour + " : "+e_min + " : " + e_sec + "." + zeroPad(e_msec,3);

	return strElapsedTime;
}

//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
//Define function zeroPadding

function zeroPad(int,digitZeroPad){
	if(int < 0){
		exit("ZeroPadding Error!!  int<0");
	}
	stringInt = ""+int;
	digitStringInt = lengthOf(stringInt);
	digitSubtra = digitZeroPad-digitStringInt;
	if(digitSubtra < 0){
		exit("ZeroPadding Error!!  digitSubtra<0");
	}
	if(digitZeroPad > 0){
		for(i=0; i<digitSubtra; i++){
			stringInt = "0"+stringInt;
		}
	}
	return stringInt;
}	

//-----------------------------------------------------------------------------
	

 

結果は、

・従来式... 78.24秒

・データべース分割式...24.26秒

でした。

 

データベース分割式の方が、3倍速いことがわかりました。

 

 

 

 

おしまい

 

 

 

 

imagej-jisui.hatenablog.com

 

 

 

 

ライセンスなんかは一切無いので、ぜひぜひ自由に使ってみてください!

 

imagej-jisui.hatenablog.com

 

 

 

 

 

【ImageJマクロ応用編】#4 PluginとMacroの処理時間の違い

 

 




 

 

皆さまこんにちは。

最近NewJeansにハマってます、yu3xx(ゆーさんちょめちょめ)です。

 

 

 

ImageJ上で自動処理を行うための拡張機能には「Macro」のほかに「Plugin」というものがあります。

 

PluginはJava言語で記述することになるのですが、レクチャー記事がMacroに比べ極端に少ないため、勉強するのが面倒でとっつきづらいのが現状です。

 

とはいえ、PluginがMacroよりも「よっぽど」優れているのであれば、まあ...仕方ない、ちょっとは勉強してやるか、と思うところもなくはないのですが...。

 

ということで、本記事では漫画自炊用Pluginを実際に作って、その処理時間を計測し、Macroの場合と比較してみました。

 

 


もくじ

 

 

Plugin

 

画像コントラストを変更するためのPluginです。試行錯誤しながらなんとか作りました...

 

Pluginで画像を扱う際には、まずImagePlusというクラス(属性)で画像を取得し、さらに画像処理を行う場合にはImageProcessorというクラスに画素値を入れ込むようです。

 

ふぇぇ。なんのこっちゃ。

 


import ij.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import ij.plugin.*;
import ij.plugin.frame.*;
import java.io.File;


public class contrast_Adjust implements PlugIn {

	double min;
	double max;

	public void run(String arg) {
		//Select dir
		IJ.showMessage("Select Open Folder");
		String openDir = IJ.getDirectory("Choose a Directory");
		if (openDir == null) return;
		
		IJ.showMessage("Select Save Folder");
		String saveDir = IJ.getDirectory("Choose a Directory");
		if (saveDir == null) return;
		
		//Get min max
		min = IJ.getNumber("Min (42 de OK?):", 42);
		max = IJ.getNumber("Max (243 de OK?):", 243);

		//Open dir
		File dir = new File(openDir);
		File[] list = dir.listFiles();

		//Operation
		for (File file : list) {
			if (file.isFile()) {
				processImage(file.getAbsolutePath(), saveDir);
			}
		}

		
	}

	private void processImage(String filePath, String saveDir) {
		//Open image
		ImagePlus imp = IJ.openImage(filePath);

		//Get Px data
		ImageProcessor ip = imp.getProcessor();

		//Apply min max
		ip.setMinAndMax(min, max);
		IJ.run(imp, "Apply LUT", "");

		//Display image
		//imp.show();
	
		//Save image
		String fileName = new File(filePath).getName();
		String savePath = saveDir + fileName;
		IJ.saveAs(imp, "JPEG", savePath);
	}


}

 

Macro

 

マクロは、以前公開した「アレンジ用プログラム」をベースにして、上記プラグインと同じような処理が出来るようにしました。

 


//ListFilesRecursively_free

//JIBUNN DE IROIRO TSUKUTTE NE
//"??????" NO TOKORONI IROIRO KAITE NE


//Do something for selected folder
showMessage("Select Processing Folder");
openDir = getDirectory("Choose a Directory");
print("Processing :",openDir);
selectWindow("Log");
showMessage("Select Save Folder");
saveDir = getDirectory("Choose a Directory");
print("Save to :",saveDir);
selectWindow("Log");
list = getFileList(openDir);
wait(1000);


//Enter parameter
Dialog.create("Processing Setting");
items = newArray("JPEG", "PNG");
Dialog.addRadioButtonGroup("Output format", items, 1, 2, "JPEG");
Dialog.show;
output = Dialog.getRadioButton();

//Extension
if(output == "JPEG") ext = ".jpg";
if(output == "PNG") ext = ".png";

//JPEG,PNG quality setting(jpeg=90,gif=-1)
quality = 90;
run("Input/Output...", "jpeg=quality gif=-1 file=.csv copy_column copy_row save_column save_row");



//make parentDirectory
parentDir = saveDir+"postProc_"+getTimeStamp()+"/";
File.makeDirectory(parentDir);


//operation
startTime = whatTimeNow();
setBatchMode(true);
var totalFiles = 0;
var procCount = 0;

countFiles(openDir);
listFiles(openDir);


//fin
finishTime = whatTimeNow();
print("");

print("Start Time .... ",startTime);
print("FinishTime ... ",finishTime);
print("oshimai");
beep();



//-----------------------------------------------------------------------------
//Define countFiles

function countFiles(dir){
	list = getFileList(dir);
	for(i=0; i<list.length; i++){
		if(endsWith(list[i],"/")){
			countFiles(dir+list[i]);
		}else{
			totalFiles++;
		}
	}
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//Define listFiles

function listFiles(dir){
	
	list = getFileList(dir);
	for(i=0; i<list.length; i++){
		if(endsWith(list[i],"/")){
			listFiles(dir+list[i]);
		}else{
			open(dir+list[i]);
			operation();
		}
	}
	
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//Define operation

function operation(){
	
	procCount++;

	//Name setting
	name = getTitle();
	dotIndex = indexOf(name, ".");
	title = substring(name,0,dotIndex);
	underBar = lastIndexOf(title,"_");
	trueTitle = substring(title,0,underBar);
	number = substring(title,underBar+1);
	newname = title+ext;	
	rename(newname);

	

	

	//--------- KOKO NI IROIRO IRETE NE ----------

	//????????????????????????????????????????????



	setMinAndMax(42,243);

	run("Apply LUT");


	//????????????????????????????????????????????

	//Save to SubDirectory
	subDir = parentDir+trueTitle+"/";
	if(!File.exists(subDir)){
		File.makeDirectory(subDir);
	}
	
	saveAs(output, subDir+newname);

	close(newname);
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//Define function getTimeStamp

function getTimeStamp(){
	getDateAndTime(year,month,dayOfWeek,dayOfMonth,hour,minute,second,msec);
	timeStamp = "string";
	strYear = ""+year;
	month = month+1;
	if(month < 10){
		strMonth = "0"+month;
	}else{
		strMonth = ""+month;
	}
	if(dayOfMonth < 10){
		strDayOfMonth = "0"+dayOfMonth;
	}else{
		strDayOfMonth = ""+dayOfMonth;
	}
	if(hour < 10){
		strHour = "0"+hour;
	}else{
		strHour = ""+hour;
	}
	if(minute < 10){
		strMinute = "0"+minute;
	}else{
		strMinute = ""+minute;
	}
	if(second < 10){
		strSecond = "0"+second;
	}else{
		strSecond = ""+second;
	}
	timeStamp = strYear+strMonth+strDayOfMonth+"_"+strHour+"h"+strMinute+"m"+strSecond+"s";
	return timeStamp;
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//Define function whatTimeNow

function whatTimeNow(){
	getDateAndTime(year,month,dayOfWeek,dayOfMonth,hour,minute,second,msec);
	stringTime = "string";
	strYear = ""+year;
	month = month+1;
	if(month < 10){
		strMonth = "0"+month;
	}else{
		strMonth = ""+month;
	}
	if(dayOfMonth < 10){
		strDayOfMonth = "0"+dayOfMonth;
	}else{
		strDayOfMonth = ""+dayOfMonth;
	}
	if(hour < 10){
		strHour = "0"+hour;
	}else{
		strHour = ""+hour;
	}
	if(minute < 10){
		strMinute = "0"+minute;
	}else{
		strMinute = ""+minute;
	}
	if(second < 10){
		strSecond = "0"+second;
	}else{
		strSecond = ""+second;
	}
	stringTime = strYear+"/"+strMonth+"/"+strDayOfMonth+"_"+strHour+":"+strMinute+":"+strSecond;
	return stringTime;
}

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//Define function zeroPadding

function zeroPad(int,digitZeroPad){
	if(int < 0){
		exit("ZeroPadding Error!!  int<0");
	}
	stringInt = ""+int;
	digitStringInt = lengthOf(stringInt);
	digitSubtra = digitZeroPad-digitStringInt;
	if(digitSubtra < 0){
		exit("ZeroPadding Error!!  digitSubtra<0");
	}
	if(digitZeroPad > 0){
		for(i=0; i<digitSubtra; i++){
			stringInt = "0"+stringInt;
		}
	}
	return stringInt;
}	

//-----------------------------------------------------------------------------

 

比較方法

 

・Plugin、Macroともに、min 42、max 243でコントラスト調整しJPEG保存

・処理対象はOne Piece集英社)第107巻、全235ファイル

・処理開始から235ファイル目が保存されるまでの時間を3回計測し平均(ディレクトリ選択やmin maxの手動設定を除いた、実際の処理時間)

 

結果

 

・Plugin... 64.2 sec、62.8 sec、63.3 sec。平均 63.4 sec

・Macro... 60.2 sec、59.8 sec、61.4 sec。平均 60.4 sec

 

感想

 

なんと

Macroの方がちょびっとだけ速い

という結果でした。

 

もっと難しい処理であればPlugin側が有利になるかもしれませんが、簡単な自炊画像処理においてはMacroで十分ということでしょうか。

 

 

 

Pluginの処理コードを実際に書いてみてわかったこと

・ネット上に情報が少ないので拾いづらい

・ChatGPT大先生が頼りになるけどウソも多い

・Macroでは使えたRecord機能が無いのがつらい

・でも試行錯誤してやっと動いたときにはとても気持ちいい

 

 

 

 

まとめ

 

 ・漫画自炊処理においては、PluginとMacroの処理時間はほぼ同等

 ・Pluginに乗り換えるほどの価値は感じないが、なんだかんだでPlugin作りも楽しい

 

 

 

 


おしまい

 

 

  

 

・公開したマクロのまとめ

imagej-jisui.hatenablog.com

 

 

 

 

外付けストレージへのバックアップをワンボタンで簡単にやってくれるMacro

 




手動だとファイル選びが面倒なバックアップ処理を、起動するだけで全てオートでやってくれるマクロです。

 

//BackUp_Proc.ijm

version = "2.9.0";

print("");
print("backUp Processing");
print("ver",version);

startTime = whatTimeNow();

//directory setting
srcDir = "/Volumes/StorageA/Data/";
dstDir = "/Volumes/StorageB/BackUp/";
oldDataDir = "/Volumes/StorageB/oldData/";
nazoDataDir = "/Volumes/StorageB/nazoData/";


//search directory
print("");
print("Source : ",srcDir);
print("Destination : ",dstDir);
print("");

flagDirExists = 1;
if(File.exists(srcDir)){
	print("srcDir...exists");
}else{
	print("srcDir...could not be found");
	flagDirExists = 0;
}
if(File.exists(dstDir)){
	print("dstDir...exists");
}else{
	print("dstDir...could not be found");
	flagDirExists = 0;
}
if(File.exists(oldDataDir)){
	print("oldDataDir...exists");
}else{
	print("oldDataDir...could not be found");
	flagDirExists = 0;
}
if(File.exists(nazoDataDir)){
	print("nazoDataDir...exists");
}else{
	print("nazoDataDir...could not be found");
	flagDirExists = 0;
}

if(flagDirExists == 0) exit("Error!! Directory ga Naiyo!!");


//variable setting
var totalFiles,flagEmpty,flagMissing;
var newCount = 0;
var updateCount = 0;
var deleteCount = 0;
var nazoCount = 0;


print("");

//File Counts of preBackUp in Source dir
print("Counting totalFiles of srcDir...");
totalFiles=0;
flagMissing=0;
countFiles(srcDir);
countSrc = totalFiles;
print(totalFiles,"Files");
print("");

//main operation
//check S dir
print("*** Loop in Source directory - (Checking Updated File)***");
listFilesSrc(srcDir,dstDir);

//Check D Dir
print("");
print("*** Loop in Destination directory part1- (Checking Deleted File)***");
listFilesDst(srcDir,dstDir);
//seek and delete disappear Directory in D Dir
print("");
print("*** Loop in Destination directory part2 - (Checking Deleted Directory)***");
seekDeletedDir(srcDir,dstDir);
	
//File Counts of postBackUp in destination dir
print("");
print("Counting totalFiles of dstDir...");
totalFiles = 0;
countFiles(dstDir);
countDst = totalFiles;
print(totalFiles,"Files");
print("");

//Check Empty Directory (File.delete...parentDir ga kesenai koto aru)
flagEmpty = 0;
print("");
print("------Suspicious of File.delete Error (Empty dir)------");
emptyDir(srcDir,dstDir);
if(flagEmpty == 0) print("  None");
print("-------------------------------------------------------");

//check file counts
print("");
print("FileCount in Source = ",countSrc);
print("FileCount in Destination = ",countDst);

if(countSrc == countDst) {
	print("...verified");
	print("");
	print("backUp consistency ...");
	validateSrc(srcDir,dstDir);
	validateDst(srcDir,dstDir);
	if(flagMissing == 1){
		print("!!!!!!!!!  Check above file location  !!!!!!!!!");
		exit("Error!!!!  The File consistency is Mismatch! Check Missing Files!");
	}else if(flagMissing == 0){
		print("... varidated");
		wait(1000);
	}
}
if(countSrc != countDst) {
	print("");
	print("!!!!!!!!!  Check this file location  !!!!!!!!!");
	validateSrc(srcDir,dstDir);
	validateDst(srcDir,dstDir);
	exit("Error!!!!  The FileCount is Mismatch! Check Missing Files!");
}

//fin
finishTime = whatTimeNow();
print("");
print("New File = ",newCount);
print("Update File = ",updateCount);
print("Delete File = ",deleteCount);
print("Nazo File = ",nazoCount);
print("");

print("Start Time .... ",startTime);
print("FinishTime ... ",finishTime);
print("oshimai");
beep();
showMessage("Complete successfully");


//-----------------------------------------------------------------------------
//Define listFilesSrc (loop in srcDir)

function listFilesSrc(dirS,dirD){
	listS = getFileList(dirS);
	for(i=0; i<listS.length; i++){
		if(endsWith(listS[i],"/")){
			//Make New Directory in dstDir
			if(!File.exists(dirD+listS[i])){
				File.makeDirectory(dirD+listS[i]);
				print("mkDir : ",dirD+listS[i]);
			}
			//Exists in dstDir		
			listFilesSrc(dirS+listS[i],dirD+listS[i]);
		}else{
			//If the object is File 
			checkFilesSrc(dirS+listS[i],dirD+listS[i]);
		}
	}
	
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function checkFilesSrc(pathS,pathD){

	//exists both S and D
	if(File.exists(pathD)){
		//update Time (source)
		timeS = File.lastModified(pathS);
		timeS = parseFloat(timeS);
			
		//update Time (destination)
		timeD = File.lastModified(pathD);
		timeD = parseFloat(timeD);
			
		//detect updated File
		if(timeS > timeD){
			
			//copy D file to old
			File.copy(pathD,oldDataDir+getTimeStampAlpha()+"_"+listS[i]);
			print(i,"-","Move Old Data to : ",oldDataDir+getTimeStampAlpha()+"_"+listS[i]);
			//copy S file to D
			File.copy(pathS,pathD);
			print(i,"-","Copy Updated Data to : ",pathD);
			updateCount++;
		}

		//detect updated File in dstDir (nazo Data)
		if(timeS < timeD){
			//copy D file to nazo
			File.copy(pathD,nazoDataDir+getTimeStampAlpha()+"_"+listS[i]);
			File.copy(pathS,pathD);
			print(i,"-","Move Nazo Data to : ",nazoDataDir+getTimeStampAlpha()+"_"+listS[i]);
			nazoCount++;
				
		}

	//exists only S
	}else{	//if File don't exists in destination dir
		//copy S file to D
		File.copy(pathS,pathD);
		print(i,"-","Copy New Data to : ",pathD);
		newCount++;
	}
}

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//Define listFilesDst (loop in dstDir)

function listFilesDst(dirS,dirD){
	listD = getFileList(dirD);
	for(i=0; i<listD.length; i++){
		if(endsWith(listD[i],"/")){	
			listFilesDst(dirS+listD[i],dirD+listD[i]);
		}else{
			checkFilesDst(dirS+listD[i],dirD+listD[i]);
		}
	}
	
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function checkFilesDst(pathS,pathD){
	//exists only D
	if(!File.exists(pathS)){
		//Detect deleted file
		print("");
		print("File.delete(fail...0, success...1)");
		//copy D file to old and delete
		File.copy(pathD,oldDataDir+getTimeStampAlpha()+"_"+listD[i]);
		File.delete(pathD);
		print(i,"-","Move deteted Data to : ",oldDataDir+getTimeStampAlpha()+"_"+listD[i]);
		deleteCount++;
	}
}

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function seekDeletedDir(pathS,pathD){
	listD = getFileList(pathD);
	for(i=0;i<listD.length;i++){
		if(endsWith(pathD+listD[i],"/")){
			if(File.exists(pathS+listD[i])){
				//the object is directory && Exists in srcDir...move a level down
				seekDeletedDir(pathS+listD[i],pathD+listD[i]);
			}else{
				//the object is directory && Not exists in srcDir...detect disappeared directory 
				deleteDir(pathD+listD[i]);
			}
		}
	}
	
}

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function deleteDir(dir){
	//Up to this line, files in dir must be deleted
	list = getFileList(dir);
	for(i=0;i<list.length;i++){
		if(endsWith(list[i],"/")){
			deleteDir(dir+list[i]);
		}
	}
	//when reaching the endpoint of one recursive search,
	//it is confirmed that there are no more dir,
	
	print(dir);
	File.delete(dir);
	
	//After deletion, performe recursive search at the same level,
	//or go back to the parentDir one level up.
	
}

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
function validateSrc(dirS,dirD){
	listS = getFileList(dirS);

	for(i=0; i<listS.length; i++){
		if(endsWith(listS[i],"/")){	
			validateSrc(dirS+listS[i],dirD+listS[i]);
		}else{	
			if(File.exists(dirD+listS[i])){
				timeS = File.lastModified(dirS+listS[i]);
				timeD = File.lastModified(dirD+listS[i]);
				if(timeS != timeD){
					print(dirS+listS[i]);
					flagMissing = 1;
				}
			}else if(!File.exists(dirD+listS[i])){
				print(dirS+listS[i]);
				flagMissing = 1;
			}
		}
	}
	
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function validateDst(dirS,dirD){
	listD = getFileList(dirD);
	for(i=0; i<listD.length; i++){
		if(endsWith(listD[i],"/")){	
			validateDst(dirS+listD[i],dirD+listD[i]);
		}else{
			if(File.exists(dirS+listD[i])){
				timeS = File.lastModified(dirS+listD[i]);
				timeD = File.lastModified(dirD+listD[i]);
				if(timeS != timeD){
					print(dirD+listD[i]);
					flagMissing = 1;
				}			
	
			}else if(!File.exists(dirS+listD[i])){
				print(dirD+listD[i]);
				flagMissing = 1;
			}
		}
	}
	
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//Define function getTimeStampAlpha

function getTimeStampAlpha(){
	getDateAndTime(year,month,dayOfWeek,dayOfMonth,hour,minute,second,msec);
	timeStamp = "string";
	strYear = ""+year;
	month = month+1;
	if(month < 10){
		strMonth = "0"+month;
	}else{
		strMonth = ""+month;
	}
	if(dayOfMonth < 10){
		strDayOfMonth = "0"+dayOfMonth;
	}else{
		strDayOfMonth = ""+dayOfMonth;
	}
	if(hour < 10){
		strHour = "0"+hour;
	}else{
		strHour = ""+hour;
	}
	if(minute < 10){
		strMinute = "0"+minute;
	}else{
		strMinute = ""+minute;
	}
	if(second < 10){
		strSecond = "0"+second;
	}else{
		strSecond = ""+second;
	}
	strMsec = "" + msec;
	strMsec = zeroPad(strMsec,3);
	timeStamp = strYear+strMonth+strDayOfMonth+"_"+strHour+"h"+strMinute+"m"+strSecond+"s"+strMsec;
	return timeStamp;
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//Define countFiles

function countFiles(dir){
	list = getFileList(dir);
	for(i=0; i<list.length; i++){
		if(endsWith(list[i],"/")){
			countFiles(dir+list[i]);
		}else{
			totalFiles++;
		}
	}
	
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
function emptyDir(dirS,dirD){
	listD = getFileList(dirD);
	for(i=0; i<listD.length; i++){
		if(endsWith(listD[i],"/")){
			totalFiles = 0;
			countFiles(dirD+listD[i]);
			if(totalFiles == 0){
				if(!File.exists(dirS+listD[i])){
					print(dirD+listD[i]);
					flagEmpty = 1;
					continue;
				}
			}
			emptyDir(dirS+listD[i],dirD+listD[i]);
		}else{
			
		}
	}
	
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//Define function zeroPadding

function zeroPad(int,digitZeroPad){
	if(int < 0){
		exit("ZeroPadding Error!!  int<0");
	}
	stringInt = ""+int;
	digitStringInt = lengthOf(stringInt);
	digitSubtra = digitZeroPad-digitStringInt;
	if(digitSubtra < 0){
		exit("ZeroPadding Error!!  digitSubtra<0");
	}
	if(digitZeroPad > 0){
		for(i=0; i<digitSubtra; i++){
			stringInt = "0"+stringInt;
		}
	}
	return stringInt;
}	

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//Define function whatTimeNow

function whatTimeNow(){
	getDateAndTime(year,month,dayOfWeek,dayOfMonth,hour,minute,second,msec);
	stringTime = "string";
	strYear = ""+year;
	month = month+1;
	if(month < 10){
		strMonth = "0"+month;
	}else{
		strMonth = ""+month;
	}
	if(dayOfMonth < 10){
		strDayOfMonth = "0"+dayOfMonth;
	}else{
		strDayOfMonth = ""+dayOfMonth;
	}
	if(hour < 10){
		strHour = "0"+hour;
	}else{
		strHour = ""+hour;
	}
	if(minute < 10){
		strMinute = "0"+minute;
	}else{
		strMinute = ""+minute;
	}
	if(second < 10){
		strSecond = "0"+second;
	}else{
		strSecond = ""+second;
	}
	stringTime = strYear+"/"+strMonth+"/"+strDayOfMonth+"_"+strHour+":"+strMinute+":"+strSecond;
	return stringTime;
}

//-----------------------------------------------------------------------------

 

 

「ストレージA」に保管してあるデータを「ストレージB」にバックアップとして複製する、つまりミラーリングのためのマクロです。

 

Macでバックアップといえば「TimeMachine」ですが、1台のMacには1台のバックアップストレージという制限があったり、ストレージからストレージへのバックアップが出来なかったりします。

 

そこで、既製ソフトを使わずに、自由な設定で気楽にバックアップを行えるようにマクロを作成しました。

 

 

動作内容

 

① ストレージAの「Data」ディレクトリの中身を、ストレージBの「BackUp」ディレクトリに、下位のディレクトリも含めてまるまる複製。

② 2回目以降のバックアップでは、ストレージAとBの内容物のファイル名と最終更新日を比較。変化なしのファイル、新しく追加されたファイル、更新されたファイル、消去されたファイルを判別し、正しくミラーリングする。詳細は手順③〜⑤。

③ ストレージA内のファイルが追加or更新されていたら、そのファイルをストレージB内に保存、上書き。その際、ストレージBの更新前ファイルは念のためストレージBの「oldData」ディレクトリに待避。

④ ストレージB内のバックアップファイルのみが更新されていたら、これはあまりいいことではなさそうなので、その更新されたファイルをストレージBの「nazoData」ディレクトリにひとまず待避。ストレージAの更新前ファイルを、ストレージBに上書き。

⑤ ストレージA内のファイルが消去もしくは場所移動されて見当たらない場合、ストレージB内のファイルを消去。消去したデータは念のためストレージBの「oldData」ディレクトリに待避。

ディレクトリも同様で、ストレージA内のディレクトリが消去もしくは場所移動されていたら、ストレージB内のディレクトリを消去。念のための退避はしない。

⑥ バックアップ作業が正しく行われたかどうかを確認するために、ストレージA、B内のファイル数をカウント。さらにファイル名と最終更新日がA、B間で一致しなかった場合は「エラー」として通知する。

 

細かな説明

 

・コピー元のディレクトリ「Data」、コピー先のディレクトリ「BackUp」、古いデータや削除されたデータを保管するディレクトリ「oldData」、なぜかバックアップのみ更新されていたデータを待避させるディレクトリ「nazoData」の四つのディレクトリが必要。あらかじめディレクトリを手動で作成しておき、パスをマクロコード内で指定しておく。oldDataとnazoDataは、ストレージBのBackUpディレクトリの外側に作る。

 

・パスを指定する際、日本語は使わないようにする。つまりフォルダ名は英数字のみで設定する。日本語をマクロ内に記入した場合、上部タブ[Plugins]から起動すると文字化けによりうまく動作しない。(ただしEditでマクロ入力ウィンドウを開いておいてRun Macroすると普通に動作する)

 

・ImageJマクロにおけるファイルやディレクトリの消去はFile.deleteだが、ディレクトリ消去の場合はその下位にファイルやディレクトリが存在しないことが条件。そのため、まずファイル消去 → 一番下の階層のディレクトリから順に消去、という順番で処理する。

 

・ファイルやディレクトリの消去が成功したかどうかはLogウィンドウでわかる。成功なら1、失敗なら0が表示される。使用しているコンピュータによっては、子フォルダを消したあとの親フォルダが消去出来なくなるという謎の現象が起こることもある。当方の環境ではWindows OK、新Mac OK、旧Mac NG、であった。詳細は不明。

 

ディレクトリ消去不可エラーに備え、中身が空っぽのディレクトリ(ストレージB内)の存在を調査し、Logウィンドウで通知する。

 

 

 

 

 

以上です!

 

 

私の場合、自炊データのメイン保管用ストレージαは「TimeMachine」でストレージβに自動バックアップしています。

なんですが、最近ストレージαの容量が満杯になってしまったせいで、第2自炊保管庫をストレージβ内のバックアップ部分とはセパレートされた領域に作成し、第2保管庫のバックアップをそれとはまた違う第3のストレージγにバックアップする、という状況が生まれてしまいました。

この複雑怪奇なバックアップ環境に対応するために、今回紹介したバックアップマクロを使用しています。

 

 

自炊データは大切な財産なので、気になった方はぜひこのバックアップマクロを試してみてください!

 

 

 

 

おしまい

 

 

 

 

 

imagej-jisui.hatenablog.com

 

 

 

imagej-jisui.hatenablog.com