feat: smooth skin effect

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2025-10-19 11:56:55 +01:00
parent 6b0753b072
commit 8120690caa
4 changed files with 111 additions and 15 deletions

View File

@@ -14,7 +14,8 @@ use ptp::{
FujiColorChromeFXBlue, FujiCustomSetting, FujiCustomSettingName, FujiDynamicRange, FujiColorChromeFXBlue, FujiCustomSetting, FujiCustomSettingName, FujiDynamicRange,
FujiDynamicRangePriority, FujiFilmSimulation, FujiGrainEffect, FujiHighISONR, FujiDynamicRangePriority, FujiFilmSimulation, FujiGrainEffect, FujiHighISONR,
FujiHighlightTone, FujiImageQuality, FujiImageSize, FujiShadowTone, FujiSharpness, FujiHighlightTone, FujiImageQuality, FujiImageSize, FujiShadowTone, FujiSharpness,
FujiWhiteBalance, FujiWhiteBalanceShift, FujiWhiteBalanceTemperature, UsbMode, FujiSmoothSkinEffect, FujiWhiteBalance, FujiWhiteBalanceShift, FujiWhiteBalanceTemperature,
UsbMode,
}, },
structs::DeviceInfo, structs::DeviceInfo,
}; };
@@ -257,6 +258,14 @@ impl Camera {
pub fn set_color_chrome_fx_blue(&mut self, value: FujiColorChromeFXBlue) -> anyhow::Result<()> { pub fn set_color_chrome_fx_blue(&mut self, value: FujiColorChromeFXBlue) -> anyhow::Result<()> {
self.r#impl.set_color_chrome_fx_blue(&mut self.ptp, value) self.r#impl.set_color_chrome_fx_blue(&mut self.ptp, value)
} }
pub fn get_smooth_skin_effect(&mut self) -> anyhow::Result<FujiSmoothSkinEffect> {
self.r#impl.get_smooth_skin_effect(&mut self.ptp)
}
pub fn set_smooth_skin_effect(&mut self, value: FujiSmoothSkinEffect) -> anyhow::Result<()> {
self.r#impl.set_smooth_skin_effect(&mut self.ptp, value)
}
} }
impl Drop for Camera { impl Drop for Camera {
@@ -789,4 +798,25 @@ pub trait CameraImpl<P: rusb::UsbContext> {
)?; )?;
Ok(()) Ok(())
} }
fn get_smooth_skin_effect(&self, ptp: &mut Ptp) -> anyhow::Result<FujiSmoothSkinEffect> {
let bytes =
self.get_prop_value(ptp, DevicePropCode::FujiStillCustomSettingSmoothSkinEffect)?;
let result = FujiSmoothSkinEffect::try_from_ptp(&bytes)?;
Ok(result)
}
fn set_smooth_skin_effect(
&self,
ptp: &mut Ptp,
value: FujiSmoothSkinEffect,
) -> anyhow::Result<()> {
let bytes = value.try_into_ptp()?;
self.set_prop_value(
ptp,
DevicePropCode::FujiStillCustomSettingSmoothSkinEffect,
&bytes,
)?;
Ok(())
}
} }

View File

@@ -145,7 +145,7 @@ pub enum DevicePropCode {
FujiStillCustomSettingGrainEffect = 0xD195, FujiStillCustomSettingGrainEffect = 0xD195,
FujiStillCustomSettingColorChromeEffect = 0xD196, FujiStillCustomSettingColorChromeEffect = 0xD196,
FujiStillCustomSettingColorChromeFXBlue = 0xD197, FujiStillCustomSettingColorChromeFXBlue = 0xD197,
// TODO: 0xD198 All 1s FujiStillCustomSettingSmoothSkinEffect = 0xD198,
FujiStillCustomSettingWhiteBalance = 0xD199, FujiStillCustomSettingWhiteBalance = 0xD199,
FujiStillCustomSettingWhiteBalanceShiftRed = 0xD19A, FujiStillCustomSettingWhiteBalanceShiftRed = 0xD19A,
FujiStillCustomSettingWhiteBalanceShiftBlue = 0xD19B, FujiStillCustomSettingWhiteBalanceShiftBlue = 0xD19B,
@@ -875,6 +875,59 @@ impl FromStr for FujiColorChromeFXBlue {
} }
} }
#[repr(u16)]
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Serialize,
IntoPrimitive,
TryFromPrimitive,
PtpSerialize,
PtpDeserialize,
EnumIter,
)]
pub enum FujiSmoothSkinEffect {
Strong = 0x3,
Weak = 0x2,
Off = 0x1,
}
impl fmt::Display for FujiSmoothSkinEffect {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Strong => write!(f, "Strong"),
Self::Weak => write!(f, "Weak"),
Self::Off => write!(f, "Off"),
}
}
}
impl FromStr for FujiSmoothSkinEffect {
type Err = anyhow::Error;
fn from_str(s: &str) -> anyhow::Result<Self> {
let input = s.trim().to_lowercase();
match input.as_str() {
"strong" => return Ok(Self::Strong),
"weak" => return Ok(Self::Weak),
"off" => return Ok(Self::Off),
_ => {}
}
let choices: Vec<String> = Self::iter().map(|v| v.to_string()).collect();
if let Some(best) = suggest_closest(s, &choices) {
bail!("Unknown smooth skin effect '{s}'. Did you mean '{best}'?");
}
bail!("Unknown smooth skin effect '{s}'");
}
}
#[repr(u16)] #[repr(u16)]
#[derive( #[derive(
Debug, Debug,

View File

@@ -1,18 +1,14 @@
use clap::Args; use clap::Args;
use crate::camera::ptp::hex::{ use crate::camera::ptp::hex::{
FujiClarity, FujiColor, FujiColorChromeEffect, FujiColorChromeFXBlue, FujiCustomSettingName, FujiClarity, FujiColor, FujiColorChromeEffect, FujiColorChromeFXBlue, FujiDynamicRange,
FujiDynamicRange, FujiDynamicRangePriority, FujiFilmSimulation, FujiGrainEffect, FujiHighISONR, FujiDynamicRangePriority, FujiFilmSimulation, FujiGrainEffect, FujiHighISONR,
FujiHighlightTone, FujiImageQuality, FujiImageSize, FujiShadowTone, FujiSharpness, FujiHighlightTone, FujiImageQuality, FujiImageSize, FujiShadowTone, FujiSharpness,
FujiWhiteBalance, FujiWhiteBalanceShift, FujiWhiteBalanceTemperature, FujiSmoothSkinEffect, FujiWhiteBalance, FujiWhiteBalanceShift, FujiWhiteBalanceTemperature,
}; };
#[derive(Args, Debug)] #[derive(Args, Debug)]
pub struct FilmSimulationOptions { pub struct FilmSimulationOptions {
/// The name of the slot
#[clap(long)]
pub name: Option<FujiCustomSettingName>,
/// The Fujifilm film simulation to use /// The Fujifilm film simulation to use
#[clap(long)] #[clap(long)]
pub simulation: Option<FujiFilmSimulation>, pub simulation: Option<FujiFilmSimulation>,
@@ -84,4 +80,8 @@ pub struct FilmSimulationOptions {
/// Color Chrome FX Blue /// Color Chrome FX Blue
#[clap(long)] #[clap(long)]
pub color_chrome_fx_blue: Option<FujiColorChromeFXBlue>, pub color_chrome_fx_blue: Option<FujiColorChromeFXBlue>,
/// Smooth Skin Effect
#[clap(long)]
pub smooth_skin_effect: Option<FujiSmoothSkinEffect>,
} }

View File

@@ -5,8 +5,8 @@ use crate::{
FujiClarity, FujiColor, FujiColorChromeEffect, FujiColorChromeFXBlue, FujiCustomSetting, FujiClarity, FujiColor, FujiColorChromeEffect, FujiColorChromeFXBlue, FujiCustomSetting,
FujiCustomSettingName, FujiDynamicRange, FujiDynamicRangePriority, FujiFilmSimulation, FujiCustomSettingName, FujiDynamicRange, FujiDynamicRangePriority, FujiFilmSimulation,
FujiGrainEffect, FujiHighISONR, FujiHighlightTone, FujiImageQuality, FujiImageSize, FujiGrainEffect, FujiHighISONR, FujiHighlightTone, FujiImageQuality, FujiImageSize,
FujiShadowTone, FujiSharpness, FujiWhiteBalance, FujiWhiteBalanceShift, FujiShadowTone, FujiSharpness, FujiSmoothSkinEffect, FujiWhiteBalance,
FujiWhiteBalanceTemperature, FujiWhiteBalanceShift, FujiWhiteBalanceTemperature,
}, },
usb, usb,
}; };
@@ -39,6 +39,10 @@ pub enum SimulationCmd {
/// Simulation slot number /// Simulation slot number
slot: FujiCustomSetting, slot: FujiCustomSetting,
/// The name of the slot
#[clap(long)]
name: Option<FujiCustomSettingName>,
#[command(flatten)] #[command(flatten)]
film_simulation_options: FilmSimulationOptions, film_simulation_options: FilmSimulationOptions,
}, },
@@ -116,6 +120,7 @@ pub struct FilmSimulationRepr {
pub grain: FujiGrainEffect, pub grain: FujiGrainEffect,
pub color_chrome_effect: FujiColorChromeEffect, pub color_chrome_effect: FujiColorChromeEffect,
pub color_chrome_fx_blue: FujiColorChromeFXBlue, pub color_chrome_fx_blue: FujiColorChromeFXBlue,
pub smooth_skin_effect: FujiSmoothSkinEffect,
} }
impl fmt::Display for FilmSimulationRepr { impl fmt::Display for FilmSimulationRepr {
@@ -145,7 +150,8 @@ impl fmt::Display for FilmSimulationRepr {
writeln!(f, "Noise Reduction: {}", self.noise_reduction)?; writeln!(f, "Noise Reduction: {}", self.noise_reduction)?;
writeln!(f, "Grain: {}", self.grain)?; writeln!(f, "Grain: {}", self.grain)?;
writeln!(f, "Color Chrome Effect: {}", self.color_chrome_effect)?; writeln!(f, "Color Chrome Effect: {}", self.color_chrome_effect)?;
writeln!(f, "Color Chrome FX Blue: {}", self.color_chrome_fx_blue) writeln!(f, "Color Chrome FX Blue: {}", self.color_chrome_fx_blue)?;
writeln!(f, "Smooth Skin Effect: {}", self.smooth_skin_effect)
} }
} }
@@ -173,6 +179,7 @@ fn handle_get(json: bool, device_id: Option<&str>, slot: FujiCustomSetting) -> a
grain: camera.get_grain_effect()?, grain: camera.get_grain_effect()?,
color_chrome_effect: camera.get_color_chrome_effect()?, color_chrome_effect: camera.get_color_chrome_effect()?,
color_chrome_fx_blue: camera.get_color_chrome_fx_blue()?, color_chrome_fx_blue: camera.get_color_chrome_fx_blue()?,
smooth_skin_effect: camera.get_smooth_skin_effect()?,
}; };
if json { if json {
@@ -187,13 +194,14 @@ fn handle_get(json: bool, device_id: Option<&str>, slot: FujiCustomSetting) -> a
fn handle_set( fn handle_set(
device_id: Option<&str>, device_id: Option<&str>,
slot: FujiCustomSetting, slot: FujiCustomSetting,
options: &FilmSimulationOptions, name: Option<FujiCustomSettingName>,
options: FilmSimulationOptions,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mut camera = usb::get_camera(device_id)?; let mut camera = usb::get_camera(device_id)?;
camera.set_active_custom_setting(slot)?; camera.set_active_custom_setting(slot)?;
// General // General
if let Some(name) = &options.name { if let Some(name) = &name {
camera.set_custom_setting_name(name)?; camera.set_custom_setting_name(name)?;
} }
@@ -238,6 +246,10 @@ fn handle_set(
camera.set_color_chrome_fx_blue(*color_chrome_fx_blue)?; camera.set_color_chrome_fx_blue(*color_chrome_fx_blue)?;
} }
if let Some(smooth_skin_effect) = &options.smooth_skin_effect {
camera.set_smooth_skin_effect(*smooth_skin_effect)?;
}
// White Balance // White Balance
if let Some(white_balance) = &options.white_balance { if let Some(white_balance) = &options.white_balance {
camera.set_white_balance(*white_balance)?; camera.set_white_balance(*white_balance)?;
@@ -328,8 +340,9 @@ pub fn handle(cmd: SimulationCmd, json: bool, device_id: Option<&str>) -> anyhow
SimulationCmd::Get { slot } => handle_get(json, device_id, slot), SimulationCmd::Get { slot } => handle_get(json, device_id, slot),
SimulationCmd::Set { SimulationCmd::Set {
slot, slot,
name,
film_simulation_options, film_simulation_options,
} => handle_set(device_id, slot, &film_simulation_options), } => handle_set(device_id, slot, name, film_simulation_options),
SimulationCmd::Export { slot, output_file } => handle_export(device_id, slot, &output_file), SimulationCmd::Export { slot, output_file } => handle_export(device_id, slot, &output_file),
SimulationCmd::Import { slot, input_file } => handle_import(device_id, slot, &input_file), SimulationCmd::Import { slot, input_file } => handle_import(device_id, slot, &input_file),
} }