示例#1
0
 def make(self):
     comps = {}
     for name, component in self.components.items():
         comps[name] = eval(component['class'])()
         cnf = comps[name].get_config()
         for key, value in eval(component['config']).items():
             cnf[key] = value
         comps[name].set_config(cnf)
     return Compound(linkages=self.linkages, **comps)
示例#2
0
 def make(self):
     comps = {}
     for name, component in self.components.items():
         comps[name] = eval(
             component['class'])(config=eval(component['config']))
     return Compound(linkages=self.linkages, **comps)
示例#3
0
def main():
    logging.basicConfig(level=logging.INFO)
    parser = argparse.ArgumentParser()
    parser.add_argument('-a',
                        '--audit',
                        action='store_true',
                        help='show processing audit trail')
    parser.add_argument('-g',
                        '--gamma',
                        action='store_true',
                        help='plot gamma correction curve')
    parser.add_argument('--histogram',
                        action='store_true',
                        help='plot histogram of output')
    parser.add_argument('-l',
                        '--linear',
                        action='store_true',
                        help='save "linear light" (not gamma corrected) image')
    parser.add_argument('--astro',
                        action='store_true',
                        help='set defaults for astrophotography')
    parser.add_argument('-e',
                        '--exposure',
                        default=0,
                        type=float,
                        metavar='x',
                        help='adjust exposure by x stops')
    parser.add_argument('-o',
                        '--offset',
                        default=1.7,
                        type=float,
                        metavar='x',
                        help='offset black level by x')
    parser.add_argument('-c',
                        '--colour',
                        default=1.07,
                        type=float,
                        metavar='x',
                        help='multiply colour by x')
    parser.add_argument('-f',
                        '--filter_colour',
                        default=0,
                        type=float,
                        metavar='x',
                        help='set colour smoothing radius to x')
    parser.add_argument('-n',
                        '--noise',
                        default=0,
                        type=float,
                        metavar='x',
                        help='set wavelet denoising threshold to x')
    parser.add_argument('-r',
                        '--reorient',
                        action='store_true',
                        help='rotate/reflect image data')
    parser.add_argument('-s',
                        '--sharpen',
                        nargs=3,
                        type=float,
                        metavar=('a', 'r', 't'),
                        help='sharpen with amount a, radius r, threshold t')
    parser.add_argument('file', nargs='+', help='raw files to be processed')
    args = parser.parse_args()
    if args.gamma or args.histogram:
        from PyQt5 import QtCore, QtWidgets
        QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_X11InitThreads)
        app = QtWidgets.QApplication(sys.argv)
        app.lastWindowClosed.connect(app.quit)
    if args.gamma:
        from pyctools.components.io.plotdata import PlotData
    if args.histogram:
        from pyctools.components.qt.showhistogram import ShowHistogram
    for in_file in args.file:
        if not os.path.exists(in_file):
            continue
        print(in_file)
        in_file = os.path.abspath(in_file)
        base, ext = os.path.splitext(in_file)
        if ext != '.CR2':
            print('Skip non raw')
            continue
        out_file = base + 'p.jpg'
        if os.path.exists(out_file):
            print('Skip existing', os.path.basename(out_file))
            continue
        md = GExiv2.Metadata()
        md.open_path(in_file)
        lens = md.get_tag_string('Exif.Photo.LensModel')
        aperture = md.get_fnumber()
        focal_length = md.get_focal_length()
        iso = md.get_iso_speed()
        if '10-18' in lens:
            lens = 'Canon_10_18'
        elif '18-55' in lens:
            lens = 'Canon_18_55'
        elif '18-200' in lens:
            lens = 'Sigma_18_200'
        elif '70-300' in lens:
            lens = 'Sigma_70_300'
        elif '50' in lens:
            lens = 'Samyang_500'
            aperture = 6.3
            focal_length = 500
        else:
            print('Skip lens', lens)
            continue
        ca_params = get_chromatic_aberration_params(lens, focal_length)
        comps = {
            'reader':
            RawImageFileReader2(path=in_file,
                                highlight_mode='Blend',
                                demosaic_algorithm='DCB',
                                dcb_iterations=0,
                                dcb_enhance=False,
                                fbdd_noise_reduction='Off',
                                **ca_params),
            'rgbtoyuv':
            RGBtoYUV(matrix='709'),
            'sharpen':
            UnsharpMask(amount=0.3, radius=2.5, threshold=1.0),
            'yuvtorgb':
            YUVtoRGB(matrix='709'),
            'gamma':
            GammaCorrect(gamma='hybrid_log',
                         scale=5,
                         black=args.offset,
                         white=100.0 / (2**args.exposure)),
            'quantise':
            ErrorFeedbackQuantise(),
            'writer':
            ImageFileWriterPIL(path=out_file,
                               options='"quality":95',
                               set_thumbnail=True),
        }
        if iso >= 3200:
            comps['reader'].set_config({
                'fbdd_noise_reduction': 'Full',
                'noise_thr': 400
            })
        elif iso >= 1600:
            comps['reader'].set_config({
                'fbdd_noise_reduction': 'Full',
                'noise_thr': 200
            })
        elif iso >= 400:
            comps['reader'].set_config({
                'fbdd_noise_reduction': 'Full',
                'noise_thr': 100
            })
        if args.astro:
            comps['reader'].set_config({
                'fbdd_noise_reduction': 'Full',
                'noise_thr': 300,
                # force daylight white balance
                'use_camera_wb': False,
                'user_wb': '2123,1024,1531,1024',
            })
        if args.noise:
            comps['reader'].set_config({'noise_thr': args.noise})
        if lens == 'Samyang_500':
            comps['sharpen'].set_config({'amount': 0.5})
        linkages = {
            ('reader', 'output'): [('rgbtoyuv', 'input')],
            ('rgbtoyuv', 'output_Y'): [('sharpen', 'input')],
            ('rgbtoyuv', 'output_UV'): [('yuvtorgb', 'input_UV')],
            ('sharpen', 'output'): [('yuvtorgb', 'input_Y')],
            ('yuvtorgb', 'output'): [('gamma', 'input')],
            ('gamma', 'output'): [('quantise', 'input')],
            ('quantise', 'output'): [('writer', 'input'), ('self', 'output')],
        }
        if args.reorient:
            comps['reorient'] = Reorient(orientation='auto')
            linkages[('reorient', 'output')] = linkages[('reader', 'output')]
            linkages[('reader', 'output')] = [('reorient', 'input')]
        vignette_params = get_vignette_params(lens, aperture, focal_length)
        if vignette_params:
            comps['vignette'] = VignetteCorrector(**vignette_params)
            linkages[('vignette', 'output')] = linkages[('reader', 'output')]
            linkages[('reader', 'output')] = [('vignette', 'input')]
        if args.gamma:
            comps['plot'] = PlotData()
            linkages[('gamma', 'function')] = [('plot', 'input')]
        if args.histogram:
            comps['histogram'] = ShowHistogram()
            linkages[('gamma', 'output')].append(('histogram', 'input'))
        if args.linear:
            from pyctools.components.io.imagefilecv import ImageFileWriterCV
            comps['lin_writer'] = ImageFileWriterCV(path=base + 'l.tiff')
            comps['lin_writer'].set_config({'16bit': True})
            linkages[('yuvtorgb', 'output')].append(('lin_writer', 'input'))
        if args.colour != 1.0:
            comps['colour'] = Arithmetic(
                func='data * pt_float({})'.format(args.colour))
            linkages[('colour', 'output')] = linkages[('rgbtoyuv',
                                                       'output_UV')]
            linkages[('rgbtoyuv', 'output_UV')] = [('colour', 'input')]
        if args.filter_colour > 0:
            comps['filter'] = UnsharpMask(amount=-1,
                                          radius=args.filter_colour,
                                          denoise=False)
            linkages[('filter', 'output')] = linkages[('rgbtoyuv',
                                                       'output_UV')]
            linkages[('rgbtoyuv', 'output_UV')] = [('filter', 'input')]
        if args.sharpen:
            a, r, t = args.sharpen
            comps['sharpen2'] = UnsharpMask(amount=a,
                                            radius=r,
                                            threshold=t,
                                            denoise=True)
            linkages[('sharpen2', 'output')] = linkages[('yuvtorgb', 'output')]
            linkages[('yuvtorgb', 'output')] = [('sharpen2', 'input')]
        graph = Compound(linkages=linkages, **comps)
        graph.start()
        if args.gamma or args.histogram:
            app.exec_()
        else:
            try:
                graph.join(end_comps=True)
            except KeyboardInterrupt:
                pass
            graph.stop()
        graph.join()
        if os.path.exists(out_file):
            # set modified timestamp
            md = GExiv2.Metadata()
            md.open_path(out_file)
            now = datetime.now()
            tz_offset = (now - datetime.utcnow()).total_seconds() / 60
            tz_offset = int(round(tz_offset / 15, 0) * 15)
            if tz_offset < 0:
                sign = '-'
                tz_offset = -tz_offset
            else:
                sign = '+'
            md.set_tag_string('Exif.Image.DateTime',
                              now.strftime('%Y:%m:%d %H:%M:%S'))
            md.clear_tag('Exif.Photo.SubSecTime')
            md.set_tag_string(
                'Xmp.xmp.ModifyDate',
                now.strftime('%Y-%m-%dT%H:%M:%S') + sign +
                '{:02d}:{:02d}'.format(tz_offset // 60, tz_offset % 60))
            # set audit trail
            audit = md.get_tag_string('Xmp.pyctools.audit')
            audit += '{} = process_raw({})\n'.format(
                os.path.basename(out_file), os.path.basename(in_file))
            params = []
            for key, value in vars(args).items():
                if key not in ('audit', 'file', 'gamma', 'histogram'):
                    params.append('{}: {}'.format(key, value))
            if params:
                audit += '    ' + ', '.join(params) + '\n'
            md.set_tag_string('Xmp.pyctools.audit', audit)
            md.save_file(out_file)
            if args.audit:
                print(audit)