-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extract common logic for detectStart and detectStop into a helper class #77
base: main
Are you sure you want to change the base?
Conversation
* @property {(input: tf.Tensor3D) => Promise<Array>} detect - core detection method. | ||
* the ImageDetector will call the `detect` method of the specific implementation. | ||
* It should accept a TensorFlow tensor?? or an image?? | ||
* And return an array of detections?? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was initially thinking that we would handle the image to tensor conversion here in the ImageDetector and call the specific model with the tensor. But it seems like all of these models accept image or video and convert it themselves?
* @property {Promise<boolean>} ready - lets the parent detector know that the | ||
* specific implementation is ready. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to change this to be a method (the this.loadModel
method) rather than a property because a property doesn't work as nicely when using the @implements {SpecificDetectorImplementation}
tag on the model class. Not sure if it's different in VSCode but JetBrains is warning me about the unused property and does not pick up that it implements anything.
It works with the detect method.
/** | ||
* @type {Promise<boolean>} | ||
* TODO: do we need to handle onReady callbacks? | ||
*/ | ||
this.ready = this.implementation.ready; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking I should handle the callCallback
stuff here instead of in each model.
async detectLoop() { | ||
// Make sure that both the model and the media are loaded before beginning. | ||
await Promise.all([ | ||
this.ready, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like the current code is not waiting for this.loadModel()
to finish so I added an additional await
here, and used Promise.all
to wait for the two things (model and media) in parallel rather than in series. Though the this.ready
promise has already started, so series vs. parallel might not even make a difference?
Thank you @lindapaiste! I haven't had a chance to dig into this more yet, but @gohai and I were discussing this morning about how while it's important for the ml5.js library to follow best practices and organize its code in a scalable and efficient way, we also want to make sure we are prioritizing the ability for beginner students (who have never worked with an open source codebase) to contribute. This has its downsides of course, but it's ok for the project to have imperfect design patterns if it makes it easier for a new contributor to come in and make a modification without having to move around between many different files (even if there is redundant code). @gohai feel free to add any more thoughts! One thing we pinponted that we definitely need help with is figuring out how to be compatible with all the quirks of p5.js |
Hi @lindapaiste - just adding to what Dan wrote with my own experiences: for most of my students, "programming" is largely putting function calls into some predefined In advising some students who hacked on ml5 last summer, I got the sense that they were often time struggling with:
I don't think we need to change code to make it conform to this stone-age notion of JavaScript necessarily, but as Dan said, I think it's okay to look past some of the code-duplication and less-than-optimal use of language features 🙂 |
Pros
Cons
Neutrals
|
/** | ||
* @param {SpecificDetectorImplementation} specificImplementation | ||
*/ | ||
constructor(specificImplementation) { | ||
/** | ||
* @type {SpecificDetectorImplementation} | ||
*/ | ||
this.implementation = specificImplementation; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gohai @shiffman After reading your comments I think that my terminology here might be over the top. I wanted to be really clear about what is what. But it's not going to translate if the person reading it doesn't understand the terminology and it sounds like your students will not be familiar with concepts and terms like interface vs implementation and concretion vs abstraction. Maybe it is better to keep it simple instead.
/**
* @param {DetectorModel} model
*/
constructor(model) {
/**
* @type {DetectorModel}
*/
this.model = model;
I'm also tempted to add a link to https://en.wikipedia.org/wiki/Dependency_injection in one of those JSDoc blocks! 😝
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I would err on the side of keeping things simple for now, maybe we can open an issue for discussion around using "interfaces" in the codebase for later? For now, I would priortize the codebase being as friendly for beginners to contribute to even if it's not making use of the latest and greatest JS features / best practices.
The logic for
detectStart
anddetectStop
is the same for 4 of the models, so I moved those common parts into a new Helper classImageDetector
. Then the ml5 functionshandPose
etc. return a newImageDetector
instance which internally uses an instance of theHandPose
class.Alternatively, we could define
ImageDetector
as aabstract class
and useclass HandPose extends ImageDetector
. That would be less flexible, but probably easier to understand?I'm marking this as a draft because there's a few things that I may want to change, mainly moving the
this.ready
into theImageDetector
. And also probably moving the p5 preload logic, but that's its own can of worms.