Commit fb75351e authored by Andrey Filippov's avatar Andrey Filippov

Working on loci-compatible reader for camera files to use single-read

pass
parent 1338de2c
......@@ -7,4 +7,5 @@ NC393I
attic
*.log
FOCUS-PSF*
src/main/resources/trained_model
\ No newline at end of file
src/main/resources/trained_model
bioformats
\ No newline at end of file
......@@ -88,7 +88,7 @@
<dependency>
<groupId>ome</groupId>
<artifactId>loci_tools</artifactId>
<version>5.0.0</version>
<version>5.9.0</version>
</dependency>
</dependencies>
......
This diff is collapsed.
......@@ -39,9 +39,12 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
......@@ -66,10 +69,15 @@ import ij.gui.GUI;
import ij.gui.GenericDialog;
import ij.io.FileInfo;
import ij.io.OpenDialog;
import ij.io.Opener;
import ij.plugin.frame.PlugInFrame;
import ij.process.ImageConverter;
import ij.process.ImageProcessor;
import ij.text.TextWindow;
import loci.common.RandomAccessInputStream;
import loci.formats.tiff.IFD;
import loci.formats.tiff.IFDList;
import loci.formats.tiff.TiffParser;
......@@ -1035,12 +1043,18 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener {
/* Modified from Opener.java */
ImagePlus openJpegOrGif(String dir, String name) {
ImagePlus imp = null;
boolean isTiff = false;
Image img = Toolkit.getDefaultToolkit().createImage(dir+name);
if (img!=null) {
try {
imp = new ImagePlus(name, img);
} catch (IllegalStateException e) {
return null; // error loading image
//java.lang.IllegalStateException: Error loading image
// Try TIFF
Opener opener=new Opener(); // Reads Tiff images
imp=opener.openImage("", dir+name);
if (imp == null) return null; // error loading image
isTiff = true;
}
if (imp.getType()==ImagePlus.COLOR_RGB) {
......@@ -1049,15 +1063,100 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener {
IJ.showStatus("Converting to 32-bits");
new ImageConverter(imp).convertToGray32();
FileInfo fi = new FileInfo();
fi.fileFormat = FileInfo.GIF_OR_JPG;
FileInfo fi = imp.getFileInfo();
fi.fileName = name;
fi.directory = dir;
fi.fileFormat = isTiff?FileInfo.TIFF: FileInfo.GIF_OR_JPG; // even if set originally, it is lost after convertToGray32
imp.setFileInfo(fi);
FileInfo ofi = imp.getOriginalFileInfo();
fi = imp.getFileInfo();
// testing
if ((ofi!=null) && (ofi.directory!=null) && (ofi.fileFormat ==FileInfo.TIFF)) {
String path = ofi.directory + ofi.fileName;
EyesisTiff ET = new EyesisTiff();
ImagePlus imptiff = ET.readTiff(path);
if (imptiff!=null) {
imptiff.show();
}
// IJ.error("TIFF Dumper", "File path not available or not TIFF file");
IJ.log("\\Clear");
IJ.log("PATH = "+path);
try {
dumpIFDs(path);
} catch(IOException e) {
IJ.error("Tiff Dumper", ""+e);
}
Frame log = WindowManager.getFrame("Log");
if (log!=null) log.toFront();
}
}
return imp;
}
public static void dumpIFDs(String path) throws IOException {
IJ.showStatus("Parsing IFDs");
RandomAccessInputStream in = new RandomAccessInputStream(path);
//TiffParser parser = new TiffParser(in);
TiffParser parser = new TiffParser(in);
IFDList ifdList = parser.getIFDs();
IJ.showStatus("");
for (IFD ifd : ifdList) {
for (Integer key : ifd.keySet()) {
int k = key.intValue();
String name = IFD.getIFDTagName(k)+String.format("(%d [0x%x])", k,k);
String value = prettyValue(ifd.getIFDValue(k), 0);
IJ.log(name + " = " + value);
}
}
in.close();
}
private static String prettyValue(Object value, int indent) {
if (!value.getClass().isArray()) return value.toString()+" ("+value.getClass().toString()+")";
char[] spaceChars = new char[indent];
Arrays.fill(spaceChars, ' ');
String spaces = new String(spaceChars);
StringBuilder sb = new StringBuilder();
sb.append("{\n");
for (int i=0; i<Array.getLength(value); i++) {
sb.append(spaces);
sb.append(" ");
Object component = Array.get(value, i);
sb.append(prettyValue(component, indent + 2));
sb.append("\n");
}
sb.append(spaces);
sb.append("}");
byte [] bstring=new byte [Array.getLength(value)];
for (int i=0;i<bstring.length;i++) {
try {
bstring[i]= (byte) Integer.parseInt(Array.get(value, i).toString());
} catch (NumberFormatException e) {
bstring[i] = 0;
}
}
// String astring=new String((byte []) value);
String astring="";
try {
astring = new String(bstring,"UTF-16");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sb.append("\n\""+astring+"\"");
return sb.toString();
}
@Override
public void setTitle (String title) {
imageTitle=title;
......
/**
**
** LwirCamera.java - Control/image acquisition for LWIR cameras
** (initially for Lepton 3.5 sensors, 103992 sesnor boards)
**
** Copyright (C) 2019 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** LwirCamera.java is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
*/
public class LwirCamera {
}
This diff is collapsed.
/**
** -----------------------------------------------------------------------------**
** ElphelTiffReader.java
**
** loci.format compatible reader for Elphel 8/16 bpp monochrome Tiff files with
** MakerNote
**
**
** Copyright (C) 2019 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** ElphelTiffReader.java is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
*/
package com.elphel.imagej.readers;
import java.io.IOException;
import java.util.Hashtable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import loci.formats.FormatException;
import loci.formats.in.MetadataLevel;
import loci.formats.in.TiffReader;
import loci.formats.meta.MetadataStore;
/*
// non-IFD tags (for internal use)
public static final int LITTLE_ENDIAN = 0;
public static final int BIG_TIFF = 1;
public static final int REUSE = 3;
<MakerNote tag="0x927c" format="LONG" count="16" seq="26" dlen="64"/> 37500
package loci.formats.tiff;
IFD.java public static final int MAKER_NOTE = 37500;
* IFD data
{ 0=false, LITTLE_ENDIAN
1=false, BIG_TIFF
0x0100 256=160, ImageWidth
0x0101 257=120, ImageLength
0x0102 258= 16, BitsPerSample
0x0106 262= 1, PhotometricInterpretation
0x0129 297= [I@3d136141, PageNumber
0x8769 34665=242, ExifTag (A pointer to the Exif IFD.)
0x010e 270=[Ljava.lang.String;@640a5e9d, ImageDescription
0x010f 271=Elphel, Make
0xc62f 50735=00:0E:64:10:8F:15, CameraSerialNumber
0x0110 272=LEPTON35_15, Model
0x0111 273=557, StripOffsets
0x0131 305=https://git.elphel.com/Elphel/elphel393, Software
0x9211 37393=122, ImageNumber
0x0112 274=1, Orientation
0x0132 306=2019:05:06 14:41:51, DateTime
0x0115 277=1, SamplesPerPixel
0x0116 278=120, RowsPerStrip
0x0117 279=38400, StripByteCounts
0x00fe 254=0 NewSubfileType }
*/
public class ElphelTiffReader extends TiffReader{ // BaseTiffReader {
// -- Constants --
public static final String ELPHEL_PROPERTY_PREFIX = "ELPHEL_";
/** Merge SubIFDs into the main IFD list. */
// protected transient boolean mergeSubIFDs = true; // false;
/** Logger for this class. */
private static final Logger LOGGER =
LoggerFactory.getLogger(ElphelTiffReader.class);
// public static final String[] ELPHEL_TIFF_SUFFIXES =
// {"tif", "tiff"}; // , "tf2", "tf8", "btf"};
// public static final String[] COMPANION_SUFFIXES = {"xml", "txt"};
// public static final int IMAGEJ_TAG = 50839;
// -- Fields --
// private String companionFile;
// private String description;
// private String calibrationUnit;
// private Double physicalSizeZ;
// private Double timeIncrement;
// private Integer xOrigin, yOrigin;
// -- Constructor --
/** Constructs a new Tiff reader. */
public ElphelTiffReader() {
super(); // "Tagged Image File Format", ELPHEL_TIFF_SUFFIXES); // See if we can use TiffReader without its parent
mergeSubIFDs = true; // false;
LOGGER.info("ElphelTiffReader(), after supper(), mergeSubIFDs = true;");
}
// -- IFormatReader API methods --
@Override
protected void initFile(java.lang.String id)
throws FormatException,
java.io.IOException
{
LOGGER.info("Starting initFile() method");
super.initFile(id);
LOGGER.info("Ending initFile() method");
}
/* @see loci.formats.IFormatReader#getSeriesUsedFiles(boolean) */
@Override
public String[] getSeriesUsedFiles(boolean noPixels) {
return super.getSeriesUsedFiles(noPixels);
// if (noPixels) {
// return companionFile == null ? null : new String[] {companionFile};
// }
// if (companionFile != null) return new String[] {companionFile, currentId};
// return new String[] {currentId};
}
/* @see loci.formats.IFormatReader#close(boolean) */
@Override
public void close(boolean fileOnly) throws IOException {
LOGGER.info("close("+fileOnly+") before super");
super.close(fileOnly);
LOGGER.info("close("+fileOnly+") after super");
if (!fileOnly) {
// companionFile = null;
// description = null;
// calibrationUnit = null;
// physicalSizeZ = null;
// timeIncrement = null;
// xOrigin = null;
// yOrigin = null;
}
}
// -- Internal BaseTiffReader API methods --
/* @see BaseTiffReader#initStandardMetadata() */
@Override
protected void initStandardMetadata() throws FormatException, IOException {
LOGGER.info("initStandardMetadata() - before super()");
super.initStandardMetadata();
String comment = ifds.get(0).getComment(); // IMAGE_DESCRIPTION
LOGGER.info("initStandardMetadata() - after super()");
ElphelMeta elphelMeta = new ElphelMeta(tiffParser, true);
Hashtable<String, String> property_table = elphelMeta.getPropertyTable();
LOGGER.info("Created elphelMeta table, size="+property_table.size());
for (String key:property_table.keySet()) {
addGlobalMeta(ELPHEL_PROPERTY_PREFIX+key,property_table.get(key));
}
MetadataLevel level = getMetadataOptions().getMetadataLevel();
if (level != MetadataLevel.MINIMUM) {
Integer[] tags = ifds.get(0).keySet().toArray(new Integer[0]);
LOGGER.info("initStandardMetadata() - got "+tags.length+" tags");
}
// check for ImageJ-style TIFF comment
boolean ij = checkCommentImageJ(comment);
// if (ij) parseCommentImageJ(comment);
/*
// check for MetaMorph-style TIFF comment
boolean metamorph = checkCommentMetamorph(comment);
if (metamorph && level != MetadataLevel.MINIMUM) {
parseCommentMetamorph(comment);
}
put("MetaMorph", metamorph ? "yes" : "no");
// check for other INI-style comment
if (!ij && !metamorph && level != MetadataLevel.MINIMUM) {
parseCommentGeneric(comment);
}
*/
// check for another file with the same name
/*
if (isGroupFiles()) {
Location currentFile = new Location(currentId).getAbsoluteFile();
String currentName = currentFile.getName();
Location directory = currentFile.getParentFile();
String[] files = directory.list(true);
if (files != null) {
for (String file : files) {
String name = file;
if (name.indexOf(".") != -1) {
name = name.substring(0, name.indexOf("."));
}
if (currentName.startsWith(name) &&
checkSuffix(name, COMPANION_SUFFIXES))
{
companionFile = new Location(directory, file).getAbsolutePath();
break;
}
}
}
}
*/
}
/* @see BaseTiffReader#initMetadataStore() */
@Override
protected void initMetadataStore() throws FormatException {
super.initMetadataStore();
MetadataStore store = makeFilterMetadata();
// if (description != null) {
// store.setImageDescription(description, 0);
// }
populateMetadataStoreImageJ(store);
}
// -- Helper methods --
// Convert to Elphel-specific parameters
/**
* Checks the original metadata table for ImageJ-specific information
* to propagate into the metadata store.
*/
private void populateMetadataStoreImageJ(MetadataStore store) {
// TODO: Perhaps we should only populate the physical Z size if the unit is
// a known, physical quantity such as "micron" rather than "pixel".
// e.g.: if (calibrationUnit.equals("micron"))
/*
if (physicalSizeZ != null) {
double zDepth = physicalSizeZ.doubleValue();
if (zDepth < 0) zDepth = -zDepth;
store.setPixelsPhysicalSizeZ(new PositiveFloat(zDepth), 0);
}
if (timeIncrement != null) {
store.setPixelsTimeIncrement(timeIncrement, 0);
}
*/
}
//
private boolean checkCommentImageJ(String comment) {
return comment != null && comment.startsWith("ImageJ=");
}
}
# This document is a configuration file identifying all file format readers
# available to Bio-Formats, and the order in which they should be used.
# Please do not edit unless you know what you are doing, see
# https://docs.openmicroscopy.org/latest/bio-formats/developers/reader-guide.html
# Could not find loci.formats.in.URLReader
# Could not find loci.formats.in.SlideBook6Reader
# "loci.formats.in.MicroCTReader" is not valid.
# Could not find loci.formats.in.CV7000Reader
# Could not find loci.formats.in.KLBReader
# java.lang.ClassNotFoundException: loci.formats.in.SlideBook6Reader
# at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ~[na:1.8.0_201]
#Elphel readers
com.elphel.imagej.readers.ElphelTiffReader # extensions same as TiffReader
loci.formats.in.FilePatternReader # pattern
##loci.formats.in.URLReader[type=external] # urlreader # Could not find loci.formats.in.URLReader
# readers for compressed/archive files
loci.formats.in.ZipReader # zip
# javax.imageio readers
loci.formats.in.APNGReader # png [javax.imageio]
loci.formats.in.JPEGReader # jpg, jpeg [javax.imageio]
# external readers with unique file extensions
##loci.formats.in.SlideBook6Reader[type=external] # sld # Could not find loci.formats.in.SlideBook6Reader
loci.formats.in.ScreenReader[type=external] # .screen
# standalone readers with unique file extensions
loci.formats.in.PGMReader # pgm
loci.formats.in.FitsReader # fits
loci.formats.in.PCXReader # pcx
loci.formats.in.GIFReader # gif
loci.formats.in.BMPReader # bmp
loci.formats.in.IPLabReader # ipl
loci.formats.in.IvisionReader # ipm
loci.formats.in.DeltavisionReader # dv, r3d
loci.formats.in.MRCReader # mrc, st, ali
loci.formats.in.GatanReader # dm3
loci.formats.in.GatanDM2Reader # dm2
loci.formats.in.ImarisReader # ims
loci.formats.in.OpenlabRawReader # raw
loci.formats.in.OMEXMLReader # ome
loci.formats.in.LIFReader # lif
loci.formats.in.AVIReader # avi
loci.formats.in.PictReader # pict, pct
loci.formats.in.SDTReader # sdt
loci.formats.in.SPCReader # spc
loci.formats.in.EPSReader # eps, epsi
loci.formats.in.SlidebookReader # sld
loci.formats.in.AliconaReader # al3d
loci.formats.in.MNGReader # mng
loci.formats.in.KhorosReader # xv
loci.formats.in.VisitechReader # html, xys
loci.formats.in.LIMReader # lim
loci.formats.in.PSDReader # psd
loci.formats.in.InCellReader # xdce
loci.formats.in.L2DReader # l2d
loci.formats.in.FEIReader # img
loci.formats.in.NAFReader # naf
loci.formats.in.MINCReader # mnc
loci.formats.in.QTReader # mov
loci.formats.in.MRWReader # mrw
loci.formats.in.TillVisionReader # vws
loci.formats.in.ARFReader # arf
loci.formats.in.CellomicsReader # c01
loci.formats.in.LiFlimReader # fli
loci.formats.in.TargaReader # tga
loci.formats.in.OxfordInstrumentsReader # top
loci.formats.in.VGSAMReader # dti
loci.formats.in.HISReader # his
loci.formats.in.WATOPReader # wat
loci.formats.in.SeikoReader # xqd, xqf
loci.formats.in.TopometrixReader # tfr, ffr, zfr, zfp, 2fl
loci.formats.in.UBMReader # pr3
loci.formats.in.QuesantReader # afm
loci.formats.in.BioRadGelReader # 1sc
loci.formats.in.RHKReader # sm2, sm3
loci.formats.in.MolecularImagingReader # stp
loci.formats.in.CellWorxReader # pnl, htd
loci.formats.in.Ecat7Reader # v
loci.formats.in.VarianFDFReader # fdf
loci.formats.in.AIMReader # aim
loci.formats.in.InCell3000Reader # frm
loci.formats.in.SpiderReader # spi
loci.formats.in.VolocityReader # mvd2
loci.formats.in.ImagicReader # hed
loci.formats.in.HamamatsuVMSReader # vms
loci.formats.in.CellSensReader # vsi
loci.formats.in.INRReader # inr
loci.formats.in.KodakReader # bip
loci.formats.in.VolocityClippingReader # acff
loci.formats.in.ZeissCZIReader # czi
loci.formats.in.SIFReader # sif
loci.formats.in.NDPISReader # ndpis
loci.formats.in.PovrayReader # df3
loci.formats.in.IMODReader # mod
loci.formats.in.FakeReader # fake
loci.formats.in.AFIReader # afi
loci.formats.in.ImspectorReader # msr
loci.formats.in.BioRadSCNReader # scn
loci.formats.in.ZeissLMSReader # lms
loci.formats.in.PQBinReader # bin
loci.formats.in.FlowSightReader # cif
loci.formats.in.IM3Reader # im3
loci.formats.in.I2IReader # i2i
loci.formats.in.SPEReader # spe
loci.formats.in.OIRReader # oir
##loci.formats.in.KLBReader # klb # Could not find loci.formats.in.KLBReader
##loci.formats.in.MicroCTReader # vff # "loci.formats.in.MicroCTReader" is not valid.
# multi-extension messes
loci.formats.in.JEOLReader # dat, img, par
loci.formats.in.NiftiReader # hdr, img, nii, nii.gz
loci.formats.in.AnalyzeReader # hdr, img
loci.formats.in.APLReader # apl, mtb, tnb
loci.formats.in.NRRDReader # nrrd, nhdr, pic
loci.formats.in.ICSReader # ics, ids
loci.formats.in.PerkinElmerReader # rec, ano, csv, htm, tim, zpo, 2, 3, ...
loci.formats.in.AmiraReader # am, amiramesh, grey, hx, labels, ...
loci.formats.in.ScanrReader # dat, xml, tif
loci.formats.in.BDReader # exp, tif
loci.formats.in.UnisokuReader # dat, hdr
loci.formats.in.PDSReader # hdr, img
loci.formats.in.FujiReader # inf, img
loci.formats.in.OperettaReader # xml, tif, tiff
loci.formats.in.InveonReader # hdr, ct.img, cat, ...
loci.formats.in.CellVoyagerReader # xml, tif
loci.formats.in.ColumbusReader # xml, tif
##loci.formats.in.CV7000Reader # wpi # Could not find loci.formats.in.CV7000Reader
# standard PIC reader must go last (it accepts any PIC)
loci.formats.in.BioRadReader # pic
# readers requiring third-party libraries
loci.formats.in.FV1000Reader # oib, oif, various [POI]
loci.formats.in.ZeissZVIReader # zvi [POI]
loci.formats.in.IPWReader # ipw [POI]
loci.formats.in.JPEG2000Reader # jp2, j2k [JAI-ImageIO]
loci.formats.in.JPXReader # jpx [JAI-ImageIO]
loci.formats.in.ND2Reader # nd2, jp2 [JAI-ImageIO]
loci.formats.in.PCIReader # cxd [POI]
loci.formats.in.ImarisHDFReader # ims [NetCDF]
loci.formats.in.CellH5Reader # ch5 [JHDF]
loci.formats.in.WlzReader # wlz [JWlz]
loci.formats.in.VeecoReader # hdf [NetCDF]
# TIFF-based readers with unique file extensions
loci.formats.in.ZeissLSMReader # lsm, mdb [MDB Tools]
loci.formats.in.SEQReader # seq
loci.formats.in.GelReader # gel
loci.formats.in.ImarisTiffReader # ims
loci.formats.in.FlexReader # flex [LuraWave]
loci.formats.in.SVSReader # svs
loci.formats.in.ImaconReader # fff
loci.formats.in.LEOReader # sxm
loci.formats.in.JPKReader # jpk
loci.formats.in.NDPIReader # ndpi
loci.formats.in.PCORAWReader # pcoraw
# TIFF-based readers with slow isThisType
loci.formats.in.OMETiffReader # tif
loci.formats.in.PyramidTiffReader # tif, tiff
loci.formats.in.MIASReader # tif
loci.formats.in.TCSReader # xml, tif
loci.formats.in.LeicaReader # lei, tif
loci.formats.in.NikonReader # nef, tif
loci.formats.in.FluoviewReader # tif
loci.formats.in.PrairieReader # xml, cfg, tif
loci.formats.in.MetamorphReader # stk, tif, nd, scan
loci.formats.in.MicromanagerReader # txt, tif
loci.formats.in.ImprovisionTiffReader # tif
loci.formats.in.MetamorphTiffReader # tif
loci.formats.in.NikonTiffReader # tif
loci.formats.in.PhotoshopTiffReader # tif
loci.formats.in.FEITiffReader # tif
loci.formats.in.SimplePCITiffReader # tif
loci.formats.in.NikonElementsTiffReader # tif
loci.formats.in.TrestleReader # tif
loci.formats.in.SISReader # tif
loci.formats.in.DNGReader # cr2, crw, jpg, thm, wav, tif?
loci.formats.in.ZeissTIFFReader # tif
loci.formats.in.LeicaSCNReader # scn
loci.formats.in.VectraReader # tif, tiff, qptiff
loci.formats.in.SlidebookTiffReader # tiff
loci.formats.in.IonpathMIBITiffReader # tif, tiff
# standard TIFF reader must go last (it accepts any TIFF)
loci.formats.in.TiffDelegateReader # tif, tiff
# standard text reader must go last (it accepts any plaintext)
loci.formats.in.TextReader # txt, csv
# non-TIFF readers with slow isThisType
loci.formats.in.BurleighReader # img
loci.formats.in.OpenlabReader # liff
loci.formats.in.DicomReader # dcm, dicom
loci.formats.in.SMCameraReader # (no extension)
loci.formats.in.SBIGReader # (no extension)
loci.formats.in.HRDGDFReader # (no extension)
loci.formats.in.HitachiReader # txt, tif, jpg, bmp
loci.formats.in.BrukerReader # fid, acqp
loci.formats.in.CanonRawReader # cr2, crw, jpg, thm, wav
loci.formats.in.OBFReader # obf, msr
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment